#include "game/scenes/logo.hpp"

#include <SDL3/SDL.h>

#include <algorithm>  // Para std::clamp

#include "core/input/global_inputs.hpp"       // Para check
#include "core/rendering/screen.hpp"          // Para Screen
#include "core/rendering/surface.hpp"         // Para Surface
#include "core/rendering/surface_sprite.hpp"  // Para SSprite
#include "core/resources/resource.hpp"        // Para Resource
#include "game/options.hpp"                   // Para Options, SectionState, options, Section
#include "game/scene_manager.hpp"             // Para SceneManager
#include "utils/delta_timer.hpp"              // Para DeltaTimer
#include "core/system/global_events.hpp"      // Para check
#include "utils/utils.hpp"                    // Para PaletteColor

// Constructor
Logo::Logo()
    : jailgames_surface_(Resource::get()->getSurface("jailgames.gif")),
      since_1998_surface_(Resource::get()->getSurface("since_1998.gif")),
      since_1998_sprite_(std::make_shared<SurfaceSprite>(since_1998_surface_, (256 - since_1998_surface_->getWidth()) / 2, 83 + jailgames_surface_->getHeight() + 5, since_1998_surface_->getWidth(), since_1998_surface_->getHeight())),
      delta_timer_(std::make_unique<DeltaTimer>()),
      state_(LogoState::INITIAL),
      state_time_(0.0F) {
    // Configura variables
    since_1998_sprite_->setClip(0, 0, since_1998_surface_->getWidth(), since_1998_surface_->getHeight());
    since_1998_color_ = static_cast<Uint8>(PaletteColor::BLACK);
    jailgames_color_ = static_cast<Uint8>(PaletteColor::BRIGHT_WHITE);

    // Inicializa variables
    SceneManager::current = SceneManager::Scene::LOGO;

    initSprites();  // Crea los sprites de cada linea
    initColors();   // Inicializa el vector de colores

    // Cambia el color del borde
    Screen::get()->setBorderColor(static_cast<Uint8>(PaletteColor::BLACK));
}

// Comprueba el manejador de eventos
void Logo::checkEvents() {
    SDL_Event event;
    while (SDL_PollEvent(&event)) {
        GlobalEvents::check(event);
    }
}

// Comprueba las entradas
void Logo::checkInput() {
    GlobalInputs::check();
}

// Gestiona el logo de JAILGAME (time-based)
void Logo::updateJAILGAMES(float delta_time) {
    // Solo actualizar durante el estado JAILGAMES_SLIDE_IN
    if (state_ != LogoState::JAILGAMES_SLIDE_IN) {
        return;
    }

    // Calcular el desplazamiento basado en velocidad y delta time
    const float DISPLACEMENT = JAILGAMES_SLIDE_SPEED * delta_time;

    // Actualizar cada línea del sprite JAILGAMES
    for (size_t i = 1; i < jailgames_sprite_.size(); ++i) {
        const int CURRENT_X = jailgames_sprite_[i]->getX();

        // Las líneas pares se mueven desde la derecha, las impares desde la izquierda
        if (i % 2 == 0) {
            // Mover hacia la izquierda
            if (CURRENT_X > JAILGAMES_DEST_X) {
                const int NEW_X = static_cast<int>(CURRENT_X - DISPLACEMENT);
                jailgames_sprite_[i]->setX(NEW_X < JAILGAMES_DEST_X ? JAILGAMES_DEST_X : NEW_X);
            }
        } else {
            // Mover hacia la derecha
            if (CURRENT_X < JAILGAMES_DEST_X) {
                const int NEW_X = static_cast<int>(CURRENT_X + DISPLACEMENT);
                jailgames_sprite_[i]->setX(NEW_X > JAILGAMES_DEST_X ? JAILGAMES_DEST_X : NEW_X);
            }
        }
    }
}

// Calcula el índice de color según el progreso (0.0-1.0)
auto Logo::getColorIndex(float progress) const -> int {
    // Asegurar que progress esté en el rango [0.0, 1.0]
    progress = std::clamp(progress, 0.0F, 1.0F);

    // Mapear el progreso al índice de color (0-7)
    const int MAX_INDEX = static_cast<int>(color_.size()) - 1;
    const int INDEX = static_cast<int>(progress * MAX_INDEX);

    return INDEX;
}

// Gestiona el color de las texturas
void Logo::updateTextureColors() {
    switch (state_) {
        case LogoState::SINCE_1998_FADE_IN: {
            // Fade-in de "Since 1998" de negro a blanco
            const float PROGRESS = state_time_ / SINCE_1998_FADE_DURATION;
            since_1998_color_ = color_[getColorIndex(PROGRESS)];
            break;
        }

        case LogoState::DISPLAY: {
            // Asegurar que ambos logos estén en blanco durante el display
            jailgames_color_ = color_.back();   // BRIGHT_WHITE
            since_1998_color_ = color_.back();  // BRIGHT_WHITE
            break;
        }

        case LogoState::FADE_OUT: {
            // Fade-out de ambos logos de blanco a negro
            const float PROGRESS = 1.0F - (state_time_ / FADE_OUT_DURATION);
            const int COLOR_INDEX = getColorIndex(PROGRESS);
            jailgames_color_ = color_[COLOR_INDEX];
            since_1998_color_ = color_[COLOR_INDEX];
            break;
        }

        default:
            // En otros estados, mantener los colores actuales
            break;
    }
}

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

// Actualiza el estado actual
void Logo::updateState(float delta_time) {
    state_time_ += delta_time;

    // Gestionar transiciones entre estados basándose en el tiempo
    switch (state_) {
        case LogoState::INITIAL:
            if (state_time_ >= INITIAL_DELAY) {
                transitionToState(LogoState::JAILGAMES_SLIDE_IN);
            }
            break;

        case LogoState::JAILGAMES_SLIDE_IN:
            if (state_time_ >= JAILGAMES_SLIDE_DURATION) {
                transitionToState(LogoState::SINCE_1998_FADE_IN);
            }
            break;

        case LogoState::SINCE_1998_FADE_IN:
            if (state_time_ >= SINCE_1998_FADE_DURATION) {
                transitionToState(LogoState::DISPLAY);
            }
            break;

        case LogoState::DISPLAY:
            if (state_time_ >= DISPLAY_DURATION) {
                transitionToState(LogoState::FADE_OUT);
            }
            break;

        case LogoState::FADE_OUT:
            if (state_time_ >= FADE_OUT_DURATION) {
                transitionToState(LogoState::END);
                endSection();
            }
            break;

        case LogoState::END:
            // Estado final, no hacer nada
            break;
    }
}

// Actualiza las variables
void Logo::update() {
    // Obtener delta time desde el último frame
    const float DELTA_TIME = delta_timer_->tick();

    checkInput();                      // Comprueba las entradas
    updateState(DELTA_TIME);           // Actualiza el estado y gestiona transiciones
    updateJAILGAMES(DELTA_TIME);       // Gestiona el logo de JAILGAME
    updateTextureColors();             // Gestiona el color de las texturas
    Screen::get()->update(DELTA_TIME); // Actualiza el objeto Screen
}

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

    // Dibuja los objetos
    for (const auto& sprite : jailgames_sprite_) {
        sprite->render(1, jailgames_color_);
    }
    since_1998_sprite_->render(1, since_1998_color_);

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

// Bucle para el logo del juego
void Logo::run() {
    while (SceneManager::current == SceneManager::Scene::LOGO) {
        update();
        checkEvents();
        render();
    }
}

// Termina la sección
void Logo::endSection() {
    switch (SceneManager::options) {
        case SceneManager::Options::LOGO_TO_TITLE:
            SceneManager::current = SceneManager::Scene::TITLE;
            break;

        case SceneManager::Options::LOGO_TO_LOADING_SCREEN:
            SceneManager::current = SceneManager::Scene::LOADING_SCREEN;
            break;

        default:
            // Ninguna acción por defecto
            break;
    }
}

// Inicializa el vector de colores
void Logo::initColors() {
    // Inicializa el vector de colores
    const std::vector<Uint8> COLORS = {
        static_cast<Uint8>(PaletteColor::BLACK),
        static_cast<Uint8>(PaletteColor::BLUE),
        static_cast<Uint8>(PaletteColor::RED),
        static_cast<Uint8>(PaletteColor::MAGENTA),
        static_cast<Uint8>(PaletteColor::GREEN),
        static_cast<Uint8>(PaletteColor::CYAN),
        static_cast<Uint8>(PaletteColor::YELLOW),
        static_cast<Uint8>(PaletteColor::BRIGHT_WHITE)};
    for (const auto& color : COLORS) {
        color_.push_back(color);
    }
}

// Crea los sprites de cada linea
void Logo::initSprites() {
    const float WIDTH = jailgames_surface_->getWidth();
    for (int i = 0; i < jailgames_surface_->getHeight(); ++i) {
        jailgames_sprite_.push_back(std::make_shared<SurfaceSprite>(jailgames_surface_, 0, i, jailgames_surface_->getWidth(), 1));
        jailgames_sprite_.back()->setClip(0, i, jailgames_surface_->getWidth(), 1);
        jailgames_sprite_.at(i)->setX((i % 2 == 0) ? (256 + (i * 3)) : (-WIDTH - (i * 3)));
        jailgames_sprite_.at(i)->setY(83 + i);
    }
}