#include "game/scenes/title.hpp"

#include <SDL3/SDL.h>

#include <algorithm>  // Para clamp

#include "core/audio/audio.hpp"               // Para Audio
#include "core/input/global_inputs.hpp"       // Para check
#include "core/input/input.hpp"               // Para Input, InputAction, Input::DO_NOT_ALLOW_REPEAT, REP...
#include "core/locale/locale.hpp"             // Para Locale
#include "core/rendering/screen.hpp"          // Para Screen
#include "core/rendering/sprite/sprite.hpp"   // Para SSprite
#include "core/rendering/surface.hpp"         // Para Surface
#include "core/rendering/text.hpp"            // Para Text, Text::CENTER_FLAG, Text::COLOR_FLAG
#include "core/resources/resource_cache.hpp"  // Para Resource
#include "core/resources/resource_list.hpp"   // Para Asset
#include "core/system/global_events.hpp"      // Para check
#include "game/gameplay/cheevos.hpp"          // Para Cheevos, Achievement
#include "game/options.hpp"                   // Para Options, options, SectionState, Section
#include "game/scene_manager.hpp"             // Para SceneManager
#include "game/ui/console.hpp"                // Para Console
#include "utils/defines.hpp"                  // Para PlayArea::CENTER_X, GameCanvas::WIDTH
#include "utils/utils.hpp"                    // Para stringToColor, PaletteColor, playMusic

// Constructor
Title::Title()
    : game_logo_surface_(Resource::Cache::get()->getSurface("title_logo.gif")),
      game_logo_sprite_(std::make_unique<Sprite>(game_logo_surface_, 29, 9, game_logo_surface_->getWidth(), game_logo_surface_->getHeight())),
      loading_screen_surface_(Resource::Cache::get()->getSurface("loading_screen_color.gif")),
      loading_screen_sprite_(std::make_unique<Sprite>(loading_screen_surface_, 0, 0, loading_screen_surface_->getWidth(), loading_screen_surface_->getHeight())),
      title_surface_(std::make_shared<Surface>(Options::game.width, Options::game.height)),
      delta_timer_(std::make_unique<DeltaTimer>()),
      marquee_text_(Resource::Cache::get()->getText("gauntlet")),
      menu_text_(Resource::Cache::get()->getText("gauntlet")) {
    // Inicializa arrays con valores por defecto
    temp_keys_.fill(SDL_SCANCODE_UNKNOWN);
    temp_buttons_.fill(-1);

    // Determina el estado inicial basado en opciones
    state_ = SceneManager::options == SceneManager::Options::TITLE_WITH_LOADING_SCREEN ? State::SHOW_LOADING_SCREEN : State::MAIN_MENU;

    // Establece SceneManager
    SceneManager::current = SceneManager::Scene::TITLE;
    SceneManager::options = SceneManager::Options::NONE;

    // Acciones iniciales
    initMarquee();                                                           // Inicializa la marquesina
    createCheevosTexture();                                                  // Crea y rellena la textura para mostrar los logros
    Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));  // Cambia el color del borde
    Audio::get()->playMusic("title.ogg");                                    // Inicia la musica
}

// Destructor
Title::~Title() {  // NOLINT(modernize-use-equals-default)
    loading_screen_surface_->resetSubPalette();
    title_surface_->resetSubPalette();
}

// Inicializa la marquesina
void Title::initMarquee() {
    letters_.clear();
    long_text_ = Locale::get()->get("title.marquee");

    // Pre-calcular anchos de caracteres para eficiencia (iteración por codepoints UTF-8)
    size_t pos = 0;
    while (pos < long_text_.size()) {
        uint32_t cp = Text::nextCodepoint(long_text_, pos);
        Glyph l;
        l.codepoint = cp;
        l.clip = marquee_text_->getGlyphClip(cp);  // Pre-calcular clip rect (evita búsqueda por frame)
        l.x = MARQUEE_START_X;
        l.width = static_cast<float>(marquee_text_->glyphWidth(cp, 0));  // Pre-calcular ancho visual del glifo
        l.enabled = false;
        letters_.push_back(l);
    }

    if (!letters_.empty()) {
        letters_[0].enabled = true;
    }
    first_active_letter_ = 0;
    last_active_letter_ = 0;
}

// Despatx d'un event (SDL3 Callback API)
void Title::handleEvent(const SDL_Event& event) {
    GlobalEvents::handle(event);

    // Manejo especial para captura de botones de gamepad
    if (is_remapping_joystick_ && !remap_completed_ &&
        (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN || event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION)) {
        handleJoystickRemap(event);
        return;  // No procesar más este evento
    }

    // Qualsevol botó del comandament al menú principal inicia partida directament
    // (els bindings ja estan definits, no cal "pulsar 1" amb el teclat). El botó
    // BACK queda exclòs perquè es reserva per a EXIT — excepte a emscripten, on
    // no es pot sortir del joc i BACK pot actuar com a botó genèric d'inici.
    if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN &&
        state_ == State::MAIN_MENU &&
        !is_remapping_keyboard_ && !is_remapping_joystick_
#ifndef __EMSCRIPTEN__
        && event.gbutton.button != SDL_GAMEPAD_BUTTON_BACK
#endif
    ) {
        handleMainMenuKeyPress(SDLK_1);  // PLAY
        return;
    }

    if (event.type == SDL_EVENT_KEY_DOWN && !Console::get()->isActive()) {
        // Si estamos en modo remap de teclado, capturar tecla
        if (is_remapping_keyboard_ && !remap_completed_) {
            handleKeyboardRemap(event);
        }
        // Si estamos en el menú principal normal
        else if (state_ == State::MAIN_MENU && !is_remapping_keyboard_ && !is_remapping_joystick_) {
            handleMainMenuKeyPress(event.key.key);
        }
    }
}

// Maneja las teclas del menu principal
void Title::handleMainMenuKeyPress(SDL_Keycode key) {
    switch (key) {
        case SDLK_1:
            // PLAY
            exit_scene_ = SceneManager::Scene::GAME;
            transitionToState(State::FADE_MENU);
            Audio::get()->fadeOutMusic(1000);
            break;

        case SDLK_2:
            // REDEFINE KEYBOARD
            is_remapping_keyboard_ = true;
            is_remapping_joystick_ = false;
            remap_step_ = 0;
            remap_completed_ = false;
            remap_error_message_.clear();
            state_time_ = 0.0F;
            break;

        case SDLK_3:
            // REDEFINE JOYSTICK (siempre visible, pero solo funciona si hay gamepad)
            if (Input::get()->gameControllerFound()) {
                is_remapping_keyboard_ = false;
                is_remapping_joystick_ = true;
                remap_step_ = 0;
                remap_completed_ = false;
                remap_error_message_.clear();
                axis_cooldown_ = 0.0F;
                state_time_ = 0.0F;
            }
            // Si no hay gamepad, simplemente no hacer nada
            break;

        case SDLK_4:
            // PROJECTS
            transitionToState(State::CHEEVOS_MENU);
            break;

        default:
            break;
    }
}

// Comprueba las entradas
void Title::handleInput(float delta_time) {
    Input::get()->update();

    // Permitir cancelar remap con ESC/CANCEL
    if ((is_remapping_keyboard_ || is_remapping_joystick_) && !remap_completed_) {
        if (Input::get()->checkAction(InputAction::CANCEL, Input::DO_NOT_ALLOW_REPEAT)) {
            is_remapping_keyboard_ = false;
            is_remapping_joystick_ = false;
            remap_step_ = 0;
            remap_completed_ = false;
            remap_error_message_.clear();
        }
        // Durante el remap, no procesar otras entradas
        GlobalInputs::handle();
        return;
    }

    switch (state_) {
        case State::SHOW_LOADING_SCREEN:
            if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT)) {
                transitionToState(State::FADE_LOADING_SCREEN);
            }
            break;

        case State::CHEEVOS_MENU:
            if (Input::get()->checkAction(InputAction::ACCEPT, Input::DO_NOT_ALLOW_REPEAT) ||
                Input::get()->checkAction(InputAction::CANCEL, Input::DO_NOT_ALLOW_REPEAT)) {
                resetCheevosScroll();
                transitionToState(State::MAIN_MENU);
            }
            break;

        default:
            break;
    }

    GlobalInputs::handle();
}

// Actualiza la marquesina
void Title::updateMarquee(float delta_time) {
    const float DISPLACEMENT = MARQUEE_SPEED * delta_time;

    // Solo procesar letras en rango activo + 1 para poder activar la siguiente
    for (int i = first_active_letter_; i <= last_active_letter_ + 1 && i < (int)letters_.size(); ++i) {
        auto& letter = letters_[i];

        if (letter.enabled) {
            letter.x -= DISPLACEMENT;

            // Desactivar si sale de pantalla
            if (letter.x < MARQUEE_EXIT_X) {
                letter.enabled = false;
                if (i == first_active_letter_) {
                    first_active_letter_++;  // Avanzar inicio del rango
                }
            }
        } else if (i > 0 && letters_[i - 1].x < MARQUEE_START_X && letters_[i - 1].enabled) {
            // Activar siguiente letra usando ancho pre-calculado
            letter.enabled = true;
            letter.x = letters_[i - 1].x + letters_[i - 1].width + MARQUEE_LETTER_SPACING;
            last_active_letter_ = i;  // Expandir fin del rango
        }
    }

    // Comprueba si ha terminado la marquesina y la reinicia
    if (letters_[letters_.size() - 1].x < MARQUEE_EXIT_X) {
        initMarquee();
    }
}

// Dibuja la marquesina
void Title::renderMarquee() const {
    auto* sprite = marquee_text_->getSprite();
    sprite->setY(MARQUEE_Y);
    // Solo renderizar letras activas (optimización: usa cache y rangos)
    for (int i = first_active_letter_; i <= last_active_letter_ + 1 && i < (int)letters_.size(); ++i) {
        const auto& letter = letters_[i];
        if (letter.enabled && letter.clip.w > 0.0F) {
            sprite->setClip(letter.clip);
            sprite->setX(letter.x);
            sprite->render(1, static_cast<Uint8>(PaletteColor::MAGENTA));
        }
    }
}

// Actualiza las variables
void Title::update() {
    const float DELTA_TIME = delta_timer_->tick();

    handleInput(DELTA_TIME);  // Comprueba las entradas

    updateState(DELTA_TIME);  // Actualiza el estado actual

    Audio::update();                    // Actualiza el objeto Audio
    Screen::get()->update(DELTA_TIME);  // Actualiza el objeto Screen
}

// Actualiza el estado actual
void Title::updateState(float delta_time) {
    switch (state_) {
        case State::SHOW_LOADING_SCREEN:
            updateShowLoadingScreen(delta_time);
            break;

        case State::FADE_LOADING_SCREEN:
            updateFadeLoadingScreen(delta_time);
            break;

        case State::MAIN_MENU:
            updateMainMenu(delta_time);
            break;

        case State::CHEEVOS_MENU:
            updateCheevosMenu(delta_time);
            break;

        case State::FADE_MENU:
            updateFadeMenu(delta_time);
            break;

        case State::POST_FADE_MENU:
            updatePostFadeMenu(delta_time);
            break;

        default:
            break;
    }
}

// Transiciona a un nuevo estado
void Title::transitionToState(State new_state) {
    state_ = new_state;
    state_time_ = 0.0F;
    fade_accumulator_ = 0.0F;
}

// Actualiza el estado SHOW_LOADING_SCREEN
void Title::updateShowLoadingScreen(float delta_time) {
    state_time_ += delta_time;
    if (state_time_ >= SHOW_LOADING_DURATION) {
        transitionToState(State::FADE_LOADING_SCREEN);
    }
}

// Actualiza el estado FADE_LOADING_SCREEN
void Title::updateFadeLoadingScreen(float delta_time) {
    fade_accumulator_ += delta_time;
    if (fade_accumulator_ >= FADE_STEP_INTERVAL) {
        fade_accumulator_ = 0.0F;
        if (loading_screen_surface_->fadeSubPalette()) {
            transitionToState(State::MAIN_MENU);
        }
    }
}

// Actualiza el estado MAIN_MENU
void Title::updateMainMenu(float delta_time) {
    // Actualiza la marquesina
    updateMarquee(delta_time);

    // Si estamos en modo remap, manejar la lógica específica
    if (is_remapping_keyboard_ || is_remapping_joystick_) {
        // Decrementar cooldown de ejes si estamos capturando botones de joystick
        if (is_remapping_joystick_ && axis_cooldown_ > 0.0F) {
            axis_cooldown_ -= delta_time;
            axis_cooldown_ = std::max(axis_cooldown_, 0.0F);
        }

        // Si el remap está completado, esperar antes de guardar
        if (remap_completed_) {
            state_time_ += delta_time;
            if (state_time_ >= KEYBOARD_REMAP_DISPLAY_DELAY) {
                if (is_remapping_keyboard_) {
                    applyKeyboardRemap();
                } else if (is_remapping_joystick_) {
                    applyJoystickRemap();
                }
                // Resetear estado de remap
                is_remapping_keyboard_ = false;
                is_remapping_joystick_ = false;
                remap_completed_ = false;
                state_time_ = 0.0F;
            }
        }
    } else {
        // Incrementa el temporizador solo en el menú principal normal
        state_time_ += delta_time;

        // Si el tiempo alcanza el timeout, va a créditos con fade
        if (state_time_ >= MAIN_MENU_IDLE_TIMEOUT) {
            exit_scene_ = SceneManager::Scene::CREDITS;
            transitionToState(State::FADE_MENU);
        }
    }
}

// Actualiza el estado CHEEVOS_MENU
void Title::updateCheevosMenu(float delta_time) {
    // Actualiza la marquesina (sigue visible en fondo)
    updateMarquee(delta_time);

    // Determina la velocidad objetivo basada en el input
    float target_velocity = 0.0F;
    if (Input::get()->checkAction(InputAction::RIGHT, Input::ALLOW_REPEAT)) {
        target_velocity = CHEEVOS_SCROLL_MAX_SPEED;  // Scroll hacia abajo
    } else if (Input::get()->checkAction(InputAction::LEFT, Input::ALLOW_REPEAT)) {
        target_velocity = -CHEEVOS_SCROLL_MAX_SPEED;  // Scroll hacia arriba
    }

    // Interpola suavemente la velocidad actual hacia la velocidad objetivo
    if (target_velocity != 0.0F) {
        // Acelerando hacia la velocidad objetivo
        const float ACCELERATION_STEP = CHEEVOS_SCROLL_ACCELERATION * delta_time;
        if (cheevos_scroll_velocity_ < target_velocity) {
            cheevos_scroll_velocity_ = std::min(cheevos_scroll_velocity_ + ACCELERATION_STEP, target_velocity);
        } else if (cheevos_scroll_velocity_ > target_velocity) {
            cheevos_scroll_velocity_ = std::max(cheevos_scroll_velocity_ - ACCELERATION_STEP, target_velocity);
        }
    } else {
        // Desacelerando hacia 0
        const float DECELERATION_STEP = CHEEVOS_SCROLL_DECELERATION * delta_time;
        if (cheevos_scroll_velocity_ > 0.0F) {
            cheevos_scroll_velocity_ = std::max(cheevos_scroll_velocity_ - DECELERATION_STEP, 0.0F);
        } else if (cheevos_scroll_velocity_ < 0.0F) {
            cheevos_scroll_velocity_ = std::min(cheevos_scroll_velocity_ + DECELERATION_STEP, 0.0F);
        }
    }

    // Aplica la velocidad actual al scroll position
    if (cheevos_scroll_velocity_ != 0.0F) {
        cheevos_surface_view_.y += cheevos_scroll_velocity_ * delta_time;

        // Ajusta los límites
        const float BOTTOM = cheevos_surface_->getHeight() - cheevos_surface_view_.h;
        cheevos_surface_view_.y = std::clamp(cheevos_surface_view_.y, 0.0F, BOTTOM);

        cheevos_sprite_->setClip(cheevos_surface_view_);
    }

    // No incrementar state_time_ (no timeout en este estado)
}

// Actualiza el estado FADE_MENU
void Title::updateFadeMenu(float delta_time) {
    fade_accumulator_ += delta_time;
    if (fade_accumulator_ >= FADE_STEP_INTERVAL) {
        fade_accumulator_ = 0.0F;
        if (title_surface_->fadeSubPalette()) {
            transitionToState(State::POST_FADE_MENU);
        }
    }
    // Actualiza la marquesina (sigue visible en fondo)
    updateMarquee(delta_time);
}

// Actualiza el estado POST_FADE_MENU
void Title::updatePostFadeMenu(float delta_time) {
    state_time_ += delta_time;
    if (state_time_ >= POST_FADE_DELAY) {
        SceneManager::current = exit_scene_;
        SceneManager::options = SceneManager::Options::NONE;
    }
}

// Dibuja en pantalla
void Title::render() {
    // Rellena la surface
    fillTitleSurface();

    // Prepara para empezar a dibujar en la textura de juego
    Screen::get()->start();
    Screen::get()->clearSurface(static_cast<Uint8>(PaletteColor::BLACK));

    // Dibuja en pantalla la surface con la composicion
    title_surface_->render();

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

// Un frame de l'escena (SDL3 Callback API)
void Title::iterate() {
    update();
    render();
}

// Crea y rellena la textura para mostrar los logros
void Title::createCheevosTexture() {  // NOLINT(readability-convert-member-functions-to-static)
    // Define la zona central del menu (entre el logo y la marquesina)
    constexpr int MENU_ZONE_Y = 73;        // Top of menu zone
    constexpr int MENU_ZONE_HEIGHT = 102;  // Height of menu zone

    // Crea la textura con el listado de logros
    const auto CHEEVOS_LIST = Cheevos::get()->list();
    const auto TEXT = Resource::Cache::get()->getText("subatomic");
    constexpr int CHEEVOS_TEXTURE_WIDTH = 200;
    constexpr int CHEEVOS_TEXTURE_VIEW_HEIGHT = MENU_ZONE_HEIGHT;
    constexpr int CHEEVOS_PADDING = 10;
    const int CHEEVO_HEIGHT = CHEEVOS_PADDING + (TEXT->getCharacterSize() * 2) + 1;
    const int CHEEVOS_TEXTURE_HEIGHT = (CHEEVO_HEIGHT * CHEEVOS_LIST.size()) + 2 + TEXT->getCharacterSize() + 8;
    cheevos_surface_ = std::make_shared<Surface>(CHEEVOS_TEXTURE_WIDTH, CHEEVOS_TEXTURE_HEIGHT);

    // Prepara para dibujar sobre la textura
    auto previuos_renderer = Screen::get()->getRendererSurface();
    Screen::get()->setRendererSurface(cheevos_surface_);

    // Rellena la textura con color sólido
    const auto CHEEVOS_BG_COLOR = static_cast<Uint8>(PaletteColor::BLACK);
    cheevos_surface_->clear(CHEEVOS_BG_COLOR);

    // Escribe la lista de logros en la textura
    const std::string CHEEVOS_OWNER = Locale::get()->get("title.projects");  // NOLINT(readability-static-accessed-through-instance)
    const std::string CHEEVOS_LIST_CAPTION = CHEEVOS_OWNER + " (" + std::to_string(Cheevos::get()->getTotalUnlockedAchievements()) + " / " + std::to_string(Cheevos::get()->size()) + ")";
    int pos = 2;
    TEXT->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, cheevos_surface_->getWidth() / 2, pos, CHEEVOS_LIST_CAPTION, 1, stringToColor("bright_green"));
    pos += TEXT->getCharacterSize();
    const Uint8 CHEEVO_LOCKED_COLOR = stringToColor("white");
    const Uint8 CHEEVO_UNLOCKED_COLOR = stringToColor("bright_green");
    constexpr int LINE_X1 = (CHEEVOS_TEXTURE_WIDTH / 7) * 3;
    constexpr int LINE_X2 = LINE_X1 + ((CHEEVOS_TEXTURE_WIDTH / 7) * 1);

    for (const auto& cheevo : CHEEVOS_LIST) {
        const Uint8 CHEEVO_COLOR = cheevo.completed ? CHEEVO_UNLOCKED_COLOR : CHEEVO_LOCKED_COLOR;
        pos += CHEEVOS_PADDING;
        constexpr int HALF = CHEEVOS_PADDING / 2;
        cheevos_surface_->drawLine(LINE_X1, pos - HALF - 1, LINE_X2, pos - HALF - 1, CHEEVO_COLOR);
        TEXT->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.caption, 1, CHEEVO_COLOR);
        pos += TEXT->getCharacterSize() + 1;
        TEXT->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, CHEEVOS_TEXTURE_WIDTH / 2, pos, cheevo.description, 1, CHEEVO_COLOR);
        pos += TEXT->getCharacterSize();
    }

    // Restablece el RenderSurface
    Screen::get()->setRendererSurface(previuos_renderer);

    // Crea el sprite para el listado de logros (usa la zona del menu)
    cheevos_sprite_ = std::make_unique<Sprite>(cheevos_surface_, (GameCanvas::WIDTH - cheevos_surface_->getWidth()) / 2, MENU_ZONE_Y, cheevos_surface_->getWidth(), cheevos_surface_->getHeight());
    cheevos_surface_view_ = {.x = 0, .y = 0, .w = static_cast<float>(cheevos_surface_->getWidth()), .h = CHEEVOS_TEXTURE_VIEW_HEIGHT};
    cheevos_sprite_->setClip(cheevos_surface_view_);
}

// Resetea el scroll de la lista de logros
void Title::resetCheevosScroll() {
    cheevos_surface_view_.y = 0;
    cheevos_scroll_velocity_ = 0.0F;
    cheevos_sprite_->setClip(cheevos_surface_view_);
}

// Dibuja el logo con el titulo del juego
void Title::renderGameLogo() {
    game_logo_sprite_->render();
}

// Dibuja el menu principal
void Title::renderMainMenu() {
    // Si estamos en modo remap, mostrar la pantalla correspondiente
    if (is_remapping_keyboard_) {
        renderKeyboardRemap();
        return;
    }
    if (is_remapping_joystick_) {
        renderJoystickRemap();
        return;
    }

    // Zona central del menu (debe coincidir con la textura de cheevos)
    constexpr int MENU_ZONE_Y = 73;
    constexpr int MENU_ZONE_HEIGHT = 102;

    // Menú principal normal con 4 opciones centradas verticalmente en la zona
    const Uint8 COLOR = stringToColor("green");
    const int TEXT_SIZE = menu_text_->getCharacterSize();
    const int MENU_CENTER_Y = MENU_ZONE_Y + (MENU_ZONE_HEIGHT / 2);
    const int SPACING = 2 * TEXT_SIZE;  // Espaciado entre opciones

    // Calcula posiciones centradas verticalmente (4 items con espaciado)
    const int TOTAL_HEIGHT = 3 * SPACING;  // 3 espacios entre 4 items
    const int START_Y = MENU_CENTER_Y - (TOTAL_HEIGHT / 2);

    const auto* loc = Locale::get();
    menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.menu.play"), 1, COLOR);
    menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + SPACING, loc->get("title.menu.keyboard"), 1, COLOR);
    menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (2 * SPACING), loc->get("title.menu.joystick"), 1, COLOR);
    menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y + (3 * SPACING), loc->get("title.menu.projects"), 1, COLOR);
}

// Dibuja el menu de logros
void Title::renderCheevosMenu() {
    cheevos_sprite_->render();
}

// Dibuja los elementos en la surface
void Title::fillTitleSurface() {
    // Renderiza sobre la textura
    auto previuos_renderer = Screen::get()->getRendererSurface();
    Screen::get()->setRendererSurface(title_surface_);

    // Rellena la textura de color
    title_surface_->clear(static_cast<Uint8>(PaletteColor::BLACK));

    switch (state_) {
        case State::MAIN_MENU:
        case State::FADE_MENU:
            renderGameLogo();
            renderMainMenu();
            renderMarquee();
            break;

        case State::CHEEVOS_MENU:
            renderGameLogo();
            renderCheevosMenu();
            renderMarquee();
            break;

        case State::SHOW_LOADING_SCREEN:
        case State::FADE_LOADING_SCREEN:
            loading_screen_sprite_->render();
            renderGameLogo();
            break;

        default:
            break;
    }

    // Deja el renderizador como estaba
    Screen::get()->setRendererSurface(previuos_renderer);
}

// Maneja la captura de teclas para redefinir el teclado
void Title::handleKeyboardRemap(const SDL_Event& event) {
    SDL_Scancode scancode = event.key.scancode;

    // Valida la tecla
    if (!isKeyValid(scancode)) {
        remap_error_message_ = Locale::get()->get("title.keys.invalid");
        return;
    }

    // Verifica duplicados
    if (isKeyDuplicate(scancode, remap_step_)) {
        remap_error_message_ = Locale::get()->get("title.keys.already_used");
        return;
    }

    // Tecla valida, guardar
    temp_keys_[remap_step_] = scancode;
    remap_error_message_.clear();
    remap_step_++;

    // Si completamos los 3 pasos, mostrar resultado y esperar
    if (remap_step_ >= 3) {
        remap_completed_ = true;
        state_time_ = 0.0F;  // Resetear el timer para el delay de 1 segundo
    }
}

// Valida si una tecla es permitida
auto Title::isKeyValid(SDL_Scancode scancode) -> bool {
    // Prohibir ESC (reservado para cancelar)
    if (scancode == SDL_SCANCODE_ESCAPE) {
        return false;
    }

    // Prohibir teclas F1-F12 (reservadas para funciones del sistema)
    if (scancode >= SDL_SCANCODE_F1 && scancode <= SDL_SCANCODE_F12) {
        return false;
    }

    // Prohibir Enter/Return (reservado para confirmaciones)
    if (scancode == SDL_SCANCODE_RETURN || scancode == SDL_SCANCODE_RETURN2) {
        return false;
    }

    return true;
}

// Verifica si una tecla ya fue usada en pasos anteriores
auto Title::isKeyDuplicate(SDL_Scancode scancode, int current_step) -> bool {  // NOLINT(readability-convert-member-functions-to-static)
    for (int i = 0; i < current_step; i++) {
        if (temp_keys_[i] == scancode) {
            return true;
        }
    }
    return false;
}

// Retorna el nombre de la accion para el paso actual
// Aplica y guarda las teclas redefinidas
void Title::applyKeyboardRemap() {  // NOLINT(readability-convert-member-functions-to-static)
    // Guardar las nuevas teclas en Options::controls
    Options::keyboard_controls.key_left = temp_keys_[0];
    Options::keyboard_controls.key_right = temp_keys_[1];
    Options::keyboard_controls.key_jump = temp_keys_[2];

    // Aplicar los bindings al sistema de Input
    Input::get()->applyKeyboardBindingsFromOptions();

    // Guardar a archivo de configuracion
    Options::saveToFile();
}

// Dibuja la pantalla de redefinir teclado
void Title::renderKeyboardRemap() const {
    // Zona central del menu (debe coincidir con la textura de cheevos)
    constexpr int MENU_ZONE_Y = 73;
    constexpr int MENU_ZONE_HEIGHT = 102;

    const Uint8 COLOR = stringToColor("green");
    const Uint8 ERROR_COLOR = stringToColor("red");
    const int TEXT_SIZE = menu_text_->getCharacterSize();
    const int MENU_CENTER_Y = MENU_ZONE_Y + (MENU_ZONE_HEIGHT / 2);

    // Calcula posiciones centradas verticalmente
    // Layout: Mensaje principal, espacio, 3 teclas (LEFT/RIGHT/JUMP), espacio, mensaje de error
    const int LINE_SPACING = TEXT_SIZE;
    const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE);  // Centrado aproximado

    // Mensaje principal: "PRESS KEY FOR [ACTION]" o "KEYS DEFINED" si completado
    const auto* loc = Locale::get();
    if (remap_step_ >= 3) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.keys.defined"), 1, COLOR);
    } else {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.keys.prompt" + std::to_string(remap_step_)), 1, COLOR);
    }

    // Mostrar teclas ya capturadas (con espaciado de 2 líneas desde el mensaje principal)
    const int KEYS_START_Y = START_Y + (2 * LINE_SPACING);
    if (remap_step_ > 0) {
        const std::string LEFT_KEY = SDL_GetScancodeName(temp_keys_[0]);
        const std::string LEFT_MSG = loc->get("title.keys.label0") + LEFT_KEY;  // NOLINT(readability-static-accessed-through-instance)
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y, LEFT_MSG, 1, COLOR);
    }
    if (remap_step_ > 1) {
        const std::string RIGHT_KEY = SDL_GetScancodeName(temp_keys_[1]);
        const std::string RIGHT_MSG = loc->get("title.keys.label1") + RIGHT_KEY;  // NOLINT(readability-static-accessed-through-instance)
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + LINE_SPACING, RIGHT_MSG, 1, COLOR);
    }
    if (remap_step_ >= 3) {
        const std::string JUMP_KEY = SDL_GetScancodeName(temp_keys_[2]);
        const std::string JUMP_MSG = loc->get("title.keys.label2") + JUMP_KEY;  // NOLINT(readability-static-accessed-through-instance)
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + (2 * LINE_SPACING), JUMP_MSG, 1, COLOR);
    }

    // Mensaje de error si existe (4 líneas después del inicio de las teclas)
    if (!remap_error_message_.empty()) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, KEYS_START_Y + (4 * LINE_SPACING), remap_error_message_, 1, ERROR_COLOR);
    }
}

// Dibuja la pantalla de redefinir joystick
void Title::renderJoystickRemap() const {
    // Zona central del menu (debe coincidir con la textura de cheevos)
    constexpr int MENU_ZONE_Y = 73;
    constexpr int MENU_ZONE_HEIGHT = 102;

    const Uint8 COLOR = stringToColor("green");
    const Uint8 ERROR_COLOR = stringToColor("red");
    const int TEXT_SIZE = menu_text_->getCharacterSize();
    const int MENU_CENTER_Y = MENU_ZONE_Y + (MENU_ZONE_HEIGHT / 2);

    // Calcula posiciones centradas verticalmente
    // Layout: Mensaje principal, espacio, 3 botones (LEFT/RIGHT/JUMP), espacio, mensaje de error
    const int LINE_SPACING = TEXT_SIZE;
    const int START_Y = MENU_CENTER_Y - (2 * TEXT_SIZE);  // Centrado aproximado

    // Mensaje principal: "PRESS BUTTON FOR [ACTION]" o "BUTTONS DEFINED" si completado
    const auto* loc = Locale::get();
    if (remap_step_ >= 3) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.buttons.defined"), 1, COLOR);
    } else {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, START_Y, loc->get("title.buttons.prompt" + std::to_string(remap_step_)), 1, COLOR);
    }

    // Mostrar botones ya capturados (con espaciado de 2 líneas desde el mensaje principal)
    const int BUTTONS_START_Y = START_Y + (2 * LINE_SPACING);
    if (remap_step_ > 0) {
        const std::string LEFT_BTN = getButtonName(temp_buttons_[0]);
        const std::string LEFT_MSG = loc->get("title.keys.label0") + LEFT_BTN;  // NOLINT(readability-static-accessed-through-instance)
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y, LEFT_MSG, 1, COLOR);
    }
    if (remap_step_ > 1) {
        const std::string RIGHT_BTN = getButtonName(temp_buttons_[1]);
        const std::string RIGHT_MSG = loc->get("title.keys.label1") + RIGHT_BTN;  // NOLINT(readability-static-accessed-through-instance)
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + LINE_SPACING, RIGHT_MSG, 1, COLOR);
    }
    if (remap_step_ >= 3) {
        const std::string JUMP_BTN = getButtonName(temp_buttons_[2]);
        const std::string JUMP_MSG = loc->get("title.keys.label2") + JUMP_BTN;  // NOLINT(readability-static-accessed-through-instance)
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + (2 * LINE_SPACING), JUMP_MSG, 1, COLOR);
    }

    // Mensaje de error si existe (4 líneas después del inicio de los botones)
    if (!remap_error_message_.empty()) {
        menu_text_->writeDX(Text::CENTER_FLAG | Text::COLOR_FLAG, PlayArea::CENTER_X, BUTTONS_START_Y + (4 * LINE_SPACING), remap_error_message_, 1, ERROR_COLOR);
    }
}

// Maneja la captura de botones del gamepad para redefinir
void Title::handleJoystickRemap(const SDL_Event& event) {
    int captured_button = -1;

    // Capturar botones del gamepad
    if (event.type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {
        captured_button = static_cast<int>(event.gbutton.button);
    }
    // Capturar triggers y ejes analógicos
    else if (event.type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {
        // Si el cooldown está activo, ignorar eventos de ejes (evita múltiples capturas)
        if (axis_cooldown_ > 0.0F) {
            return;
        }

        constexpr Sint16 TRIGGER_THRESHOLD = 20000;
        constexpr Sint16 AXIS_THRESHOLD = 20000;

        // Capturar triggers como botones (usando valores especiales 100/101)
        if (event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFT_TRIGGER && event.gaxis.value > TRIGGER_THRESHOLD) {
            captured_button = Input::TRIGGER_L2_AS_BUTTON;  // 100
            axis_cooldown_ = 0.5F;                          // Cooldown de medio segundo
        } else if (event.gaxis.axis == SDL_GAMEPAD_AXIS_RIGHT_TRIGGER && event.gaxis.value > TRIGGER_THRESHOLD) {
            captured_button = Input::TRIGGER_R2_AS_BUTTON;  // 101
            axis_cooldown_ = 0.5F;
        }
        // Capturar ejes del stick analógico (usando valores especiales 200+)
        else if (event.gaxis.axis == SDL_GAMEPAD_AXIS_LEFTX) {
            if (event.gaxis.value < -AXIS_THRESHOLD) {
                captured_button = 200;  // Left stick izquierda
                axis_cooldown_ = 0.5F;
            } else if (event.gaxis.value > AXIS_THRESHOLD) {
                captured_button = 201;  // Left stick derecha
                axis_cooldown_ = 0.5F;
            }
        }
    }

    // Si no se capturó ningún input válido, salir
    if (captured_button == -1) {
        return;
    }

    // Verifica duplicados
    if (isButtonDuplicate(captured_button, remap_step_)) {
        remap_error_message_ = Locale::get()->get("title.buttons.already_used");
        return;
    }

    // Botón válido, guardar
    temp_buttons_[remap_step_] = captured_button;
    remap_error_message_.clear();
    remap_step_++;

    // Si completamos los 3 pasos, mostrar resultado y esperar
    if (remap_step_ >= 3) {
        remap_completed_ = true;
        state_time_ = 0.0F;  // Resetear el timer para el delay
    }
}

// Valida si un botón está duplicado
auto Title::isButtonDuplicate(int button, int current_step) -> bool {  // NOLINT(readability-convert-member-functions-to-static)
    for (int i = 0; i < current_step; ++i) {
        if (temp_buttons_[i] == button) {
            return true;
        }
    }
    return false;
}

// Aplica y guarda los botones del gamepad redefinidos
void Title::applyJoystickRemap() {  // NOLINT(readability-convert-member-functions-to-static)
    // Guardar los nuevos botones en Options::gamepad_controls
    Options::gamepad_controls.button_left = temp_buttons_[0];
    Options::gamepad_controls.button_right = temp_buttons_[1];
    Options::gamepad_controls.button_jump = temp_buttons_[2];

    // Aplicar los bindings al sistema de Input
    Input::get()->applyGamepadBindingsFromOptions();

    // Guardar a archivo de configuracion
    Options::saveToFile();
}

// Retorna el nombre amigable del botón del gamepad
auto Title::getButtonName(int button) -> std::string {  // NOLINT(readability-convert-member-functions-to-static)
    // Triggers especiales
    if (button == Input::TRIGGER_L2_AS_BUTTON) {
        return "L2";
    }
    if (button == Input::TRIGGER_R2_AS_BUTTON) {
        return "R2";
    }

    // Ejes del stick analógico
    if (button == 200) {
        return "LEFT STICK LEFT";
    }
    if (button == 201) {
        return "LEFT STICK RIGHT";
    }

    // Botones estándar SDL
    const auto GAMEPAD_BUTTON = static_cast<SDL_GamepadButton>(button);
    const char* button_name = SDL_GetGamepadStringForButton(GAMEPAD_BUTTON);
    return (button_name != nullptr) ? std::string(button_name) : "UNKNOWN";
}