#pragma once

#include <SDL3/SDL.h>

#include <array>    // Para std::array
#include <cstdint>  // Para uint8_t
#include <memory>   // Para shared_ptr

#include "game/scenes/scene.hpp"  // Para Scene
#include "utils/delta_timer.hpp"  // Para DeltaTimer
#include "utils/utils.hpp"        // Para PaletteColor
class Sprite;                     // Forward declaration
class Surface;                    // Forward declaration

class LoadingScreen : public Scene {
   public:
    // --- Constructor y Destructor ---
    LoadingScreen();
    ~LoadingScreen() override;

    // --- Bucle principal (SDL3 Callback API) ---
    void iterate() override;
    void handleEvent(const SDL_Event& event) override;

   private:
    // --- Enumeraciones ---
    // Estados de la secuencia de carga
    enum class State : std::uint8_t {
        SILENT1,        // Pausa inicial antes de empezar
        HEADER1,        // Cabecera
        DATA1,          // Datos
        SILENT2,        // Segunda pausa
        HEADER2,        // Cabecera pantalla
        LOADING_MONO,   // Carga de pantalla monocromática (escaneo de líneas)
        LOADING_COLOR,  // Carga de pantalla en color (bloques)
        DATA2,          // Datos
        COMPLETE        // Carga completa
    };

    // Tipos de borde para la pantalla de carga
    enum class Border : std::uint8_t {
        NONE,
        YELLOW_AND_BLUE,
        RED_AND_CYAN,
        WHITE,
        BLACK,
        RED,
        CYAN
    };

    // --- Estructuras ---
    struct Carrier {
        float offset{0.0F};      // Offset para la carga de cabeceras
        bool toggle{false};      // Para cambiar el color inicial
        float total_time{0.0F};  // Tiempo acumulado para modulación de velocidad
    };

    struct Noise {
        float value{0.0F};       // Nivel actual de ruido (0.0 a 1.0)
        float total_time{0.0F};  // Tiempo acumulado para modulación
        bool crossed{false};     // Flag para detectar cruce de umbral
    };

    // --- Constantes de tiempo (en segundos) ---
    static constexpr float SILENT1_DURATION = 2.0F;        // Pausa inicial
    static constexpr float HEADER1_DURATION = 4.0F;        // Cabecera
    static constexpr float DATA1_DURATION = 0.18F;         // Datos
    static constexpr float SILENT2_DURATION = 1.6F;        // Segunda pausa
    static constexpr float HEADER2_DURATION = 2.0F;        // Cabecera pantalla
    static constexpr float LOADING_MONO_DURATION = 16.0F;  // Duración total de la carga monocromática
    static constexpr float LOADING_COLOR_DURATION = 4.0F;  // Duración total de la carga en color
    static constexpr float DATA2_DURATION = 5.0F;          // Datos

    // --- Constantes de geometría ---
    static constexpr int MONO_TOTAL_LINES = 192;        // Total de líneas en carga monocromática
    static constexpr int MONO_STEPS_PER_LINE = 5;       // Pasos de animación por línea
    static constexpr int COLOR_TOTAL_BLOCKS = 768;      // Total de bloques en carga color
    static constexpr int COLOR_BLOCK_WIDTH = 16;        // Ancho del bloque de color
    static constexpr int COLOR_BLOCK_HEIGHT = 8;        // Alto del bloque de color
    static constexpr int COLOR_BLOCKS_PER_ROW = 32;     // Bloques por fila (256 / 8)
    static constexpr int COLOR_BLOCK_SPACING = 8;       // Espaciado entre bloques
    static constexpr int HEADER_DATAROW_HEIGHT = 9.0F;  // Alto de las barras del borde de la carga de las cabeceras

    // --- Métodos ---
    void update();                                        // Actualiza las variables
    void render();                                        // Dibuja en pantalla
    static void handleInput();                            // Comprueba las entradas
    void updateState(float delta_time);                   // Actualiza el estado actual
    void transitionToState(State new_state);              // Transiciona a un nuevo estado
    void updateMonoLoad(float delta_time);                // Gestiona la carga monocromática (time-based)
    void updateColorLoad(float delta_time);               // Gestiona la carga en color (time-based)
    void renderBorder();                                  // Pinta el borde
    static void renderDataBorder();                       // Dibuja el efecto de carga amarillo y azul en el borde
    void renderHeaderBorder() const;                      // Dibuja el efecto de carga rojo y azul en el borde
    static void renderColoredBorder(PaletteColor color);  // Dibuja el borde de color
    void initLineIndexArray();                            // Inicializa el array de índices de líneas
    void printProgramName();                              // Escribe el nombre del programa
    void updateCarrier(float delta_time);                 // Actualiza la portadora
    void updateSilent(float delta_time);                  // Actualiza el ruido durante el tiempo de silencio

    // --- Variables miembro ---
    // Objetos y punteros a recursos
    std::shared_ptr<Surface> mono_loading_screen_surface_;   // Surface con la pantalla de carga en blanco y negro
    std::shared_ptr<Surface> color_loading_screen_surface_;  // Surface con la pantalla de carga en color
    std::unique_ptr<Sprite> mono_loading_screen_sprite_;     // SurfaceSprite para manejar la textura mono_loading_screen_surface_
    std::unique_ptr<Sprite> color_loading_screen_sprite_;    // SurfaceSprite para manejar la textura color_loading_screen_surface_
    std::unique_ptr<Sprite> program_sprite_;                 // SurfaceSprite para manejar la textura con el nombre del programa
    std::shared_ptr<Surface> screen_surface_;                // Surface para dibujar la pantalla de carga
    std::unique_ptr<DeltaTimer> delta_timer_;                // Timer para delta time

    // Variables de estado de la secuencia
    State state_{State::SILENT1};               // Estado actual de la secuencia
    float state_time_{0.0F};                    // Tiempo acumulado en el estado actual
    Border current_border_type_{Border::NONE};  // Tipo de borde actual

    // Arrays y estructuras auxiliares
    std::array<int, MONO_TOTAL_LINES> line_index_;                     // El orden en el que se procesan las 192 líneas de la pantalla de carga
    SDL_FRect load_rect_{.x = 0.0F, .y = 0.0F, .w = 0.0F, .h = 1.0F};  // Rectángulo para dibujar la pantalla de carga
    Carrier carrier_;                                                  // Estructura para los efectos de la carga de cabeceras
    Noise noise_;                                                      // Variaciones de ruido durante los silencios

    // Variables de seguimiento para evitar saltos de pasos/bloques
    int last_mono_step_{-1};    // Último paso mono dibujado
    int last_color_block_{-1};  // Último bloque color dibujado
};