// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#pragma once

#ifndef GLAD_GL_H_
typedef unsigned int GLuint;
typedef int          GLint;
typedef unsigned int GLenum;
typedef int          GLsizei;
#endif
#include <string>

namespace dragonx {
namespace ui {
namespace effects {

/**
 * @brief GLSL shader program for blur effects
 * 
 * Implements a two-pass Gaussian blur using separable convolution.
 * First pass blurs horizontally, second pass blurs vertically.
 */
class BlurShader {
public:
    BlurShader();
    ~BlurShader();
    
    // Non-copyable
    BlurShader(const BlurShader&) = delete;
    BlurShader& operator=(const BlurShader&) = delete;
    
    /**
     * @brief Compile and link the blur shader program
     * @return true if successful
     */
    bool init();
    
    /**
     * @brief Release shader resources
     */
    void destroy();
    
    /**
     * @brief Use this shader program
     */
    void bind();
    
    /**
     * @brief Stop using this shader
     */
    void unbind();
    
    /**
     * @brief Set the blur direction
     * @param horizontal true for horizontal pass, false for vertical
     */
    void setDirection(bool horizontal);
    
    /**
     * @brief Set the blur radius/intensity
     * @param radius Blur radius in pixels (typical: 1.0 - 10.0)
     */
    void setRadius(float radius);
    
    /**
     * @brief Set the texture resolution for proper texel calculation
     * @param width Texture width
     * @param height Texture height
     */
    void setResolution(int width, int height);
    
    /**
     * @brief Set the input texture unit
     * @param unit Texture unit (0 = GL_TEXTURE0)
     */
    void setTexture(int unit = 0);
    
    /**
     * @brief Check if shader is valid
     */
    bool isValid() const { return program_ != 0; }
    
    /**
     * @brief Get the shader program ID
     */
    GLuint getProgram() const { return program_; }

private:
    bool compileShader(GLuint shader, const char* source);
    bool linkProgram();
    
    GLuint program_ = 0;
    GLuint vertexShader_ = 0;
    GLuint fragmentShader_ = 0;
    
    // Uniform locations
    GLint uTexture_ = -1;
    GLint uDirection_ = -1;
    GLint uResolution_ = -1;
    GLint uRadius_ = -1;
};

/**
 * @brief Manages the full-screen quad VAO for post-processing
 */
class FullscreenQuad {
public:
    FullscreenQuad();
    ~FullscreenQuad();
    
    /**
     * @brief Initialize the quad VAO/VBO
     */
    bool init();
    
    /**
     * @brief Release resources
     */
    void destroy();
    
    /**
     * @brief Draw the fullscreen quad
     */
    void draw();
    
    bool isValid() const { return vao_ != 0; }

private:
    GLuint vao_ = 0;
    GLuint vbo_ = 0;
};

// ============================================================================
// Shader Source Code (embedded)
// ============================================================================

namespace ShaderSource {

// Simple passthrough vertex shader for fullscreen quad
constexpr const char* BLUR_VERTEX = R"glsl(
#version 330 core

layout (location = 0) in vec2 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 vTexCoord;

void main()
{
    gl_Position = vec4(aPos, 0.0, 1.0);
    vTexCoord = aTexCoord;
}
)glsl";

// Gaussian blur fragment shader (dynamic kernel, separable)
// Loop-based with 1-texel spacing + triangular dithering
constexpr const char* BLUR_FRAGMENT = R"glsl(
#version 330 core

in vec2 vTexCoord;
out vec4 FragColor;

uniform sampler2D uTexture;
uniform vec2 uDirection;   // (1,0) for horizontal, (0,1) for vertical
uniform vec2 uResolution;  // Texture dimensions
uniform float uRadius;     // Gaussian sigma in texels

// Screen-space hash for dithering (returns 0..1)
float hash12(vec2 p) {
    vec3 p3 = fract(vec3(p.xyx) * 0.1031);
    p3 += dot(p3, p3.yzx + 33.33);
    return fract((p3.x + p3.y) * p3.z);
}

void main()
{
    vec2 texelSize = 1.0 / uResolution;
    vec2 step = uDirection * texelSize;

    // Dynamic Gaussian kernel: uRadius = sigma, extend to ~2.5 sigma
    float sigma = max(uRadius, 0.5);
    int iRadius = clamp(int(sigma * 2.5 + 0.5), 1, 40);
    float inv2sigma2 = 1.0 / (2.0 * sigma * sigma);

    vec3 sum = vec3(0.0);
    float wSum = 0.0;

    for (int i = -iRadius; i <= iRadius; i++) {
        float w = exp(-float(i * i) * inv2sigma2);
        sum += texture(uTexture, vTexCoord + step * float(i)).rgb * w;
        wSum += w;
    }

    vec3 color = sum / wSum;

    // Triangular-PDF dithering to break up 8-bit quantization banding.
    // Two uniform hashes combined give a triangular distribution (-1..+1).
    float d = hash12(gl_FragCoord.xy) + hash12(gl_FragCoord.xy + 71.37) - 1.0;
    color += d / 255.0;

    FragColor = vec4(color, 1.0);
}
)glsl";

} // namespace ShaderSource

} // namespace effects
} // namespace ui
} // namespace dragonx
