// logo_scene.cpp - Implementació de l'escena logo
// © 2026 JailDesigner

#include "logo_scene.hpp"

#include <algorithm>
#include <cfloat>
#include <iostream>
#include <random>
#include <set>

#include "core/audio/audio.hpp"
#include "core/graphics/shape_loader.hpp"
#include "core/input/input.hpp"
#include "core/rendering/shape_renderer.hpp"
#include "core/system/scene_context.hpp"

// Using declarations per simplificar el codi
using SceneManager::SceneContext;
using SceneType = SceneContext::SceneType;
using Option = SceneContext::Option;

// Helper: calcular el progrés individual de una letter
// en función del progrés global (efecte seqüencial)
static auto computeLetterProgress(size_t letter_index, size_t num_letters, float global_progress, float threshold) -> float {
    if (num_letters == 0) {
        return 1.0F;
    }

    // Calcular time per letter
    float duration_per_letter = 1.0F / static_cast<float>(num_letters);
    float step = threshold * duration_per_letter;
    float start = static_cast<float>(letter_index) * step;
    float end = start + duration_per_letter;

    // Interpolar progrés
    if (global_progress < start) {
        return 0.0F;  // Aún no ha començat
    }
    if (global_progress >= end) {
        return 1.0F;  // Completament apareguda
    }
    return (global_progress - start) / (end - start);
}

LogoScene::LogoScene(SDLManager& sdl, SceneContext& context)
    : sdl_(sdl),
      context_(context),

      debris_manager_(std::make_unique<Effects::DebrisManager>(sdl.getRenderer())) {
    std::cout << "SceneType Logo: Inicialitzant...\n";

    // Consumir opciones (LOGO no processa opciones actualment)
    auto option = context_.consumeOption();
    (void)option;  // Suprimir warning

    sound_played_.fill(false);  // Inicialitzar seguiment de sons

    // Si ja sona música (venim de la demo del cicle d'atracció), operar en
    // silenci: ni sons de lletres ni reinici de title.ogg.
    attract_silent_ = (Audio::getMusicState() == Audio::MusicState::PLAYING);

    initLetters();
}

LogoScene::~LogoScene() {
    // Aturar todos los sons y la música
    Audio::get()->stopAllSounds();
    std::cout << "SceneType Logo: Sons parados\n";
}

auto LogoScene::isFinished() const -> bool {
    return context_.nextScene() != SceneType::LOGO;
}

void LogoScene::handleEvent(const SDL_Event& event) {
    // La lógica de skip se decide en update() consultando el estado de Input;
    // aquí no hay eventos puntuales que procesar.
    (void)event;
}

void LogoScene::initLetters() {
    using namespace Graphics;

    // Llista de archivos .shp (A repetida para las dues A's)
    std::vector<std::string> archivos = {
        "logo/letra_j.shp",
        "logo/letra_a.shp",
        "logo/letra_i.shp",
        "logo/letra_l.shp",
        "logo/letra_g.shp",
        "logo/letra_a.shp",
        "logo/letra_m.shp",
        "logo/letra_e.shp",
        "logo/letra_s.shp"};

    // Pas 1: Carregar todas las formes i calcular amplades
    float total_width = 0.0F;

    for (const auto& file : archivos) {
        auto shape = ShapeLoader::load(file);
        if (!shape || !shape->isValid()) {
            std::cerr << "[LogoScene] Error carregant " << file << '\n';
            continue;
        }

        // Calcular bounding box de la shape (trobar ancho)
        float min_x = FLT_MAX;
        float max_x = -FLT_MAX;

        for (const auto& prim : shape->getPrimitives()) {
            for (const auto& point : prim.points) {
                min_x = std::min(min_x, point.x);
                max_x = std::max(max_x, point.x);
            }
        }

        float width_unscaled = max_x - min_x;

        // IMPORTANT: Escalar ancho i offset con FINAL_SCALE
        // per que las posicions finals coincideixin con la mida real de las lletres
        float width = width_unscaled * FINAL_SCALE;
        float center_offset = (shape->getCenter().x - min_x) * FINAL_SCALE;

        letters_.push_back({shape,
            {.x = 0.0F, .y = 0.0F},  // Posición es calcularà después
            width,
            center_offset});

        total_width += width;
    }

    // Pas 2: Añadir espaiat entre lletres
    total_width += LETTER_SPACING * (letters_.size() - 1);

    // Pas 3: Calcular posición inicial (centrat horitzontal)
    constexpr auto SCREEN_WIDTH = static_cast<float>(Defaults::Game::WIDTH);
    constexpr auto PANTALLA_ALTO = static_cast<float>(Defaults::Game::HEIGHT);

    float x_inicial = (SCREEN_WIDTH - total_width) / 2.0F;
    float y_centre = PANTALLA_ALTO / 2.0F;

    // Pas 4: Assignar posicions a cada letter
    float x_actual = x_inicial;

    for (auto& letter : letters_) {
        // Posicionar el centro de la shape (shape_centre) en pantalla
        // Usar center_offset en lloc de ancho/2 perquè shape_centre
        // pot no estar exactament al mig del bounding box
        letter.position.x = x_actual + letter.center_offset;
        letter.position.y = y_centre;

        // Avançar para següent letter
        x_actual += letter.width + LETTER_SPACING;
    }

    std::cout << "[LogoScene] " << letters_.size()
              << " lletres carregades, ancho total: " << total_width << " px\n";
}

void LogoScene::changeState(AnimationState nou_estat) {
    current_state_ = nou_estat;
    temps_current_state_ = 0.0F;  // Reset time

    // Inicialitzar state de explosión
    if (nou_estat == AnimationState::EXPLOSION) {
        letter_explosion_index_ = 0;
        time_since_last_explosion_ = 0.0F;

        // Generar ordre aleatori de explosions
        explosion_order_.clear();
        for (size_t i = 0; i < letters_.size(); i++) {
            explosion_order_.push_back(i);
        }
        std::random_device rd;
        std::mt19937 g(rd());
        std::shuffle(explosion_order_.begin(), explosion_order_.end(), g);
    } else if (nou_estat == AnimationState::POST_EXPLOSION) {
        // En el cicle d'atracció la música ja sona; no la reiniciem.
        if (!attract_silent_) {
            Audio::get()->playMusic("title.ogg");
        }
    }

    std::cout << "[LogoScene] Canvi a state: " << static_cast<int>(nou_estat)
              << "\n";
}

auto LogoScene::allLettersComplete() const -> bool {
    // Cuando global_progress = 1.0, todas las lletres tenen letter_progress = 1.0
    return temps_current_state_ >= DURATION_ZOOM;
}

void LogoScene::updateExplosions(float delta_time) {
    time_since_last_explosion_ += delta_time;

    // Comprovar si es el moment de explode la següent letter
    if (time_since_last_explosion_ >= DELAY_ENTRE_EXPLOSIONS) {
        if (letter_explosion_index_ < letters_.size()) {
            // Explotar letter actual (en ordre aleatori)
            size_t index_actual = explosion_order_[letter_explosion_index_];
            const auto& letter = letters_[index_actual];

            debris_manager_->explode(
                letter.shape,            // Forma a explode
                letter.position,         // Posición
                0.0F,                    // Angle (sin rotación)
                FINAL_SCALE,             // Escala (lletres a scale final)
                SPEED_EXPLOSIO,          // Velocidad base
                1.0F,                    // Brightness màxim (per defecte)
                {.x = 0.0F, .y = 0.0F},  // Sin velocity (per defecte)
                0.0F,                    // Velocitat angular (per defecte)
                0.0F,                    // Factor herència visual (per defecte)
                // Dins el cicle d'atracció les explosions són mudes; fora, so
                // d'explosió per defecte.
                attract_silent_ ? "" : Defaults::Sound::ENEMY_EXPLOSION);

            std::cout << "[LogoScene] Explota letter " << letter_explosion_index_ << "\n";

            // Passar a la següent letter
            letter_explosion_index_++;
            time_since_last_explosion_ = 0.0F;
        } else {
            // Todas las lletres han explotat, transición a POST_EXPLOSION
            changeState(AnimationState::POST_EXPLOSION);
        }
    }
}

void LogoScene::update(float delta_time) {
    temps_current_state_ += delta_time;

    switch (current_state_) {
        case AnimationState::PRE_ANIMATION:
            if (temps_current_state_ >= DURATION_PRE) {
                changeState(AnimationState::ANIMATION);
            }
            break;

        case AnimationState::ANIMATION: {
            // Reproduir so per cada letter cuando comença a aparèixer
            float global_progress = std::min(temps_current_state_ / DURATION_ZOOM, 1.0F);

            for (size_t i = 0; i < letters_.size() && i < sound_played_.size(); i++) {
                if (!sound_played_[i]) {
                    float letter_progress = computeLetterProgress(
                        i,
                        letters_.size(),
                        global_progress,
                        LETTER_THRESHOLD);

                    // Reproduir so cuando la letter comença a aparèixer (progress > 0).
                    // En mode silenciós (cicle d'atracció) saltem el so però igualment
                    // marquem la letter per no acumular pendents.
                    if (letter_progress > 0.0F) {
                        if (!attract_silent_) {
                            Audio::get()->playSound(Defaults::Sound::LOGO, Audio::Group::GAME);
                        }
                        sound_played_[i] = true;
                    }
                }
            }

            if (allLettersComplete()) {
                changeState(AnimationState::POST_ANIMATION);
            }
            break;
        }

        case AnimationState::POST_ANIMATION:
            if (temps_current_state_ >= DURATION_POST_ANIMATION) {
                changeState(AnimationState::EXPLOSION);
            }
            break;

        case AnimationState::EXPLOSION:
            updateExplosions(delta_time);
            break;

        case AnimationState::POST_EXPLOSION:
            if (temps_current_state_ >= DURATION_POST_EXPLOSION) {
                // Transición a pantalla de título
                context_.setNextScene(SceneType::TITLE);
            }
            break;
    }

    // Verificar botones de skip (SHOOT P1/P2)
    if (checkSkipButtonPressed()) {
        context_.setNextScene(SceneType::TITLE, Option::JUMP_TO_TITLE_MAIN);
    }

    // Actualitzar animaciones de debris
    debris_manager_->update(delta_time);
}

void LogoScene::draw() {
    // Director ha hecho el clear; aquí solo pintamos lo de la escena.

    // PRE_ANIMATION: Solo pantalla negra (no se pinta nada).
    if (current_state_ == AnimationState::PRE_ANIMATION) {
        return;
    }

    // ANIMATION o POST_ANIMATION: Dibuixar lletres con animación
    if (current_state_ == AnimationState::ANIMATION ||
        current_state_ == AnimationState::POST_ANIMATION) {
        float global_progress =
            (current_state_ == AnimationState::ANIMATION)
            ? std::min(temps_current_state_ / DURATION_ZOOM, 1.0F)
            : 1.0F;  // POST: mantenir al 100%

        const Vec2 ZOOM_ORIGIN = {.x = ZOOM_ORIGIN_X, .y = ZOOM_ORIGIN_Y};

        for (size_t i = 0; i < letters_.size(); i++) {
            const auto& letter = letters_[i];

            float letter_progress = computeLetterProgress(
                i,
                letters_.size(),
                global_progress,
                LETTER_THRESHOLD);

            if (letter_progress <= 0.0F) {
                continue;
            }

            Vec2 pos_actual;
            pos_actual.x =
                ZOOM_ORIGIN.x + ((letter.position.x - ZOOM_ORIGIN.x) * letter_progress);
            pos_actual.y =
                ZOOM_ORIGIN.y + ((letter.position.y - ZOOM_ORIGIN.y) * letter_progress);

            float t = letter_progress;
            float ease_factor = 1.0F - ((1.0F - t) * (1.0F - t));
            float current_scale =
                INITIAL_SCALE + ((FINAL_SCALE - INITIAL_SCALE) * ease_factor);

            Rendering::renderShape(
                sdl_.getRenderer(),
                letter.shape,
                pos_actual,
                0.0F,
                current_scale,
                1.0F);
        }
    }

    // EXPLOSION: Dibuixar solo lletres que aún no han explotat
    if (current_state_ == AnimationState::EXPLOSION) {
        // Crear conjunt de lletres ya explotades
        std::set<size_t> explotades;
        for (size_t i = 0; i < letter_explosion_index_; i++) {
            explotades.insert(explosion_order_[i]);
        }

        // Dibuixar solo lletres que NO han explotat
        for (size_t i = 0; i < letters_.size(); i++) {
            if (!explotades.contains(i)) {
                const auto& letter = letters_[i];

                Rendering::renderShape(
                    sdl_.getRenderer(),
                    letter.shape,
                    letter.position,
                    0.0F,
                    FINAL_SCALE,
                    1.0F);
            }
        }
    }

    // POST_EXPLOSION: No draw lletres, solo debris (a baix)

    // Siempre draw debris (si n'hay de active)
    debris_manager_->draw();
}

auto LogoScene::checkSkipButtonPressed() -> bool {
    return Input::get()->checkAnyPlayerAction(ARCADE_BUTTONS);
}
