#pragma once

#include <GLES3/gl3.h>
#include <GLES2/gl2ext.h>

#include <cmath>
#include <mutex>
#include <string>
#include <vector>

struct CompositionTransform {
    float posX = 0.0f;   // NDC [-1, 1]
    float posY = 0.0f;
    float scaleX = 1.0f;
    float scaleY = 1.0f;
    float rotation = 0.0f; // radians
    float anchorX = 0.5f;  // [0, 1] within layer
    float anchorY = 0.5f;
};

struct CompositionLayer {
    int id = -1;
    GLuint textureId = 0;       // GL_TEXTURE_2D
    int texWidth = 0;
    int texHeight = 0;
    CompositionTransform transform;
    float opacity = 1.0f;
    bool enabled = true;
    int zOrder = 0;
    std::string tag;
    bool ownsTexture = true;    // if true, pipeline deletes texture on removal
};

/**
 * GPU composition pipeline.
 * Renders a base OES texture (game frame) plus overlay layers to an FBO,
 * producing a GL_TEXTURE_2D that can be blit to encoder and preview surfaces.
 */
class CompositionPipeline {
public:
    CompositionPipeline();
    ~CompositionPipeline();

    /** Initialize FBO, shaders, and quad geometry at encoder resolution. */
    bool Init(int width, int height);

    /**
     * Compose the scene: render base OES texture + overlay layers to FBO.
     * Must be called on the GL thread.
     */
    void Compose(GLuint srcOesTexture);

    /** Returns the FBO color attachment (GL_TEXTURE_2D). */
    GLuint GetComposedTexture() const { return fboTexture; }

    /** Layer management — thread-safe. */
    int AddLayer(GLuint textureId, int texW, int texH,
                 const CompositionTransform& transform,
                 float opacity, int zOrder, const std::string& tag,
                 bool ownsTexture = true);
    void RemoveLayer(int layerId);
    void UpdateLayerTransform(int layerId, const CompositionTransform& transform);
    void UpdateLayerOpacity(int layerId, float opacity);
    void SetLayerEnabled(int layerId, bool enabled);

    /** Upload raw RGBA pixels as a GL_TEXTURE_2D. Returns texture ID (0 on failure). */
    static GLuint UploadTexture(const uint8_t* rgbaData, int w, int h);

    /** Release all GL resources. Must be called on the GL thread. */
    void Release();

    bool IsInitialized() const { return initialized; }

private:
    void RenderBasePass(GLuint srcOesTexture);
    void RenderStandbyPattern();
    void RenderOverlayLayers();
    void BuildTransformMatrix(const CompositionLayer& layer, float* outMat4);

    // Standby pattern shader
    GLuint standbyProgram = 0;

    int fboWidth = 0;
    int fboHeight = 0;
    bool initialized = false;

    // FBO
    GLuint fbo = 0;
    GLuint fboTexture = 0;     // RGBA8 color attachment

    // Shaders
    GLuint baseProgram = 0;     // samplerExternalOES for game frame
    GLuint overlayProgram = 0;  // sampler2D + uMVP + uOpacity for layers

    // Shared quad VAO
    GLuint quadVao = 0;
    GLuint quadVbo = 0;

    // Overlay uniform locations
    GLint overlayMvpLoc = -1;
    GLint overlayOpacityLoc = -1;
    GLint overlayTexLoc = -1;

    // Layers (protected by mutex)
    std::mutex layerMutex;
    std::vector<CompositionLayer> layers;
    int nextLayerId = 1;
};
