// logo_scene.cpp - Implementació de l'escena logo
// © 2025 Port a C++20

#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 lletra
// en función del progrés global (efecte seqüencial)
static float calcular_progress_letra(size_t letra_index, size_t num_letras, float global_progress, float threshold) {
    if (num_letras == 0) {
        return 1.0F;
    }

    // Calcular time per lletra
    float duration_per_letra = 1.0F / static_cast<float>(num_letras);
    float step = threshold * duration_per_letra;
    float start = static_cast<float>(letra_index) * step;
    float end = start + duration_per_letra;

    // 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),
      estat_actual_(AnimationState::PRE_ANIMATION),
      temps_estat_actual_(0.0F),
      debris_manager_(std::make_unique<Effects::DebrisManager>(sdl.getRenderer())),
      lletra_explosio_index_(0),
      temps_des_ultima_explosio_(0.0F) {
    std::cout << "SceneType Logo: Inicialitzant...\n";

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

    so_reproduit_.fill(false);  // Inicialitzar seguiment de sons
    inicialitzar_lletres();
}

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::inicialitzar_lletres() {
    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 ancho_total = 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->get_primitives()) {
            for (const auto& point : prim.points) {
                min_x = std::min(min_x, point.x);
                max_x = std::max(max_x, point.x);
            }
        }

        float ancho_sin_escalar = max_x - min_x;

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

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

        ancho_total += ancho;
    }

    // Pas 2: Añadir espaiat entre lletres
    ancho_total += ESPAI_ENTRE_LLETRES * (lletres_.size() - 1);

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

    float x_inicial = (PANTALLA_ANCHO - ancho_total) / 2.0F;
    float y_centre = PANTALLA_ALTO / 2.0F;

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

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

        // Avançar para següent lletra
        x_actual += lletra.ancho + ESPAI_ENTRE_LLETRES;
    }

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

void LogoScene::canviar_estat(AnimationState nou_estat) {
    estat_actual_ = nou_estat;
    temps_estat_actual_ = 0.0F;  // Reset time

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

        // Generar ordre aleatori de explosions
        ordre_explosio_.clear();
        for (size_t i = 0; i < lletres_.size(); i++) {
            ordre_explosio_.push_back(i);
        }
        std::random_device rd;
        std::mt19937 g(rd());
        std::shuffle(ordre_explosio_.begin(), ordre_explosio_.end(), g);
    } else if (nou_estat == AnimationState::POST_EXPLOSION) {
        Audio::get()->playMusic("title.ogg");
    }

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

bool LogoScene::totes_lletres_completes() const {
    // Cuando global_progress = 1.0, todas las lletres tenen letra_progress = 1.0
    return temps_estat_actual_ >= DURACIO_ZOOM;
}

void LogoScene::actualitzar_explosions(float delta_time) {
    temps_des_ultima_explosio_ += delta_time;

    // Comprovar si es el moment de explode la següent lletra
    if (temps_des_ultima_explosio_ >= DELAY_ENTRE_EXPLOSIONS) {
        if (lletra_explosio_index_ < lletres_.size()) {
            // Explotar lletra actual (en ordre aleatori)
            size_t index_actual = ordre_explosio_[lletra_explosio_index_];
            const auto& lletra = lletres_[index_actual];

            debris_manager_->explode(
                lletra.shape,           // Forma a explode
                lletra.position,         // Posición
                0.0F,                   // Angle (sin rotación)
                ESCALA_FINAL,           // Escala (lletres a scale final)
                VELOCITAT_EXPLOSIO,     // Velocidad base
                1.0F,                   // Brightness màxim (per defecte)
                {.x = 0.0F, .y = 0.0F}  // Sin velocity (per defecte)
            );

            std::cout << "[LogoScene] Explota lletra " << lletra_explosio_index_ << "\n";

            // Passar a la següent lletra
            lletra_explosio_index_++;
            temps_des_ultima_explosio_ = 0.0F;
        } else {
            // Todas las lletres han explotat, transición a POST_EXPLOSION
            canviar_estat(AnimationState::POST_EXPLOSION);
        }
    }
}

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

    switch (estat_actual_) {
        case AnimationState::PRE_ANIMATION:
            if (temps_estat_actual_ >= DURACIO_PRE) {
                canviar_estat(AnimationState::ANIMATION);
            }
            break;

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

            for (size_t i = 0; i < lletres_.size() && i < so_reproduit_.size(); i++) {
                if (!so_reproduit_[i]) {
                    float letra_progress = calcular_progress_letra(
                        i,
                        lletres_.size(),
                        global_progress,
                        THRESHOLD_LETRA);

                    // Reproduir so cuando la lletra comença a aparèixer (progress > 0)
                    if (letra_progress > 0.0F) {
                        Audio::get()->playSound(Defaults::Sound::LOGO, Audio::Group::GAME);
                        so_reproduit_[i] = true;
                    }
                }
            }

            if (totes_lletres_completes()) {
                canviar_estat(AnimationState::POST_ANIMATION);
            }
            break;
        }

        case AnimationState::POST_ANIMATION:
            if (temps_estat_actual_ >= DURACIO_POST_ANIMATION) {
                canviar_estat(AnimationState::EXPLOSION);
            }
            break;

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

        case AnimationState::POST_EXPLOSION:
            if (temps_estat_actual_ >= DURACIO_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 (estat_actual_ == AnimationState::PRE_ANIMATION) {
        return;
    }

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

        const Vec2 ORIGEN_ZOOM = {.x = ORIGEN_ZOOM_X, .y = ORIGEN_ZOOM_Y};

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

            float letra_progress = calcular_progress_letra(
                i,
                lletres_.size(),
                global_progress,
                THRESHOLD_LETRA);

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

            Vec2 pos_actual;
            pos_actual.x =
                ORIGEN_ZOOM.x + ((lletra.position.x - ORIGEN_ZOOM.x) * letra_progress);
            pos_actual.y =
                ORIGEN_ZOOM.y + ((lletra.position.y - ORIGEN_ZOOM.y) * letra_progress);

            float t = letra_progress;
            float ease_factor = 1.0F - ((1.0F - t) * (1.0F - t));
            float current_scale =
                ESCALA_INICIAL + ((ESCALA_FINAL - ESCALA_INICIAL) * ease_factor);

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

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

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

                Rendering::render_shape(
                    sdl_.getRenderer(),
                    lletra.shape,
                    lletra.position,
                    0.0F,
                    ESCALA_FINAL,
                    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);
}
