#pragma once

#include <SDL3/SDL.h>
#include <SDL3/SDL_pixels.h>  // Para Uint32

#include <cstddef>  // Para size_t
#include <cstdint>  // Para uint8_t
#include <memory>   // Para shared_ptr, __shared_ptr_access
#include <string>   // Para string
#include <utility>  // Para std::pair
#include <vector>   // Para vector

#include "core/rendering/palette_manager.hpp"  // Para PaletteManager
#include "core/rendering/shader_backend.hpp"   // Para Rendering::ShaderType, ShaderBackend
#include "utils/utils.hpp"                     // Para Color
class Surface;
class Text;

class Screen {
   public:
    // Tipos de filtro
    enum class Filter : std::uint8_t {
        NEAREST = 0,
        LINEAR = 1,
    };

    // Singleton
    static void init();            // Crea el singleton
    static void destroy();         // Destruye el singleton
    static auto get() -> Screen*;  // Obtiene el singleton

    // Renderizado
    void clearRenderer(Rgb color = {0x00, 0x00, 0x00});  // Limpia el renderer
    void clearSurface(Uint8 index);                      // Limpia la game_surface_
    void start();                                        // Prepara para empezar a dibujar en la textura de juego
    void render();                                       // Vuelca el contenido del renderizador en pantalla
    void update(float delta_time);                       // Actualiza la lógica de la clase

    // Video y ventana
    void setVideoMode(bool mode);          // Establece el modo de video
    void toggleVideoMode();                // Cambia entre pantalla completa y ventana
    void handleCanvasResized();            // Restaura el canvas cuando SDL3 no reporta el cambio (emscripten only: salida de fullscreen con Esc, rotación); no-op fuera de emscripten
    void toggleIntegerScale();             // Alterna entre activar y desactivar el escalado entero
    void toggleVSync();                    // Alterna entre activar y desactivar el V-Sync
    auto decWindowZoom() -> bool;          // Reduce el tamaño de la ventana
    auto incWindowZoom() -> bool;          // Aumenta el tamaño de la ventana
    auto setWindowZoom(int zoom) -> bool;  // Establece zoom directo; false si fuera de [1, max_zoom]
    void show();                           // Muestra la ventana
    void hide();                           // Oculta la ventana

    // Borde
    void setBorderColor(Uint8 color);          // Cambia el color del borde
    static void setBorderWidth(int width);     // Establece el ancho del borde
    static void setBorderHeight(int height);   // Establece el alto del borde
    static void setBorderEnabled(bool value);  // Establece si se ha de ver el borde
    void toggleBorder();                       // Cambia entre borde visible y no visible

    // Paletas y PostFX
    void nextPalette();                                                      // Cambia a la siguiente paleta
    void previousPalette();                                                  // Cambia a la paleta anterior
    auto setPaletteByName(const std::string& name) -> bool;                  // Cambia a paleta por nombre; false si no existe
    [[nodiscard]] auto getPaletteNames() const -> std::vector<std::string>;  // Nombres disponibles (minúsculas, sin .pal)
    [[nodiscard]] auto getPalettePrettyName() const -> std::string;          // Nombre actual con guiones sustituidos por espacios
    void nextPaletteSortMode();                                              // Cicla al siguiente modo de ordenación de paleta
    void setPaletteSortMode(PaletteSortMode mode);                           // Establece modo de ordenación concreto
    [[nodiscard]] auto getPaletteSortModeName() const -> std::string;        // Nombre del modo de ordenación actual
    void toggleShaders();                                                    // Activa/desactiva todos los shaders respetando current_shader
    void toggleSupersampling();                                              // Activa/desactiva el supersampling global
    void reloadPostFX();                                                     // Recarga el shader del preset actual sin toggle
    void reloadCrtPi();                                                      // Recarga el shader CrtPi del preset actual sin toggle
    void setLinearUpscale(bool linear);                                      // Upscale NEAREST (false) o LINEAR (true) en el paso SS
    void setDownscaleAlgo(int algo);                                         // 0=bilinear legacy, 1=Lanczos2, 2=Lanczos3
    void setActiveShader(Rendering::ShaderType type);                        // Cambia el shader de post-procesado activo
    void nextShader();                                                       // Cicla al siguiente shader disponible (para futura UI)

    // Render offset (para transiciones de pantalla)
    void setRenderOffset(int x, int y);  // Establece el offset de renderizado
    [[nodiscard]] auto getRenderOffsetX() const -> int { return render_offset_x_; }
    [[nodiscard]] auto getRenderOffsetY() const -> int { return render_offset_y_; }

    // Surfaces y notificaciones
    void setRendererSurface(const std::shared_ptr<Surface>& surface = nullptr);  // Establece el renderizador para las surfaces
    void setNotificationsEnabled(bool value);                                    // Establece la visibilidad de las notificaciones
    void updateZoomFactor();                                                     // Recalcula y almacena el factor de zoom real

    // Captura el contenido actual (borde + juego si el borde está activo, solo juego si no)
    void captureComposite(std::vector<Uint32>& buffer, int& out_width, int& out_height) const;

    // Getters
    auto getRenderer() -> SDL_Renderer*;
    auto getRendererSurface() -> std::shared_ptr<Surface>;
    auto getBorderSurface() -> std::shared_ptr<Surface>;
    auto getGameSurface() -> std::shared_ptr<Surface>;
    [[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; }
    [[nodiscard]] auto getGameSurfaceDstRect() const -> SDL_FRect { return game_surface_dstrect_; }
    [[nodiscard]] auto getGPUDriver() const -> const std::string& { return gpu_driver_; }
    [[nodiscard]] auto isHardwareAccelerated() const -> bool { return shader_backend_ && shader_backend_->isHardwareAccelerated(); }
    [[nodiscard]] auto getLastFPS() const -> int { return fps_.last_value; }
    [[nodiscard]] auto getZoomFactor() const -> float { return zoom_factor_; }
    [[nodiscard]] static auto getMaxZoom() -> int;
    [[nodiscard]] auto getSsTextureSize() const -> std::pair<int, int>;

   private:
    // Estructuras
    struct DisplayMonitor {
        std::string name;
        int width{0};
        int height{0};
        int refresh_rate{0};
    };

    struct FPS {
        Uint32 ticks{0};     // Tiempo en milisegundos desde que se comenzó a contar
        int frame_count{0};  // Número acumulado de frames en el intervalo
        int last_value{0};   // Número de frames calculado en el último segundo

        void increment() {
            frame_count++;
        }

        auto calculate(Uint32 current_ticks) -> int {
            if (current_ticks - ticks >= 1000) {
                last_value = frame_count;
                frame_count = 0;
                ticks = current_ticks;
            }
            return last_value;
        }
    };

    // Constantes
    static constexpr int WINDOWS_DECORATIONS = 35;  // Decoraciones de la ventana

    // Singleton
    static Screen* screen;

    // Métodos privados
    void renderNotifications() const;         // Dibuja las notificaciones
    void adjustWindowSize();                  // Calcula el tamaño de la ventana
    void adjustRenderLogicalSize();           // Ajusta el tamaño lógico del renderizador
    void surfaceToTexture();                  // Copia la surface a la textura
    void textureToRenderer();                 // Copia la textura al renderizador
    void renderOverlays();                    // Renderiza todos los overlays
    void initShaders();                       // Inicializa los shaders
    void applyCurrentPostFXPreset();          // Aplica los parámetros del preset PostFX actual al backend
    void applyCurrentCrtPiPreset();           // Aplica los parámetros del preset CrtPi actual al backend
    void getDisplayInfo();                    // Obtiene información sobre la pantalla
    auto initSDLVideo() -> bool;              // Arranca SDL VIDEO y crea la ventana
    void registerEmscriptenEventCallbacks();  // Registra los callbacks nativos para restaurar el canvas en wasm (no-op fuera de emscripten)
    void createText();                        // Crea el objeto de texto

    // Constructor y destructor
    Screen();
    ~Screen();

    // Objetos SDL
    SDL_Window* window_{nullptr};           // Ventana de la aplicación
    SDL_Renderer* renderer_{nullptr};       // Renderizador de la ventana
    SDL_Texture* game_texture_{nullptr};    // Textura donde se dibuja el juego
    SDL_Texture* border_texture_{nullptr};  // Textura donde se dibuja el borde del juego

    // Surfaces y renderizado
    std::shared_ptr<Surface> game_surface_;                       // Surface principal del juego
    std::shared_ptr<Surface> border_surface_;                     // Surface para el borde de la pantalla
    std::shared_ptr<std::shared_ptr<Surface>> renderer_surface_;  // Puntero a la Surface activa
    std::unique_ptr<Rendering::ShaderBackend> shader_backend_;    // Backend de shaders (OpenGL/Metal/Vulkan)
    std::shared_ptr<Text> text_;                                  // Objeto para escribir texto

    // Buffers persistentes para evitar .resize() cada frame
    std::vector<Uint32> game_pixel_buffer_;    // Textura de juego
    std::vector<Uint32> border_pixel_buffer_;  // Textura de borde (composición final borde+juego)

    Uint32 border_argb_color_{0};  // Color ARGB pre-convertido del borde

    // Configuración de ventana y pantalla
    int window_width_{0};             // Ancho de la pantalla o ventana
    int window_height_{0};            // Alto de la pantalla o ventana
    float zoom_factor_{1.0F};         // Factor de zoom calculado (alto físico / alto lógico)
    SDL_FRect game_surface_dstrect_;  // Coordenadas donde se dibuja la textura del juego

    // Paletas y colores
    Uint8 border_color_{0};                            // Color del borde
    std::unique_ptr<PaletteManager> palette_manager_;  // Gestor de paletas de color

    // Estado y configuración
    bool notifications_enabled_{false};  // Indica si se muestran las notificaciones
    FPS fps_;                            // Gestor de frames por segundo
    DisplayMonitor display_monitor_;     // Información de la pantalla

    // Shaders
    std::string info_resolution_;  // Texto con la información de la pantalla
    std::string gpu_driver_;       // Nombre del driver GPU (SDL3GPU), capturado en initShaders()

    // Render offset para transiciones
    int render_offset_x_{0};
    int render_offset_y_{0};
};