#pragma once

#include <SDL3/SDL.h>

#include <string>
#include <utility>

namespace Rendering {

    /** @brief Identificador del shader de post-procesado activo */
    enum class ShaderType { POSTFX,
        CRTPI };

    /**
     * @brief Parámetros de intensidad de los efectos PostFX
     * Definido a nivel de namespace para facilitar el uso desde subclases y screen.cpp
     */
    struct PostFXParams {
        float vignette = 0.0F;   // Intensidad de la viñeta
        float scanlines = 0.0F;  // Intensidad de las scanlines
        float chroma = 0.0F;     // Aberración cromática
        float mask = 0.0F;       // Máscara de fósforo RGB
        float gamma = 0.0F;      // Corrección gamma (blend 0=off, 1=full)
        float curvature = 0.0F;  // Curvatura barrel CRT
        float bleeding = 0.0F;   // Sangrado de color NTSC
        float flicker = 0.0F;    // Parpadeo de fósforo CRT ~50 Hz
    };

    /**
     * @brief Parámetros del shader CRT-Pi (algoritmo de scanlines continuas)
     * Diferente al PostFX: usa pesos gaussianos por distancia subpixel y bloom.
     */
    struct CrtPiParams {
        float scanline_weight{6.0F};           // Ajuste gaussiano (mayor = scanlines más estrechas)
        float scanline_gap_brightness{0.12F};  // Brillo mínimo en las ranuras entre scanlines
        float bloom_factor{3.5F};              // Factor de brillo para zonas iluminadas
        float input_gamma{2.4F};               // Gamma de entrada (linealización)
        float output_gamma{2.2F};              // Gamma de salida (codificación)
        float mask_brightness{0.80F};          // Sub-píxeles tenues en la máscara de fósforo
        float curvature_x{0.05F};              // Distorsión barrel eje X
        float curvature_y{0.10F};              // Distorsión barrel eje Y
        int mask_type{2};                      // 0=ninguna, 1=verde/magenta, 2=RGB fósforo
        bool enable_scanlines{true};           // Activar efecto de scanlines
        bool enable_multisample{true};         // Antialiasing analítico de scanlines
        bool enable_gamma{true};               // Corrección gamma
        bool enable_curvature{false};          // Distorsión barrel CRT
        bool enable_sharper{false};            // Submuestreo más nítido (modo SHARPER)
    };

    /**
     * @brief Interfaz abstracta para backends de renderizado con shaders
     *
     * Esta interfaz define el contrato que todos los backends de shaders
     * deben cumplir (OpenGL, Metal, Vulkan, etc.)
     */
    class ShaderBackend {
       public:
        virtual ~ShaderBackend() = default;

        /**
         * @brief Inicializa el backend de shaders
         * @param window Ventana SDL
         * @param texture Textura de backbuffer a la que aplicar shaders
         * @param vertex_source Código fuente del vertex shader
         * @param fragment_source Código fuente del fragment shader
         * @return true si la inicialización fue exitosa
         */
        virtual auto init(SDL_Window* window,
            SDL_Texture* texture,
            const std::string& vertex_source,
            const std::string& fragment_source) -> bool = 0;

        /**
         * @brief Renderiza la textura con los shaders aplicados
         */
        virtual void render() = 0;

        /**
         * @brief Establece el tamaño de la textura como parámetro del shader
         * @param width Ancho de la textura
         * @param height Alto de la textura
         */
        virtual void setTextureSize(float width, float height) = 0;

        /**
         * @brief Limpia y libera recursos del backend
         */
        virtual void cleanup() = 0;

        /**
         * @brief Sube píxeles ARGB8888 desde la CPU al backend de shaders
         * Usado por SDL3GPUShader para evitar pasar por SDL_Texture
         */
        virtual void uploadPixels(const Uint32* /*pixels*/, int /*width*/, int /*height*/) {}

        /**
         * @brief Establece los parámetros de intensidad de los efectos PostFX
         * @param p Struct con todos los parámetros PostFX
         */
        virtual void setPostFXParams(const PostFXParams& /*p*/) {}

        /**
         * @brief Activa o desactiva VSync en el swapchain del GPU device
         */
        virtual void setVSync(bool /*vsync*/) {}

        /**
         * @brief Activa o desactiva el escalado entero (integer scale)
         */
        virtual void setScaleMode(bool /*integer_scale*/) {}

        /**
         * @brief Establece el factor de supersampling (1 = off, 3 = 3× SS)
         * Con factor > 1, la textura GPU se crea a game×factor resolución y
         * las scanlines se hornean en CPU (uploadPixels). El sampler usa LINEAR.
         */
        virtual void setOversample(int /*factor*/) {}

        /**
         * @brief Activa/desactiva interpolación LINEAR en el paso de upscale (SS).
         * Por defecto NEAREST (false). Solo tiene efecto con supersampling activo.
         */
        virtual void setLinearUpscale(bool /*linear*/) {}
        [[nodiscard]] virtual auto isLinearUpscale() const -> bool { return false; }

        /**
         * @brief Selecciona el algoritmo de downscale tras el PostFX (SS activo).
         * 0 = bilinear legacy (comportamiento actual, sin textura intermedia),
         * 1 = Lanczos2 (ventana 2, ~25 muestras), 2 = Lanczos3 (ventana 3, ~49 muestras).
         */
        virtual void setDownscaleAlgo(int /*algo*/) {}
        [[nodiscard]] virtual auto getDownscaleAlgo() const -> int { return 0; }

        /**
         * @brief Devuelve las dimensiones de la textura de supersampling.
         * @return Par (ancho, alto) en píxeles; (0, 0) si SS está desactivado.
         */
        [[nodiscard]] virtual auto getSsTextureSize() const -> std::pair<int, int> { return {0, 0}; }

        /**
         * @brief Verifica si el backend está usando aceleración por hardware
         * @return true si usa aceleración (OpenGL/Metal/Vulkan)
         */
        [[nodiscard]] virtual auto isHardwareAccelerated() const -> bool = 0;

        /**
         * @brief Nombre del driver GPU activo (p.ej. "vulkan", "metal", "direct3d12")
         * @return Cadena vacía si no disponible
         */
        [[nodiscard]] virtual auto getDriverName() const -> std::string { return {}; }

        /**
         * @brief Establece el driver GPU preferido antes de init().
         * Vacío = selección automática de SDL. Implementado en SDL3GPUShader.
         */
        virtual void setPreferredDriver(const std::string& /*driver*/) {}

        /**
         * @brief Selecciona el shader de post-procesado activo (POSTFX o CRTPI).
         * Debe llamarse antes de render(). No recrea pipelines.
         */
        virtual void setActiveShader(ShaderType /*type*/) {}

        /**
         * @brief Establece los parámetros del shader CRT-Pi.
         */
        virtual void setCrtPiParams(const CrtPiParams& /*p*/) {}

        /**
         * @brief Devuelve el shader de post-procesado activo.
         */
        [[nodiscard]] virtual auto getActiveShader() const -> ShaderType { return ShaderType::POSTFX; }

        /**
         * @brief Activa/desactiva estirament vertical 4:3 (200→240 línies efectives).
         * Només afecta el viewport, no les textures ni els shaders.
         */
        virtual void setStretch4_3(bool /*enabled*/) {}
        [[nodiscard]] virtual auto isStretch4_3() const -> bool { return false; }

        /**
         * @brief Filtre per a l'estirament 4:3 (false=NEAREST, true=LINEAR).
         */
        virtual void setStretchFilter(bool /*linear*/) {}
    };

}  // namespace Rendering
