#include "game/scenes/title.h"

#include <SDL3/SDL.h>

#include <cstdlib>   // for rand
#include <iostream>  // for basic_ostream, operator<<, basic_ostrea...
#include <string>    // for basic_string, operator+, char_traits

#include "core/audio/audio.hpp"             // for Audio
#include "core/input/global_inputs.hpp"     // for GlobalInputs::handle
#include "core/input/input.h"               // for Input, INPUT_USE_GAMECONTROLLER, INPUT_...
#include "core/locale/lang.h"               // for Lang, Lang::Code
#include "core/rendering/animatedsprite.h"  // for AnimatedSprite
#include "core/rendering/fade.h"            // for Fade
#include "core/rendering/screen.h"          // for Screen
#include "core/rendering/smartsprite.h"     // for SmartSprite
#include "core/rendering/sprite.h"          // for Sprite
#include "core/rendering/text.h"            // for Text, TXT_CENTER, TXT_SHADOW
#include "core/rendering/texture.h"         // for Texture
#include "core/resources/asset.h"           // for Asset
#include "core/resources/resource.h"
#include "game/defaults.hpp"  // for GAMECANVAS_CENTER_X, SECTION_PROG_QUIT
#include "game/game.h"        // for Game
#include "game/options.hpp"   // for Options
#include "game/ui/menu.h"     // for Menu

// Constructor
Title::Title(SDL_Renderer *renderer, Section *section) {
    // Copia las direcciones de los punteros
    this->renderer_ = renderer;
    this->section_ = section;

    // Reserva memoria para los punteros
    event_handler_ = new SDL_Event();
    fade_ = new Fade(renderer);

    Resource *resource = Resource::get();
    dust_texture_ = resource->getTexture("title_dust.png");
    coffee_texture_ = resource->getTexture("title_coffee.png");
    crisis_texture_ = resource->getTexture("title_crisis.png");
    gradient_texture_ = resource->getTexture("title_gradient.png");

    coffee_bitmap_ = new SmartSprite(coffee_texture_, renderer);
    crisis_bitmap_ = new SmartSprite(crisis_texture_, renderer);
    dust_bitmap_left_ = new AnimatedSprite(dust_texture_, renderer, "", &resource->getAnimationLines("title_dust.ani"));
    dust_bitmap_right_ = new AnimatedSprite(dust_texture_, renderer, "", &resource->getAnimationLines("title_dust.ani"));
    gradient_ = new Sprite({0, 0, 256, 192}, gradient_texture_, renderer);

    text1_ = resource->getText("smb2");
    text2_ = resource->getText("8bithud");

#ifdef GAME_CONSOLE
    menu_.title = R->getMenu("title_gc");
    menu_.options = R->getMenu("options_gc");
#else
    menu_.title = resource->getMenu("title");
    menu_.options = resource->getMenu("options");
#endif
    menu_.player_select = resource->getMenu("player_select");

#ifdef __EMSCRIPTEN__
    // En la versión web no se puede cerrar el programa: ocultamos la opción QUIT del menú de título
    menu_.title->setVisible(3, false);
    menu_.title->setSelectable(3, false);
#endif

    // Sonidos y música (handles compartidos)
    crash_sound_ = resource->getSound("title.wav");
    title_music_ = resource->getMusic("title.ogg");

    // Inicializa los valores
    init();
}

// Destructor
Title::~Title() {
    delete event_handler_;
    delete fade_;

    // Las texturas, Text, Menu, sonido y música son propiedad de Resource —
    // no se liberan aquí. Solo los sprites que posee esta escena.
    delete coffee_bitmap_;
    delete crisis_bitmap_;
    delete dust_bitmap_left_;
    delete dust_bitmap_right_;
    delete gradient_;

    SDL_DestroyTexture(background_);
}

// Inicializa los valores
void Title::init() {
    // Inicializa variables
    section_->subsection = SUBSECTION_TITLE_1;
    counter_ = COUNTER;
    background_counter_ = 0;
    background_mode_ = rand() % 2;
    menu_visible_ = false;
    menu_.active = menu_.title;
    next_section_.name = SECTION_PROG_GAME;
    post_fade_ = 0;
    ticks_ = 0;
    ticks_speed_ = 15;
    fade_->init(0x17, 0x17, 0x26);
    demo_ = true;
    vibration_step_ = 0;
    vibration_initialized_ = false;
    instructions_active_ = false;
    demo_game_active_ = false;
    demo_then_instructions_ = false;

    // Pone valores por defecto a las opciones de control
    Options::inputs.clear();

    InputDevice inp;
    inp.id = 0;
    inp.name = "KEYBOARD";
    inp.device_type = INPUT_USE_KEYBOARD;
    Options::inputs.push_back(inp);

    inp.id = 0;
    inp.name = "GAME CONTROLLER";
    inp.device_type = INPUT_USE_GAMECONTROLLER;
    Options::inputs.push_back(inp);

    // Comprueba si hay mandos conectados
    checkInputDevices();

    // Pone valores por defecto. El primer jugador el teclado. El segundo jugador el primer mando
    device_index_.clear();
    device_index_.push_back(available_input_devices_.size() - 1);  // El último dispositivo encontrado es el teclado
    device_index_.push_back(0);                                    // El primer mando encontrado. Si no ha encontrado ninguno es el teclado

    // Si ha encontrado un mando se lo asigna al segundo jugador
    if (Input::get()->gameControllerFound()) {
        Options::inputs[1].id = available_input_devices_[device_index_[1]].id;
        Options::inputs[1].name = available_input_devices_[device_index_[1]].name;
        Options::inputs[1].device_type = available_input_devices_[device_index_[1]].device_type;
    } else {  // Si no ha encontrado un mando, deshabilita la opción de jugar a 2 jugadores
        menu_.title->setSelectable(1, false);
        menu_.title->setGreyed(1, true);
    }

    // Inicializa el bitmap de Coffee
    coffee_bitmap_->init();
    coffee_bitmap_->setPosX(45);
    coffee_bitmap_->setPosY(11 - 200);
    coffee_bitmap_->setWidth(167);
    coffee_bitmap_->setHeight(46);
    coffee_bitmap_->setVelX(0.0F);
    coffee_bitmap_->setVelY(2.5F);
    coffee_bitmap_->setAccelX(0.0F);
    coffee_bitmap_->setAccelY(0.1F);
    coffee_bitmap_->setSpriteClip(0, 0, 167, 46);
    coffee_bitmap_->setEnabled(true);
    coffee_bitmap_->setEnabledCounter(0);
    coffee_bitmap_->setDestX(45);
    coffee_bitmap_->setDestY(11);

    // Inicializa el bitmap de Crisis
    crisis_bitmap_->init();
    crisis_bitmap_->setPosX(60);
    crisis_bitmap_->setPosY(57 + 200);
    crisis_bitmap_->setWidth(137);
    crisis_bitmap_->setHeight(46);
    crisis_bitmap_->setVelX(0.0F);
    crisis_bitmap_->setVelY(-2.5F);
    crisis_bitmap_->setAccelX(0.0F);
    crisis_bitmap_->setAccelY(-0.1F);
    crisis_bitmap_->setSpriteClip(0, 0, 137, 46);
    crisis_bitmap_->setEnabled(true);
    crisis_bitmap_->setEnabledCounter(0);
    crisis_bitmap_->setDestX(60);
    crisis_bitmap_->setDestY(57);

    // Inicializa el bitmap de DustRight
    dust_bitmap_right_->resetAnimation();
    dust_bitmap_right_->setPosX(218);
    dust_bitmap_right_->setPosY(47);
    dust_bitmap_right_->setWidth(16);
    dust_bitmap_right_->setHeight(16);
    dust_bitmap_right_->setFlip(SDL_FLIP_HORIZONTAL);

    // Inicializa el bitmap de DustLeft
    dust_bitmap_left_->resetAnimation();
    dust_bitmap_left_->setPosX(33);
    dust_bitmap_left_->setPosY(47);
    dust_bitmap_left_->setWidth(16);
    dust_bitmap_left_->setHeight(16);

    // Inicializa el sprite con el degradado
    gradient_->setSpriteClip(0, 96, 256, 192);

    // Crea el mosaico de fondo del titulo
    createTiledBackground();

    // Coloca la ventana que recorre el mosaico de fondo de manera que coincida con el mosaico que hay pintado en el titulo al iniciar
    background_window_.x = 128;
    background_window_.y = 96;
    background_window_.w = GAMECANVAS_WIDTH;
    background_window_.h = GAMECANVAS_HEIGHT;

    // Inicializa los valores del vector con los valores del seno
    for (int i = 0; i < 360; ++i) {
        sin_[i] = SDL_sinf((float)i * 3.14F / 180.0F);
    }

    // Actualiza los textos de los menus
    updateMenuLabels();
}

// Actualiza las variables del objeto
void Title::update() {
    Audio::update();
    checkInput();

    if (SDL_GetTicks() - ticks_ <= ticks_speed_) {
        return;
    }
    ticks_ = SDL_GetTicks();

    switch (section_->subsection) {
        case SUBSECTION_TITLE_1:
            updateTitle1();
            break;
        case SUBSECTION_TITLE_2:
            updateTitle2();
            break;
        case SUBSECTION_TITLE_3:
            updateTitle3();
            break;
        default:
            break;
    }
}

// Sección 1 - Titulo desplazandose
void Title::updateTitle1() {
    coffee_bitmap_->update();
    crisis_bitmap_->update();

    // Si los objetos han llegado a su destino, cambiamos de Sección
    if (coffee_bitmap_->hasFinished() && crisis_bitmap_->hasFinished()) {
        section_->subsection = SUBSECTION_TITLE_2;

        // Pantallazo blanco: pintar sobre el gameCanvas y dejar
        // que Screen::blit() presente por la ruta activa (GPU o
        // SDL_Renderer). Un `SDL_RenderPresent(renderer)` directe
        // crasheja quan el SDL3 GPU ha reclamat la ventana.
        Screen::get()->start();
        SDL_SetRenderDrawColor(renderer_, 0xFF, 0xFF, 0xFF, 0xFF);
        SDL_RenderClear(renderer_);
        Screen::get()->blit();

        Audio::get()->playSound(crash_sound_);
    }
}

// Sección 2 - Titulo vibrando
void Title::updateTitle2() {
    // Captura las posiciones base la primera vez
    if (!vibration_initialized_) {
        vibration_coffee_base_x_ = coffee_bitmap_->getPosX();
        vibration_crisis_base_x_ = crisis_bitmap_->getPosX();
        vibration_initialized_ = true;
    }

    const int V[] = {-1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 0};
    coffee_bitmap_->setPosX(vibration_coffee_base_x_ + V[vibration_step_ / 3]);
    crisis_bitmap_->setPosX(vibration_crisis_base_x_ + V[vibration_step_ / 3]);
    dust_bitmap_right_->update();
    dust_bitmap_left_->update();

    vibration_step_++;

    if (vibration_step_ >= 33) {
        section_->subsection = SUBSECTION_TITLE_3;
        vibration_step_ = 0;
        vibration_initialized_ = false;
    }
}

// Sección 3 - La pantalla de titulo con el menú y la música
void Title::updateTitle3() {
    if (counter_ > 0) {
        if (Audio::getRealMusicState() == Audio::MusicState::STOPPED) {
            Audio::get()->playMusic(title_music_);
        }

        dust_bitmap_right_->update();
        dust_bitmap_left_->update();
        fade_->update();

        if (fade_->hasEnded()) {
            handlePostFadeAction();
        }

        updateBG();

        if (menu_visible_ && !fade_->isEnabled()) {
            menu_.active->update();
        }

        if (menu_.active->getName() == "TITLE") {
            handleTitleMenuSelection();
        }
        if (menu_.active->getName() == "PLAYER_SELECT") {
            handlePlayerSelectMenuSelection();
        }
        if (menu_.active->getName() == "OPTIONS") {
            handleOptionsMenuSelection();
        }

        if (menu_.active->getName() == "TITLE") {
            counter_--;
        }
    } else if (counter_ == 0) {
        if (demo_) {
            demo_then_instructions_ = true;
            runDemoGame();
        } else {
            section_->name = SECTION_PROG_LOGO;
        }
    }

    if (section_->subsection == SUBSECTION_TITLE_INSTRUCTIONS) {
        runInstructions(Instructions::Mode::AUTO);
    }
}

// Acción tras finalizar el fundido del título
void Title::handlePostFadeAction() {
    switch (post_fade_) {
        case 0:  // 1 PLAYER
            section_->name = SECTION_PROG_GAME;
            section_->subsection = SUBSECTION_GAME_PLAY_1P;
            Audio::get()->stopMusic();
            break;

        case 1:  // 2 PLAYERS
            section_->name = SECTION_PROG_GAME;
            section_->subsection = SUBSECTION_GAME_PLAY_2P;
            Audio::get()->stopMusic();
            break;

        case 2:  // QUIT
#ifndef __EMSCRIPTEN__
            section_->name = SECTION_PROG_QUIT;
            Audio::get()->stopMusic();
#endif
            break;

        case 3:  // TIME OUT
            counter_ = COUNTER;
            menu_.active->reset();
            if (demo_) {
                demo_then_instructions_ = true;
                runDemoGame();
            } else {
                section_->name = SECTION_PROG_LOGO;
            }
            break;

        default:
            break;
    }
}

// Procesa selección del menú TITLE
void Title::handleTitleMenuSelection() {
    switch (menu_.active->getItemSelected()) {
        case 0:  // 1 PLAYER -> Cambia al manu de selección de jugador
            menu_.active = menu_.player_select;
            break;

        case 1:  // 2 PLAYERS
            post_fade_ = 1;
            fade_->activateFade();
            break;

        case 2:  // OPTIONS
            menu_.active = menu_.options;
            prev_video_ = Options::video;
            prev_window_ = Options::window;
            prev_settings_ = Options::settings;
            prev_inputs_ = Options::inputs;
            break;

        case 3:  // QUIT
            post_fade_ = 2;
            fade_->activateFade();
            break;

        default:
            break;
    }
}

// Procesa selección del menú PLAYER_SELECT
void Title::handlePlayerSelectMenuSelection() {
    switch (menu_.active->getItemSelected()) {
        case 0:
            // Este item no se puede seleccionar y actua de titulo
            break;

        case 1:  // BAL1
            post_fade_ = 0;
            Options::settings.player_selected = 0;
            fade_->activateFade();
            break;

        case 2:  // AROUNDER
            post_fade_ = 0;
            Options::settings.player_selected = 1;
            fade_->activateFade();
            break;

        case 3:  // BACK
            menu_.active = menu_.title;
            menu_.player_select->reset();
            break;

        default:
            break;
    }
}

// Procesa selección del menú OPTIONS
void Title::handleOptionsMenuSelection() {
    switch (menu_.active->getItemSelected()) {
        case 0:  // Difficulty
            if (Options::settings.difficulty == DIFFICULTY_EASY) {
                Options::settings.difficulty = DIFFICULTY_NORMAL;
            } else if (Options::settings.difficulty == DIFFICULTY_NORMAL) {
                Options::settings.difficulty = DIFFICULTY_HARD;
            } else {
                Options::settings.difficulty = DIFFICULTY_EASY;
            }
            updateMenuLabels();
            break;

        case 1:  // PLAYER 1 CONTROLS
            updatePlayerInputs(0);
            updateMenuLabels();
            break;

        case 3:  // PLAYER 2 CONTROLS
            updatePlayerInputs(1);
            updateMenuLabels();
            break;

        case 5:  // Language
            Options::settings.language = Lang::nextLanguage(Options::settings.language);
            updateMenuLabels();
            break;

        case 6:  // Display mode
            switchFullScreenModeVar();
            if (Options::video.fullscreen) {
                menu_.options->setSelectable(8, false);
                menu_.options->setGreyed(8, true);
            } else {
                menu_.options->setSelectable(8, true);
                menu_.options->setGreyed(8, false);
            }
            updateMenuLabels();
            break;

        case 8:  // Windows size
            Options::window.zoom++;
            if (Options::window.zoom > Options::window.max_zoom) {
                Options::window.zoom = 1;
            }
            updateMenuLabels();
            break;

        case 9:  // Scale mode
            Options::video.scale_mode = (Options::video.scale_mode == SDL_SCALEMODE_NEAREST)
                ? SDL_SCALEMODE_LINEAR
                : SDL_SCALEMODE_NEAREST;
            Texture::setGlobalScaleMode(Options::video.scale_mode);
            reLoadTextures();
            updateMenuLabels();
            break;

        case 10:  // VSYNC
            Options::video.vsync = !Options::video.vsync;
            updateMenuLabels();
            break;

        case 11:  // HOW TO PLAY
            runInstructions(Instructions::Mode::MANUAL);
            break;

        case 12:  // ACCEPT
            applyOptions();
            menu_.active->reset();
            menu_.active = menu_.title;
            break;

        case 13:  // CANCEL
            Options::video = prev_video_;
            Options::window = prev_window_;
            Options::settings = prev_settings_;
            Options::inputs = prev_inputs_;
            updateMenuLabels();
            menu_.active->reset();
            menu_.active = menu_.title;
            break;

        default:
            break;
    }
}

// Dibuja el objeto en pantalla
void Title::render() {
    switch (section_->subsection) {
        // Sección 1 - Titulo desplazandose
        case SUBSECTION_TITLE_1: {
            // Prepara para empezar a dibujar en la textura de juego
            Screen::get()->start();

            // Limpia la pantalla
            Screen::get()->clean(bgColor);

            // Dibuja el tileado de fondo
            {
                SDL_FRect f_src = {(float)background_window_.x, (float)background_window_.y, (float)background_window_.w, (float)background_window_.h};
                SDL_RenderTexture(renderer_, background_, &f_src, nullptr);
            };

            // Dibuja el degradado
            gradient_->render();

            // Dibuja los objetos
            coffee_bitmap_->render();
            crisis_bitmap_->render();

            // Vuelca el contenido del renderizador en pantalla
            Screen::get()->blit();
        } break;

        // Sección 2 - Titulo vibrando
        case SUBSECTION_TITLE_2: {
            // Prepara para empezar a dibujar en la textura de juego
            Screen::get()->start();

            // Limpia la pantalla
            Screen::get()->clean(bgColor);

            // Dibuja el tileado de fondo
            {
                SDL_FRect f_src = {(float)background_window_.x, (float)background_window_.y, (float)background_window_.w, (float)background_window_.h};
                SDL_RenderTexture(renderer_, background_, &f_src, nullptr);
            };

            // Dibuja el degradado
            gradient_->render();

            // Dibuja los objetos (posiciones ya actualizadas por update)
            coffee_bitmap_->render();
            crisis_bitmap_->render();

            dust_bitmap_right_->render();
            dust_bitmap_left_->render();

            // Vuelca el contenido del renderizador en pantalla
            Screen::get()->blit();
        } break;

        // Sección 3 - La pantalla de titulo con el menú y la música
        case SUBSECTION_TITLE_3: {  // Prepara para empezar a dibujar en la textura de juego
            Screen::get()->start();

            // Limpia la pantalla
            Screen::get()->clean(bgColor);

            // Dibuja el tileado de fondo
            {
                SDL_FRect f_src = {(float)background_window_.x, (float)background_window_.y, (float)background_window_.w, (float)background_window_.h};
                SDL_RenderTexture(renderer_, background_, &f_src, nullptr);
            };

            // Dibuja el degradado
            gradient_->render();

            // Dibuja los objetos
            if (menu_.active->getName() != "OPTIONS") {
                // Bitmaps con el logo/titulo del juego
                coffee_bitmap_->render();
                crisis_bitmap_->render();

                // Texto con el copyright y versión
                text2_->writeDX(TXT_CENTER | TXT_SHADOW, GAMECANVAS_CENTER_X, GAMECANVAS_HEIGHT - (BLOCK * 2), COPYRIGHT, 1, noColor, 1, shdwTxtColor);
            }

            if (menu_visible_) {
                menu_.active->render();
            }

            dust_bitmap_right_->render();
            dust_bitmap_left_->render();

            // PRESS ANY KEY!
            if ((counter_ % 50 > 14) && (!menu_visible_)) {
                text1_->writeDX(TXT_CENTER | TXT_SHADOW, GAMECANVAS_CENTER_X, PLAY_AREA_THIRD_QUARTER_Y + BLOCK, Lang::get()->getText(23), 1, noColor, 1, shdwTxtColor);
            }

            // Fade
            fade_->render();

            // Vuelca el contenido del renderizador en pantalla
            Screen::get()->blit();
        } break;

        default:
            break;
    }
}

// Comprueba las entradas
void Title::checkInput() {
#ifndef __EMSCRIPTEN__
    if (Input::get()->checkInput(EXIT, REPEAT_FALSE)) {
        section_->name = SECTION_PROG_QUIT;
        return;
    }
#endif
    GlobalInputs::handle();
}

// Actualiza el tileado de fondo
void Title::updateBG() {
    if (background_mode_ == 0) {  // El tileado de fondo se desplaza en diagonal
        ++background_window_.x %= 64;
        ++background_window_.y %= 64;
    } else {  // El tileado de fondo se desplaza en circulo
        ++background_counter_ %= 360;
        background_window_.x = 128 + (int(sin_[(background_counter_ + 270) % 360] * 128));
        background_window_.y = 96 + (int(sin_[(360 - background_counter_) % 360] * 96));
    }
}

// Cambia el valor de la variable de modo de pantalla completa
void Title::switchFullScreenModeVar() {
    Options::video.fullscreen = !Options::video.fullscreen;
}

// Actualiza los elementos de los menus
void Title::updateMenuLabels() const {
    int i = 0;
    // DIFFICULTY
    switch (Options::settings.difficulty) {
        case DIFFICULTY_EASY:
            menu_.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(66));  // EASY
            break;

        case DIFFICULTY_NORMAL:
            menu_.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(67));  // NORMAL
            break;

        case DIFFICULTY_HARD:
            menu_.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(68));  // HARD
            break;

        default:
            menu_.options->setItemCaption(i, Lang::get()->getText(59) + ": " + Lang::get()->getText(67));  // NORMAL
            break;
    }

    i++;
    // PLAYER 1 CONTROLS
    menu_.options->setItemCaption(i, Lang::get()->getText(62));

    i++;
    // PLAYER 1 CONTROLS - OPTIONS
    switch (Options::inputs[0].device_type) {
        case INPUT_USE_KEYBOARD:
            menu_.options->setItemCaption(i, Lang::get()->getText(69));  // KEYBOARD
            menu_.options->setGreyed(i, false);
            break;

        case INPUT_USE_GAMECONTROLLER:
            menu_.options->setItemCaption(i, Lang::get()->getText(70));  // GAME CONTROLLER
            if (!Input::get()->gameControllerFound()) {
                menu_.options->setGreyed(i, true);
            } else {
                menu_.options->setGreyed(i, false);
                menu_.options->setItemCaption(i, Options::inputs[0].name);
            }
            break;

        default:
            menu_.options->setItemCaption(i, Lang::get()->getText(69));  // KEYBOARD
            break;
    }

    i++;
    // PLAYER 2 CONTROLS
    menu_.options->setItemCaption(i, Lang::get()->getText(63));

    i++;
    // PLAYER 2 CONTROLS - OPTIONS
    switch (Options::inputs[1].device_type) {
        case INPUT_USE_KEYBOARD:
            menu_.options->setItemCaption(i, Lang::get()->getText(69));  // KEYBOARD
            menu_.options->setGreyed(i, false);
            break;

        case INPUT_USE_GAMECONTROLLER:
            menu_.options->setItemCaption(i, Lang::get()->getText(70));  // GAME CONTROLLER
            if (!Input::get()->gameControllerFound()) {
                menu_.options->setGreyed(i, true);
            } else {
                menu_.options->setGreyed(i, false);
                menu_.options->setItemCaption(i, Options::inputs[1].name);
            }
            break;

        default:
            menu_.options->setItemCaption(i, Lang::get()->getText(69));  // KEYBOARD
            break;
    }

    i++;
    // LANGUAGE
    switch (Options::settings.language) {
        case Lang::Code::ES_ES:
            menu_.options->setItemCaption(i, Lang::get()->getText(8) + ": " + Lang::get()->getText(24));  // SPANISH
            break;

        case Lang::Code::BA_BA:
            menu_.options->setItemCaption(i, Lang::get()->getText(8) + ": " + Lang::get()->getText(25));  // VALENCIAN
            break;

        case Lang::Code::EN_UK:
        default:
            // ENGLISH (i fallback per a llengües no reconegudes)
            menu_.options->setItemCaption(i, Lang::get()->getText(8) + ": " + Lang::get()->getText(26));
            break;
    }

    i++;
    // DISPLAY MODE
    menu_.options->setItemCaption(i, Lang::get()->getText(58));

    i++;
    // DISPLAY MODE - OPTIONS
    menu_.options->setItemCaption(i, Options::video.fullscreen ? Lang::get()->getText(5) : Lang::get()->getText(4));

    i++;
    // WINDOW SIZE
    menu_.options->setItemCaption(i, Lang::get()->getText(7) + " x" + std::to_string(Options::window.zoom));  // WINDOW SIZE

    i++;
    // SCALE MODE
    if (Options::video.scale_mode == SDL_SCALEMODE_LINEAR) {
        menu_.options->setItemCaption(i, Lang::get()->getText(60) + ": " + Lang::get()->getText(71));  // BILINEAL
    } else {
        menu_.options->setItemCaption(i, Lang::get()->getText(60) + ": " + Lang::get()->getText(72));  // LINEAL
    }

    i++;
    // VSYNC
    if (Options::video.vsync) {
        menu_.options->setItemCaption(i, Lang::get()->getText(61) + ": " + Lang::get()->getText(73));  // ON
    } else {
        menu_.options->setItemCaption(i, Lang::get()->getText(61) + ": " + Lang::get()->getText(74));  // OFF
    }

    i++;
    // HOW TO PLAY
    menu_.options->setItemCaption(i, Lang::get()->getText(2));

    i++;
    // ACCEPT
    menu_.options->setItemCaption(i, Lang::get()->getText(9));  // ACCEPT

    i++;
    // CANCEL
    menu_.options->setItemCaption(i, Lang::get()->getText(10));  // CANCEL

    // Recoloca el menu de opciones
    menu_.options->centerMenuOnX(GAMECANVAS_CENTER_X);
    menu_.options->centerMenuOnY(GAMECANVAS_CENTER_Y);
    menu_.options->centerMenuElementsOnX();

// Establece las etiquetas del menu de titulo
#ifdef GAME_CONSOLE
    menu_.title->setItemCaption(0, Lang::get()->getText(0));  // PLAY
#else
    menu_.title->setItemCaption(0, Lang::get()->getText(51));  // 1 PLAYER
#endif
    menu_.title->setItemCaption(1, Lang::get()->getText(52));  // 2 PLAYERS
    menu_.title->setItemCaption(2, Lang::get()->getText(1));   // OPTIONS
    menu_.title->setItemCaption(3, Lang::get()->getText(3));   // QUIT
#ifdef __EMSCRIPTEN__
    menu_.title->setVisible(3, false);
    menu_.title->setSelectable(3, false);
#endif

    // Recoloca el menu de titulo
    menu_.title->centerMenuOnX(GAMECANVAS_CENTER_X);
    menu_.title->centerMenuElementsOnX();

    // Establece las etiquetas del menu de seleccion de jugador
    menu_.player_select->setItemCaption(0, Lang::get()->getText(39));  // SELECT PLAYER
    menu_.player_select->setItemCaption(3, Lang::get()->getText(40));  // BACK

    // Recoloca el menu de selección de jugador
    menu_.player_select->centerMenuOnX(GAMECANVAS_CENTER_X);
    menu_.player_select->centerMenuElementsOnX();

#ifdef GAME_CONSOLE
    menu_.options->setGreyed(1, true);
    menu_.options->setSelectable(1, false);
    menu_.options->setGreyed(2, true);
    menu_.options->setSelectable(2, false);
    menu_.options->setGreyed(3, true);
    menu_.options->setSelectable(3, false);
    menu_.options->setGreyed(4, true);
    menu_.options->setSelectable(4, false);
#endif
}

// Aplica las opciones de menu seleccionadas
void Title::applyOptions() {
    Screen::get()->setVideoMode(Options::video.fullscreen);
    Screen::get()->setWindowZoom(Options::window.zoom);
    Screen::get()->setVSync(Options::video.vsync);

    Lang::get()->setLang(Options::settings.language);

    updateMenuLabels();
    createTiledBackground();
}

// Ejecuta un frame
void Title::iterate() {
    // Si las instrucciones están activas, delega el frame
    if (instructions_active_) {
        instructions_->update();
        instructions_->render();

        if (instructions_->hasFinished()) {
            bool was_quit = instructions_->isQuitRequested();
            delete instructions_;
            instructions_ = nullptr;
            instructions_active_ = false;

            if (was_quit) {
                section_->name = SECTION_PROG_QUIT;
            } else if (instructions_mode_ == Instructions::Mode::AUTO) {
                section_->name = SECTION_PROG_TITLE;
                init();
                demo_ = true;
            } else {
                section_->name = SECTION_PROG_TITLE;
                section_->subsection = SUBSECTION_TITLE_3;
            }
        }
        return;
    }

    // Si el juego demo está activo, delega el frame
    if (demo_game_active_) {
        // El demo Game necesita section->name == SECTION_PROG_GAME para funcionar
        section_->name = SECTION_PROG_GAME;
        demo_game_->iterate();

        if (demo_game_->hasFinished()) {
            // cppcheck-suppress knownConditionTrueFalse
            const bool WAS_QUIT = (section_->name == SECTION_PROG_QUIT);
            delete demo_game_;
            demo_game_ = nullptr;
            demo_game_active_ = false;

            // cppcheck-suppress knownConditionTrueFalse
            if (WAS_QUIT) {
                section_->name = SECTION_PROG_QUIT;
            } else if (demo_then_instructions_) {
                section_->name = SECTION_PROG_TITLE;
                section_->subsection = SUBSECTION_TITLE_3;
                demo_then_instructions_ = false;
                runInstructions(Instructions::Mode::AUTO);
            } else {
                section_->name = SECTION_PROG_TITLE;
                section_->subsection = SUBSECTION_TITLE_1;
            }
        } else {
            // Restaura section para que Director no transicione fuera de Title
            section_->name = SECTION_PROG_TITLE;
        }
        return;
    }

    // Ejecución normal del título
    update();
    render();
}

// Procesa un evento individual
void Title::handleEvent(const SDL_Event *event) {
    // Si hay un sub-estado activo, delega el evento
    if (instructions_active_ && (instructions_ != nullptr)) {
        // SDL_EVENT_QUIT ya lo maneja Director
        return;
    }

    if (demo_game_active_ && (demo_game_ != nullptr)) {
        demo_game_->handleEvent(event);
        return;
    }

    // SDL_EVENT_QUIT ya lo maneja Director

    if (event->type == SDL_EVENT_RENDER_DEVICE_RESET || event->type == SDL_EVENT_RENDER_TARGETS_RESET) {
        reLoadTextures();
    }

    if (section_->subsection == SUBSECTION_TITLE_3) {
        if ((event->type == SDL_EVENT_KEY_UP) || (event->type == SDL_EVENT_JOYSTICK_BUTTON_UP)) {
            menu_visible_ = true;
            counter_ = COUNTER;
        }
    }
}

// Bucle para el titulo del juego (compatibilidad)
void Title::run() {
    while (section_->name == SECTION_PROG_TITLE || instructions_active_ || demo_game_active_) {
        iterate();
    }
}

// Inicia la parte donde se muestran las instrucciones
void Title::runInstructions(Instructions::Mode mode) {
    instructions_ = new Instructions(renderer_, section_);
    instructions_->start(mode);
    instructions_active_ = true;
    instructions_mode_ = mode;
}

// Inicia el juego en modo demo
void Title::runDemoGame() {
    // Temporalmente ponemos section para que el constructor de Game funcione
    section_->name = SECTION_PROG_GAME;
    section_->subsection = SUBSECTION_GAME_PLAY_1P;
    demo_game_ = new Game(1, 0, renderer_, true, section_);
    demo_game_active_ = true;
    // Restauramos section para que Director no transicione fuera de Title
    section_->name = SECTION_PROG_TITLE;
}

// Modifica las opciones para los controles de los jugadores
auto Title::updatePlayerInputs(int num_player) -> bool {
    const int NUM_DEVICES = available_input_devices_.size();

    if (!Input::get()->gameControllerFound()) {  // Si no hay mandos se deja todo de manera prefijada
        device_index_[0] = 0;
        device_index_[1] = 0;

        Options::inputs[0].id = -1;
        Options::inputs[0].name = "KEYBOARD";
        Options::inputs[0].device_type = INPUT_USE_KEYBOARD;

        Options::inputs[1].id = 0;
        Options::inputs[1].name = "GAME CONTROLLER";
        Options::inputs[1].device_type = INPUT_USE_GAMECONTROLLER;

        return true;
    }  // Si hay mas de un dispositivo, se recorre el vector
    if (Options::settings.console) {
        std::cout << "numplayer:" << num_player << '\n';
        std::cout << "deviceindex:" << device_index_[num_player] << '\n';
    }

    // Incrementa el indice
    if (device_index_[num_player] < NUM_DEVICES - 1) {
        device_index_[num_player]++;
    } else {
        device_index_[num_player] = 0;
    }
    if (Options::settings.console) {
        std::cout << "deviceindex:" << device_index_[num_player] << '\n';
    }

    // Si coincide con el del otro jugador, se lo intercambian
    if (device_index_[0] == device_index_[1]) {
        const int THE_OTHER_PLAYER = (num_player + 1) % 2;
        device_index_[THE_OTHER_PLAYER]--;
        if (device_index_[THE_OTHER_PLAYER] < 0) {
            device_index_[THE_OTHER_PLAYER] = NUM_DEVICES - 1;
        }
    }

    // Copia el dispositivo marcado por el indice a la variable de opciones de cada jugador
    Options::inputs[0] = available_input_devices_[device_index_[0]];
    Options::inputs[1] = available_input_devices_[device_index_[1]];

    return true;
}

// Crea el mosaico de fondo del titulo
void Title::createTiledBackground() {
    // Crea la textura para el mosaico de fondo
    background_ = SDL_CreateTexture(renderer_, SDL_PIXELFORMAT_RGBA8888, SDL_TEXTUREACCESS_TARGET, GAMECANVAS_WIDTH * 2, GAMECANVAS_HEIGHT * 2);
    if (background_ != nullptr) {
        SDL_SetTextureScaleMode(background_, Texture::current_scale_mode);
    }
    if (background_ == nullptr) {
        if (Options::settings.console) {
            std::cout << "TitleSurface could not be created!\nSDL Error: " << SDL_GetError() << '\n';
        }
    }

    // Crea los objetos para pintar en la textura de fondo
    auto *bg_tile_texture = new Texture(renderer_, Asset::get()->get("title_bg_tile.png"));
    auto *tile = new Sprite({0, 0, 64, 64}, bg_tile_texture, renderer_);

    // Prepara para dibujar sobre la textura
    SDL_SetRenderTarget(renderer_, background_);
    SDL_SetRenderDrawColor(renderer_, 0x43, 0x43, 0x4F, 0xFF);
    SDL_RenderClear(renderer_);

    // Rellena la textura con el tile
    tile->setSpriteClip(0, 0, 64, 64);
    for (int i = 0; i < 8; ++i) {
        for (int j = 0; j < 6; ++j) {
            tile->setPosX(i * 64);
            tile->setPosY(j * 64);
            tile->render();
        }
    }

    // Vuelve a colocar el renderizador apuntando a la pantalla
    SDL_SetRenderTarget(renderer_, nullptr);

    // Libera la memoria utilizada por los objetos
    bg_tile_texture->unload();
    delete bg_tile_texture;
    delete tile;
}

// Comprueba cuantos mandos hay conectados para gestionar el menu de opciones.
// El estado de Input lo mantiene al día Director via eventos SDL_EVENT_GAMEPAD_ADDED/REMOVED,
// así que aquí solo leemos la lista actual sin reescanear.
void Title::checkInputDevices() {
    if (Options::settings.console) {
        std::cout << "Filling devices for options menu_..." << '\n';
    }
    const int NUM_CONTROLLERS = Input::get()->getNumControllers();
    available_input_devices_.clear();
    InputDevice temp;

    // Añade todos los mandos
    if (NUM_CONTROLLERS > 0) {
        for (int i = 0; i < NUM_CONTROLLERS; ++i) {
            temp.id = i;
            temp.name = Input::get()->getControllerName(i);
            temp.device_type = INPUT_USE_GAMECONTROLLER;
            available_input_devices_.push_back(temp);
            if (Options::settings.console) {
                std::cout << "Device " << (int)available_input_devices_.size() << " - " << temp.name.c_str() << '\n';
            }
        }
    }

    // Añade el teclado al final
    temp.id = -1;
    temp.name = "KEYBOARD";
    temp.device_type = INPUT_USE_KEYBOARD;
    available_input_devices_.push_back(temp);
    if (Options::settings.console) {
        std::cout << "Device " << (int)available_input_devices_.size() << " - " << temp.name.c_str() << '\n';
        std::cout << '\n';
    }
}

// Recarga las texturas
void Title::reLoadTextures() {
    dust_texture_->reLoad();
    coffee_texture_->reLoad();
    crisis_texture_->reLoad();
    gradient_texture_->reLoad();
    createTiledBackground();
}