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

#pragma once

// GL type definitions needed by class declarations.
// If GLAD was already included, these types are already defined.
#ifndef GLAD_GL_H_
typedef unsigned int GLuint;
typedef int          GLint;
typedef unsigned int GLenum;
typedef int          GLsizei;
#endif
#include <cstdint>

namespace dragonx {
namespace ui {
namespace effects {

/**
 * @brief OpenGL Framebuffer Object manager for off-screen rendering
 * 
 * Used for capturing screen content to use as blur source for acrylic effects.
 */
class Framebuffer {
public:
    Framebuffer();
    ~Framebuffer();
    
    // Non-copyable
    Framebuffer(const Framebuffer&) = delete;
    Framebuffer& operator=(const Framebuffer&) = delete;
    
    // Move constructors
    Framebuffer(Framebuffer&& other) noexcept;
    Framebuffer& operator=(Framebuffer&& other) noexcept;
    
    /**
     * @brief Initialize framebuffer with given dimensions
     * @param width Width in pixels
     * @param height Height in pixels
     * @param useFloat16 Use RGBA16F format for higher precision (reduces banding)
     * @return true if successful
     */
    bool init(int width, int height, bool useFloat16 = false);
    
    /**
     * @brief Resize framebuffer (recreates internal textures)
     * @param width New width
     * @param height New height
     * @return true if successful
     */
    bool resize(int width, int height);
    
    /**
     * @brief Release all OpenGL resources
     */
    void destroy();
    
    /**
     * @brief Bind this framebuffer for rendering
     */
    void bind();
    
    /**
     * @brief Unbind (return to default framebuffer)
     */
    void unbind();
    
    /**
     * @brief Clear the framebuffer
     * @param r Red component (0-1)
     * @param g Green component (0-1)
     * @param b Blue component (0-1)
     * @param a Alpha component (0-1)
     */
    void clear(float r = 0.0f, float g = 0.0f, float b = 0.0f, float a = 1.0f);
    
    /**
     * @brief Get the color texture attached to this framebuffer
     * @return OpenGL texture ID
     */
    GLuint getColorTexture() const { return colorTexture_; }

    /**
     * @brief Get the underlying framebuffer object ID
     * @return OpenGL FBO ID
     */
    GLuint getFbo() const { return fbo_; }
    
    /**
     * @brief Get framebuffer dimensions
     */
    int getWidth() const { return width_; }
    int getHeight() const { return height_; }
    
    /**
     * @brief Check if framebuffer is valid and complete
     */
    bool isValid() const { return fbo_ != 0 && isComplete_; }
    
    /**
     * @brief Copy a region from the current framebuffer to this one
     * @param srcX Source X position
     * @param srcY Source Y position
     * @param srcWidth Source width
     * @param srcHeight Source height
     * @param dstX Destination X position
     * @param dstY Destination Y position
     */
    void blitFrom(GLuint srcFbo, int srcX, int srcY, int srcWidth, int srcHeight,
                  int dstX = 0, int dstY = 0);
    
    /**
     * @brief Copy entire default framebuffer to this one
     * @param viewportWidth Current viewport width
     * @param viewportHeight Current viewport height
     */
    void captureScreen(int viewportWidth, int viewportHeight);

private:
    void cleanup();
    bool checkComplete();
    
    GLuint fbo_ = 0;
    GLuint colorTexture_ = 0;
    GLuint depthRbo_ = 0;  // Renderbuffer for depth (optional)
    
    int width_ = 0;
    int height_ = 0;
    bool useFloat16_ = false;  // RGBA16F for blur precision
    bool isComplete_ = false;
};

/**
 * @brief Manages multiple framebuffers for ping-pong blur operations
 */
class FramebufferPingPong {
public:
    FramebufferPingPong() = default;
    ~FramebufferPingPong() = default;
    
    /**
     * @brief Initialize both framebuffers
     */
    bool init(int width, int height, bool useFloat16 = false);
    
    /**
     * @brief Resize both framebuffers
     */
    bool resize(int width, int height);
    
    /**
     * @brief Destroy both framebuffers
     */
    void destroy();
    
    /**
     * @brief Swap which buffer is source/destination
     */
    void swap();
    
    /**
     * @brief Get current source framebuffer
     */
    Framebuffer& getSource() { return buffers_[currentSource_]; }
    
    /**
     * @brief Get current destination framebuffer
     */
    Framebuffer& getDest() { return buffers_[1 - currentSource_]; }
    
    /**
     * @brief Get source texture for reading
     */
    GLuint getSourceTexture() const { return buffers_[currentSource_].getColorTexture(); }
    
    /**
     * @brief Get destination texture
     */
    GLuint getDestTexture() const { return buffers_[1 - currentSource_].getColorTexture(); }
    
    bool isValid() const { return buffers_[0].isValid() && buffers_[1].isValid(); }

private:
    Framebuffer buffers_[2];
    int currentSource_ = 0;
};

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