#pragma once

#include <SDL3/SDL.h>

#include <cstddef>  // Para size_t
#include <memory>   // Para shared_ptr, __shared_ptr_access
#include <string>   // Para string
#include <vector>   // Para vector

#include "utils/utils.hpp"  // Para Color
class Surface;
class Text;
namespace Rendering {
class ShaderBackend;
}

class Screen {
    public:
        // Tipos de filtro
        enum class Filter : Uint32 {
            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(Color 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 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
        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 shaders
        void nextPalette();       // Cambia a la siguiente paleta
        void previousPalette();   // Cambia a la paleta anterior
        void setPalete();         // Establece la paleta actual
        void toggleShaders();     // Cambia el estado de los shaders

        // 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 toggleDebugInfo();                    // Activa o desactiva la información de debug

        // Getters
        auto getRenderer() -> SDL_Renderer*;
        auto getRendererSurface() -> std::shared_ptr<Surface>;
        auto getBorderSurface() -> std::shared_ptr<Surface>;
        [[nodiscard]] auto getText() const -> std::shared_ptr<Text> { return text_; }

    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 processPaletteList();                            // Extrae los nombres de las paletas
        void surfaceToTexture();                              // Copia la surface a la textura
        void textureToRenderer();                             // Copia la textura al renderizador
        void renderOverlays();                                // Renderiza todos los overlays
        auto findPalette(const std::string& name) -> size_t;  // Localiza la paleta dentro del vector de paletas
        void initShaders();                                   // Inicializa los shaders
        void loadShaders();                                   // Carga el contenido del archivo GLSL
        void renderInfo();                                    // Muestra información por pantalla
        void getDisplayInfo();                                // Obtiene información sobre la pantalla
        auto initSDLVideo() -> bool;                          // Arranca SDL VIDEO y crea la ventana
        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

        // 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
        SDL_FRect game_surface_dstrect_;    // Coordenadas donde se dibuja la textura del juego

        // Paletas y colores
        Uint8 border_color_{0};             // Color del borde
        std::vector<std::string> palettes_; // Listado de ficheros de paleta disponibles
        Uint8 current_palette_{0};          // Índice para el vector de paletas

        // 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 vertex_shader_source_;   // Almacena el vertex shader
        std::string fragment_shader_source_; // Almacena el fragment shader

#ifdef _DEBUG
        bool show_debug_info_{true};   // Indica si ha de mostrar la información de debug
#else
        bool show_debug_info_{false};  // Indica si ha de mostrar la información de debug
#endif
};