// gpu_frame_renderer.hpp - Renderer de alto nivel basado en SDL_GPU
// © 2025 Orni Attack
//
// Flujo por frame:
//   1. beginFrame(clear_color)
//        → acquire swapchain + begin render pass sobre la textura OFFSCREEN
//          (clear a black; la swapchain se pinta después con el postpro).
//   2. pushLine(...) — encola líneas (extrusión en CPU).
//   3. endFrame()
//        → flush del batch en offscreen + pase de postpro (sample offscreen
//          → swapchain con bloom/flicker/background) + submit + presenta.
//
// La oscilación de brillo y el fondo pulsante viven ahora en el shader de
// postpro; el CPU solo le pasa los uniformes del struct PostFxParams.

#pragma once

#include <SDL3/SDL.h>
#include <SDL3/SDL_gpu.h>

#include <cstdint>
#include <vector>

#include "core/rendering/gpu/gpu_device.hpp"
#include "core/rendering/gpu/gpu_line_pipeline.hpp"
#include "core/rendering/gpu/gpu_postfx_pipeline.hpp"

namespace Rendering::GPU {

// Parámetros del postpro que el caller (SDLManager) pasa cada frame.
// Equivalente al lado "humano" del PostFxUniforms (sin paddings).
struct PostFxParams {
    bool bloom_enabled{true};
    float bloom_intensity{0.6F};
    float bloom_threshold{0.4F};
    float bloom_radius_px{2.0F};

    bool flicker_enabled{true};
    float flicker_amplitude{0.10F};
    float flicker_frequency_hz{6.0F};

    bool background_enabled{true};
    float background_min_r{0.0F};
    float background_min_g{0.02F};
    float background_min_b{0.0F};
    float background_max_r{0.0F};
    float background_max_g{0.06F};
    float background_max_b{0.0F};
    float background_pulse_freq_hz{6.0F};
};

class GpuFrameRenderer {
    public:
        GpuFrameRenderer() = default;
        ~GpuFrameRenderer();

        GpuFrameRenderer(const GpuFrameRenderer&) = delete;
        auto operator=(const GpuFrameRenderer&) -> GpuFrameRenderer& = delete;
        GpuFrameRenderer(GpuFrameRenderer&&) = delete;
        auto operator=(GpuFrameRenderer&&) -> GpuFrameRenderer& = delete;

        // Crea device + pipeline + offscreen + sampler. logical_w/h = tamaño
        // en píxeles lógicos del juego (1280×720), usado como base del
        // offscreen y de la transformación a NDC del shader de líneas.
        [[nodiscard]] auto init(SDL_Window* window, float logical_w, float logical_h) -> bool;
        void destroy();

        // beginFrame: adquiere swapchain, abre render pass sobre offscreen
        // con clear a negro. Devuelve false si no hay textura disponible.
        // Los argumentos clear_r/g/b se ignoran (compatibilidad de API: el
        // fondo lo dibuja el postpro).
        [[nodiscard]] auto beginFrame(float clear_r, float clear_g, float clear_b) -> bool;

        // Encola una línea con grosor configurable (px). Color RGBA en [0..1].
        void pushLine(float x1, float y1, float x2, float y2, float thickness,
                      float r, float g, float b, float a);

        // endFrame: flush del batch de líneas → composite postpro → submit + presenta.
        void endFrame();

        // Viewport del PASE FINAL (postpro → swapchain) en píxeles físicos.
        // Implementa el letterbox: cualquier área fuera del viewport queda
        // como el clear color del pase de composite (negro). Si w<=0 o h<=0,
        // se usa el tamaño completo de la ventana.
        void setViewport(float x, float y, float w, float h);

        // Activa/desactiva VSync. true = SDL_GPU_PRESENTMODE_VSYNC, false = IMMEDIATE.
        void setVSync(bool enabled);

        // Parámetros del postpro que se aplican en endFrame. Por defecto =
        // valores de Defaults (bloom moderado, flicker suave, fondo verde tenue).
        void setPostFx(const PostFxParams& params) { postfx_params_ = params; }
        [[nodiscard]] auto postfx() const -> const PostFxParams& { return postfx_params_; }

        // Acceso a internals.
        [[nodiscard]] auto device() -> GpuDevice& { return device_; }
        [[nodiscard]] auto isInsideFrame() const -> bool { return cmd_buffer_ != nullptr; }

    private:
        GpuDevice device_;
        GpuLinePipeline line_pipeline_;
        GpuPostFxPipeline postfx_pipeline_;

        // Tamaño lógico del juego (= tamaño del offscreen).
        float logical_w_{1280.0F};
        float logical_h_{720.0F};

        // Viewport del pase final en píxeles físicos. <0 = full window.
        float viewport_x_{0.0F};
        float viewport_y_{0.0F};
        float viewport_w_{-1.0F};
        float viewport_h_{-1.0F};

        // Offscreen color target (formato fijo R8G8B8A8_UNORM para portabilidad).
        SDL_GPUTexture* offscreen_texture_{nullptr};
        SDL_GPUTextureFormat offscreen_format_{SDL_GPU_TEXTUREFORMAT_R8G8B8A8_UNORM};
        SDL_GPUSampler* linear_sampler_{nullptr};

        // Batch del frame en curso.
        std::vector<LineVertex> vertices_;
        std::vector<uint16_t> indices_;

        // Estado del frame en curso.
        SDL_GPUCommandBuffer* cmd_buffer_{nullptr};
        SDL_GPUTexture* swapchain_texture_{nullptr};
        SDL_GPURenderPass* render_pass_{nullptr};

        // Parámetros del postpro (configurables vía YAML).
        PostFxParams postfx_params_{};

        // Helpers internos.
        [[nodiscard]] auto createOffscreen() -> bool;
        void destroyOffscreen();
        void flushBatch();
        void compositePass();
        void applyFinalViewport();
};

}  // namespace Rendering::GPU
