#include "game/scenes/logo.hpp"

#include <SDL3/SDL.h>  // Para SDL_GetTicks, SDL_PollEvent, SDL_Event, SDL_FRect, Uint64

#include <cstddef>  // Para size_t
#include <string>   // Para basic_string
#include <utility>  // Para move

#include "core/audio/audio.hpp"              // Para Audio
#include "core/input/global_inputs.hpp"      // Para check
#include "core/input/input.hpp"              // Para Input
#include "core/rendering/screen.hpp"         // Para Screen
#include "core/rendering/sprite/sprite.hpp"  // Para Sprite
#include "core/rendering/texture.hpp"        // Para Texture
#include "core/resources/resource.hpp"       // Para Resource
#include "core/system/global_events.hpp"     // Para handle
#include "core/system/section.hpp"           // Para Name, name
#include "utils/color.hpp"                   // Para Color
#include "utils/param.hpp"                   // Para Param, ParamGame, param
#include "utils/utils.hpp"                   // Para Zone

// Constructor
Logo::Logo()
    : since_texture_(Resource::get()->getTexture("logo_since_1998.png")),
      since_sprite_(std::make_unique<Sprite>(since_texture_)),
      jail_texture_(Resource::get()->getTexture("logo_jailgames.png")) {
    // Inicializa variables
    Section::name = Section::Name::LOGO;
    dest_.x = param.game.game_area.center_x - (jail_texture_->getWidth() / 2);
    dest_.y = param.game.game_area.center_y - (jail_texture_->getHeight() / 2);
    since_sprite_->setPosition(SDL_FRect{
        .x = static_cast<float>((param.game.width - since_texture_->getWidth()) / 2),
        .y = static_cast<float>(SINCE_SPRITE_Y_OFFSET + jail_texture_->getHeight() + LOGO_SPACING),
        .w = static_cast<float>(since_texture_->getWidth()),
        .h = static_cast<float>(since_texture_->getHeight())});
    since_sprite_->setY(dest_.y + jail_texture_->getHeight() + LOGO_SPACING);
    since_sprite_->setSpriteClip(0, 0, since_texture_->getWidth(), since_texture_->getHeight());
    since_texture_->setColor(SPECTRUM_BLACK.r, SPECTRUM_BLACK.g, SPECTRUM_BLACK.b);

    // Crea los sprites de cada linea
    for (int i = 0; i < jail_texture_->getHeight(); ++i) {
        auto temp = std::make_unique<Sprite>(jail_texture_, 0, i, jail_texture_->getWidth(), SPRITE_LINE_HEIGHT);
        temp->setSpriteClip(0, i, jail_texture_->getWidth(), SPRITE_LINE_HEIGHT);
        const int POS_X = (i % 2 == 0) ? param.game.width + (i * LINE_OFFSET_FACTOR) : -jail_texture_->getWidth() - (i * LINE_OFFSET_FACTOR);
        temp->setX(POS_X);
        temp->setY(dest_.y + i);
        jail_sprite_.push_back(std::move(temp));
    }

    // Inicializa el timer de delta time para el primer frame del callback
    last_time_ = SDL_GetTicks();

    // Inicializa el vector de colores con la paleta ZX Spectrum
    color_.emplace_back(SPECTRUM_BLACK);
    color_.emplace_back(SPECTRUM_BLUE);
    color_.emplace_back(SPECTRUM_RED);
    color_.emplace_back(SPECTRUM_MAGENTA);
    color_.emplace_back(SPECTRUM_GREEN);
    color_.emplace_back(SPECTRUM_CYAN);
    color_.emplace_back(SPECTRUM_YELLOW);
    color_.emplace_back(SPECTRUM_WHITE);
}

// Destructor
Logo::~Logo() {
    jail_texture_->setColor(RESET_COLOR.r, RESET_COLOR.g, RESET_COLOR.b);
    since_texture_->setColor(RESET_COLOR.r, RESET_COLOR.g, RESET_COLOR.b);
    Audio::get()->stopAllSounds();
    Audio::get()->stopMusic();
}

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

// Comprueba las entradas
void Logo::checkInput() {
    Input::get()->update();
    GlobalInputs::check();
}

// Maneja la reproducción del sonido del logo
void Logo::handleSound() {
    if (!sound_triggered_ && elapsed_time_s_ >= SOUND_TRIGGER_TIME_S) {
        Audio::get()->playSound("logo.wav");
        sound_triggered_ = true;
    }
}

// Gestiona el logo de JAILGAMES
void Logo::updateJAILGAMES(float delta_time) {
    if (elapsed_time_s_ > SOUND_TRIGGER_TIME_S) {
        const float PIXELS_TO_MOVE = LOGO_SPEED_PX_PER_S * delta_time;

        for (size_t i = 0; i < jail_sprite_.size(); ++i) {
            if (jail_sprite_[i]->getX() != dest_.x) {
                if (i % 2 == 0) {
                    jail_sprite_[i]->incX(-PIXELS_TO_MOVE);
                    if (jail_sprite_[i]->getX() < dest_.x) {
                        jail_sprite_[i]->setX(dest_.x);
                    }
                } else {
                    jail_sprite_[i]->incX(PIXELS_TO_MOVE);
                    if (jail_sprite_[i]->getX() > dest_.x) {
                        jail_sprite_[i]->setX(dest_.x);
                    }
                }
            }
        }
    }

    // Comprueba si ha terminado el logo
    if (elapsed_time_s_ >= END_LOGO_TIME_S + POST_LOGO_DURATION_S) {
        Section::name = Section::Name::INTRO;
    }
}

// Gestiona el color de las texturas
void Logo::updateTextureColors(float delta_time) {
    // Manejo de 'sinceTexture'
    for (int i = 0; i <= MAX_SINCE_COLOR_INDEX; ++i) {
        const float TARGET_TIME = SHOW_SINCE_SPRITE_TIME_S + (COLOR_CHANGE_INTERVAL_S * i);
        if (elapsed_time_s_ >= TARGET_TIME && elapsed_time_s_ - delta_time < TARGET_TIME) {
            since_texture_->setColor(color_[i].r, color_[i].g, color_[i].b);
        }
    }

    // Manejo de 'jailTexture' y 'sinceTexture' en el fade
    for (int i = 0; i <= MAX_FADE_COLOR_INDEX; ++i) {
        const float TARGET_TIME = INIT_FADE_TIME_S + (COLOR_CHANGE_INTERVAL_S * i);
        if (elapsed_time_s_ >= TARGET_TIME && elapsed_time_s_ - delta_time < TARGET_TIME) {
            jail_texture_->setColor(color_[MAX_FADE_COLOR_INDEX - i].r, color_[MAX_FADE_COLOR_INDEX - i].g, color_[MAX_FADE_COLOR_INDEX - i].b);
            since_texture_->setColor(color_[MAX_FADE_COLOR_INDEX - i].r, color_[MAX_FADE_COLOR_INDEX - i].g, color_[MAX_FADE_COLOR_INDEX - i].b);
        }
    }
}

// Actualiza las variables
void Logo::update(float delta_time) {
    elapsed_time_s_ += delta_time;  // Acumula el tiempo transcurrido

    static auto* const SCREEN = Screen::get();
    SCREEN->update(delta_time);  // Actualiza el objeto screen
    Audio::update();             // Actualiza el objeto audio

    handleSound();                    // Maneja la reproducción del sonido
    updateTextureColors(delta_time);  // Actualiza los colores de las texturas
    updateJAILGAMES(delta_time);      // Actualiza el logo de JAILGAMES
}

// Dibuja en pantalla
void Logo::render() {
    static auto* const SCREEN = Screen::get();

    SCREEN->start();
    SCREEN->clean();

    renderJAILGAMES();

    SCREEN->render();
}

// Calcula el tiempo transcurrido desde el último frame
auto Logo::calculateDeltaTime() -> float {
    const Uint64 CURRENT_TIME = SDL_GetTicks();
    const float DELTA_TIME = static_cast<float>(CURRENT_TIME - last_time_) / 1000.0F;  // Convertir ms a segundos
    last_time_ = CURRENT_TIME;
    return DELTA_TIME;
}

// Avanza un frame del logo (llamado desde Director::iterate)
void Logo::iterate() {
    const float DELTA_TIME = calculateDeltaTime();
    checkInput();
    update(DELTA_TIME);
    render();
}

// Procesa un evento (llamado desde Director::handleEvent)
void Logo::handleEvent(const SDL_Event& /*event*/) {
    // Eventos globales (QUIT, resize, hotplug) ya gestionados por Director::handleEvent
}

// Bucle para el logo del juego (fallback legacy)
void Logo::run() {
    last_time_ = SDL_GetTicks();

    while (Section::name == Section::Name::LOGO) {
        const float DELTA_TIME = calculateDeltaTime();

        checkInput();
        update(DELTA_TIME);
        checkEvents();  // Tiene que ir antes del render
        render();
    }
}

// Renderiza el logo de JAILGAMES
void Logo::renderJAILGAMES() {
    // Dibuja los sprites
    for (auto& sprite : jail_sprite_) {
        sprite->render();
    }

    if (elapsed_time_s_ >= SHOW_SINCE_SPRITE_TIME_S) {
        since_sprite_->render();
    }
}