/*
    SPDX-FileCopyrightText: 2022 Vlad Zahorodnii <vlad.zahorodnii@kde.org>

    SPDX-License-Identifier: GPL-2.0-or-later
*/

#pragma once

#include "opengl/glutils.h"
#include "scene/itemrenderer.h"
#include "scene/surfaceitem.h"

#include <unordered_set>

namespace KWin
{

class EglDisplay;

class KWIN_EXPORT ItemRendererOpenGL : public ItemRenderer
{
public:
    struct RenderNode
    {
        ShaderTraits traits;
        QVarLengthArray<GLTexture *, 4> textures;
        RenderGeometry geometry;
        QMatrix4x4 transformMatrix;
        int firstVertex = 0;
        int vertexCount = 0;
        qreal opacity = 1;
        bool hasAlpha = false;
        std::shared_ptr<ColorDescription> colorDescription;
        RenderingIntent renderingIntent;
        std::shared_ptr<SyncReleasePoint> bufferReleasePoint;
        QVector4D box;
        QVector4D borderRadius;
        int borderThickness = 0;
        QColor borderColor;
        bool paintHole = false;
        bool hasFloatingPointColor = false;
        std::optional<RectF> layerDebugBox;
    };

    struct RenderCorner
    {
        RectF box;
        BorderRadius radius;
    };

    struct RenderContext
    {
        QList<RenderNode> renderNodes;
        QStack<QMatrix4x4> transformStack;
        QStack<qreal> opacityStack;
        QStack<RenderCorner> cornerStack;
        const QMatrix4x4 projectionMatrix;
        const QMatrix4x4 rootTransform;
        const Region deviceClip;
        const bool hardwareClipping;
        const qreal renderTargetScale;
        const QPointF viewportOrigin;
        const QPoint renderOffset;
    };

    ItemRendererOpenGL(EglDisplay *eglDisplay);

    std::unique_ptr<Texture> createTexture(GraphicsBuffer *buffer, const std::shared_ptr<SyncReleasePoint> &releasePoint) override;
    std::unique_ptr<Texture> createTexture(const QImage &image) override;

    std::unique_ptr<NinePatch> createNinePatch(const QImage &image) override;
    std::unique_ptr<NinePatch> createNinePatch(const QImage &topLeftPatch,
                                               const QImage &topPatch,
                                               const QImage &topRightPatch,
                                               const QImage &rightPatch,
                                               const QImage &bottomRightPatch,
                                               const QImage &bottomPatch,
                                               const QImage &bottomLeftPatch,
                                               const QImage &leftPatch) override;

    std::unique_ptr<Atlas> createAtlas(const QList<QImage> &sprites) override;

    void beginFrame(const RenderTarget &renderTarget, const RenderViewport &viewport) override;
    void endFrame() override;

    void renderBackground(const RenderTarget &renderTarget, const RenderViewport &viewport, const Region &deviceRegion) override;
    void renderItem(const RenderTarget &renderTarget, const RenderViewport &viewport, Item *item, int mask, const Region &deviceRegion, const WindowPaintData &data, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter) override;

    void setLayerDebugging(bool enable) override;

private:
    QVector4D modulate(float opacity, float brightness) const;
    void setBlendEnabled(bool enabled);
    void createRenderNode(Item *item, RenderContext *context, const std::function<bool(Item *)> &filter, const std::function<bool(Item *)> &holeFilter);
    void visualizeFractional(const RenderViewport &viewport, const Region &logicalRegion, const RenderContext &renderContext);

    bool m_blendingEnabled = false;
    EglDisplay *const m_eglDisplay;
    std::unordered_set<std::shared_ptr<SyncReleasePoint>> m_releasePoints;

    struct
    {
        bool fractionalEnabled = false;
        std::unique_ptr<GLShader> fractionalShader;
        bool layerEnabled = false;
    } m_debug;
};

} // namespace KWin
