#pragma once

#include <SDL3/SDL.h>

#include <vector>  // for vector

#include "game/options.hpp"            // for Options::Video, Options::Window (per snapshot cancel)
#include "game/scenes/instructions.h"  // for Instructions::Mode
#include "utils/utils.h"               // for InputDevice, Section
class AnimatedSprite;
class Fade;
class Game;
class Menu;
class SmartSprite;
class Sprite;
class Text;
class Texture;
namespace Ja {
    struct Music;
    struct Sound;
}  // namespace Ja

class Title {
   public:
    Title(SDL_Renderer *renderer, Section *section);  // Constructor
    ~Title();                                         // Destructor

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

    void run();                                // Bucle para el titulo del juego
    void iterate();                            // Ejecuta un frame
    void handleEvent(const SDL_Event *event);  // Procesa un evento

   private:
    static constexpr const char *COPYRIGHT = "@2020 JailDesigner (v2.3.4)";
    static constexpr int COUNTER = 800;

    struct MenuData {
        Menu *title;          // Menu de la pantalla de título
        Menu *options;        // Menú de la pantalla de opciones
        Menu *player_select;  // Menu para elegir jugador
        Menu *active;         // Menu activo (de momento para la pantalla del titulo)
        bool key_pressed;     // Variable para evitar la repetición de teclas en los menus
    };

    // Objetos y punteros
    SDL_Renderer *renderer_;               // El renderizador de la ventana
    Instructions *instructions_{nullptr};  // Objeto para la sección de las instrucciones
    Game *demo_game_{nullptr};             // Objeto para lanzar la demo del juego
    SDL_Event *event_handler_;             // Manejador de eventos
    Section *section_;                     // Indicador para el bucle del titulo

    Texture *dust_texture_;      // Textura con los graficos del polvo
    Texture *coffee_texture_;    // Textura con los graficos de la palabra coffee
    Texture *crisis_texture_;    // Textura con los graficos de la plabra crisis
    Texture *gradient_texture_;  // Textura con los graficos para el degradado del fondo del titulo

    SDL_Rect background_window_;  // Ventana visible para la textura de fondo del titulo
    SDL_Texture *background_;     // Textura dibujar el fondo del titulo

    AnimatedSprite *dust_bitmap_left_;   // Sprite con la el polvo que aparece al colisionar el texto de la pantalla de titulo
    AnimatedSprite *dust_bitmap_right_;  // Sprite con la el polvo que aparece al colisionar el texto de la pantalla de titulo

    SmartSprite *coffee_bitmap_;  // Sprite con la palabra COFFEE para la pantalla de titulo
    SmartSprite *crisis_bitmap_;  // Sprite con la palabra CRISIS para la pantalla de titulo

    Sprite *gradient_;  // Sprite para dibujar el degradado del titulo

    Text *text1_;  // Objeto de texto para poder escribir textos en pantalla
    Text *text2_;  // Objeto de texto para poder escribir textos en pantalla
    Fade *fade_;   // Objeto para realizar fundidos en pantalla

    // Variables
    Ja::Music *title_music_;  // Musica para el titulo
    Ja::Sound *crash_sound_;  // Sonido con el impacto del título
    int background_counter_;   // Temporizador para el fondo de tiles de la pantalla de titulo
    int counter_;              // Temporizador para la pantalla de titulo
    Uint32 ticks_;             // Contador de ticks para ajustar la velocidad del programa
    Uint8 background_mode_;    // Variable para almacenar el tipo de efecto que hará el fondo de la pantalla de titulo
    float sin_[360];           // Vector con los valores del seno precalculados
    bool menu_visible_;        // Indicador para saber si se muestra el menu del titulo o la frase intermitente
    bool demo_;                // Indica si el modo demo estará activo
    Section next_section_;     // Indica cual es la siguiente sección a cargar cuando termine el contador del titulo
    Uint32 ticks_speed_;       // Velocidad a la que se repiten los bucles del programa
    Uint8 post_fade_;          // Opción a realizar cuando termina el fundido
    MenuData menu_;            // Variable con todos los objetos menus y sus variables
    // Snapshot per a permetre CANCEL al menú d'opcions.
    Options::Video prev_video_;
    Options::Window prev_window_;
    Options::Settings prev_settings_;
    std::vector<InputDevice> prev_inputs_;
    std::vector<InputDevice> available_input_devices_;  // Vector con todos los metodos de control disponibles
    std::vector<int> device_index_;                     // Indice para el jugador [i] del vector de dispositivos de entrada disponibles

    // Variables para la vibración del título (SUBSECTION_TITLE_2)
    int vibration_step_;              // Paso actual de la vibración
    int vibration_coffee_base_x_{0};  // Posición X base del bitmap Coffee
    int vibration_crisis_base_x_{0};  // Posición X base del bitmap Crisis
    bool vibration_initialized_;      // Indica si se han capturado las posiciones base

    // Variables para sub-estados delegados (instrucciones y demo)
    bool instructions_active_;                                        // Indica si las instrucciones están activas
    bool demo_game_active_;                                           // Indica si el juego demo está activo
    Instructions::Mode instructions_mode_{Instructions::Mode::AUTO};  // Modo de las instrucciones activas
    bool demo_then_instructions_;                                     // Indica si tras la demo hay que mostrar instrucciones

    void init();        // Inicializa los valores
    void update();      // Actualiza las variables del objeto
    void render();      // Dibuja el objeto en pantalla
    void checkInput();  // Comprueba las entradas

    // Helpers de update, uno por cada subsección y por cada switch dentro del título 3
    void updateTitle1();
    void updateTitle2();
    void updateTitle3();
    void handlePostFadeAction();
    void handleTitleMenuSelection();
    void handlePlayerSelectMenuSelection();
    void handleOptionsMenuSelection();

    void updateBG();                                  // Actualiza el tileado de fondo
    static void switchFullScreenModeVar();            // Cambia el valor de la variable de modo de pantalla completa
    void updateMenuLabels() const;                    // Actualiza los elementos de los menus
    void applyOptions();                              // Aplica las opciones de menu seleccionadas
    void runInstructions(Instructions::Mode mode);    // Ejecuta la parte donde se muestran las instrucciones
    void runDemoGame();                               // Ejecuta el juego en modo demo
    auto updatePlayerInputs(int num_player) -> bool;  // Modifica las opciones para los controles de los jugadores
    void createTiledBackground();                     // Crea el mosaico de fondo del titulo
    void checkInputDevices();                         // Comprueba cuantos mandos hay conectados para gestionar el menu de opciones
    void reLoadTextures();                            // Recarga las texturas
};
