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

#include "escena_logo.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/input/mouse.hpp"
#include "core/rendering/shape_renderer.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/global_events.hpp"

// Using declarations per simplificar el codi
using GestorEscenes::ContextEscenes;
using Escena = ContextEscenes::Escena;
using Opcio = ContextEscenes::Opcio;

// Helper: calcular el progrés individual d'una lletra
// en funció 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 temps 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;  // Encara no ha començat
    }
    if (global_progress >= end) {
        return 1.0F;  // Completament apareguda
    }
    return (global_progress - start) / (end - start);
}

EscenaLogo::EscenaLogo(SDLManager& sdl, ContextEscenes& context)
    : sdl_(sdl),
      context_(context),
      estat_actual_(EstatAnimacio::PRE_ANIMATION),
      temps_estat_actual_(0.0F),
      debris_manager_(std::make_unique<Effects::DebrisManager>(sdl.obte_renderer())),
      lletra_explosio_index_(0),
      temps_des_ultima_explosio_(0.0F) {
    std::cout << "Escena Logo: Inicialitzant...\n";

    // Consumir opcions (LOGO no processa opcions actualment)
    auto opcio = context_.consumir_opcio();
    (void)opcio;  // Suprimir warning

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

EscenaLogo::~EscenaLogo() {
    // Aturar tots els sons i la música
    Audio::get()->stopAllSounds();
    std::cout << "Escena Logo: Sons aturats\n";
}

void EscenaLogo::executar() {
    SDL_Event event;
    Uint64 last_time = SDL_GetTicks();

    while (GestorEscenes::actual == Escena::LOGO) {
        // Calcular delta_time real
        Uint64 current_time = SDL_GetTicks();
        float delta_time = (current_time - last_time) / 1000.0F;
        last_time = current_time;

        // Limitar delta_time per evitar grans salts
        delta_time = std::min(delta_time, 0.05F);

        // Actualitzar comptador de FPS
        sdl_.updateFPS(delta_time);

        // Actualitzar visibilitat del cursor (auto-ocultar)
        Mouse::updateCursorVisibility();

        // Actualitzar sistema d'input ABANS del event loop
        Input::get()->update();

        // Processar events SDL
        while (SDL_PollEvent(&event)) {
            // Manejo de finestra
            if (sdl_.handleWindowEvent(event)) {
                continue;
            }

            // Events globals (F1/F2/F3/ESC/QUIT)
            if (GlobalEvents::handle(event, sdl_, context_)) {
                continue;
            }

            // Processar events de l'escena (qualsevol tecla/clic salta al joc)
            processar_events(event);
        }

        // Actualitzar lògica
        actualitzar(delta_time);

        // Actualitzar colors oscil·lats (efecte verd global)
        sdl_.updateColors(delta_time);

        // Actualitzar context de renderitzat (factor d'escala global)
        sdl_.updateRenderingContext();

        // Dibuixar
        dibuixar();
    }

    std::cout << "Escena Logo: Finalitzant...\n";
}

void EscenaLogo::inicialitzar_lletres() {
    using namespace Graphics;

    // Llista de fitxers .shp (A repetida per a les dues A's)
    std::vector<std::string> fitxers = {
        "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 totes les formes i calcular amplades
    float ancho_total = 0.0F;

    for (const auto& fitxer : fitxers) {
        auto forma = ShapeLoader::load(fitxer);
        if (!forma || !forma->es_valida()) {
            std::cerr << "[EscenaLogo] Error carregant " << fitxer << std::endl;
            continue;
        }

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

        for (const auto& prim : forma->get_primitives()) {
            for (const auto& punt : prim.points) {
                min_x = std::min(min_x, punt.x);
                max_x = std::max(max_x, punt.x);
            }
        }

        float ancho_sin_escalar = max_x - min_x;

        // IMPORTANT: Escalar ancho i offset amb ESCALA_FINAL
        // per que les posicions finals coincideixin amb la mida real de les lletres
        float ancho = ancho_sin_escalar * ESCALA_FINAL;
        float offset_centre = (forma->get_centre().x - min_x) * ESCALA_FINAL;

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

        ancho_total += ancho;
    }

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

    // Pas 3: Calcular posició inicial (centrat horitzontal)
    constexpr float PANTALLA_ANCHO = 640.0F;
    constexpr float PANTALLA_ALTO = 480.0F;

    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 centre de la forma (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.posicio.x = x_actual + lletra.offset_centre;
        lletra.posicio.y = y_centre;

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

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

void EscenaLogo::canviar_estat(EstatAnimacio nou_estat) {
    estat_actual_ = nou_estat;
    temps_estat_actual_ = 0.0F;  // Reset temps

    // Inicialitzar estat d'explosió
    if (nou_estat == EstatAnimacio::EXPLOSION) {
        lletra_explosio_index_ = 0;
        temps_des_ultima_explosio_ = 0.0F;

        // Generar ordre aleatori d'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 == EstatAnimacio::POST_EXPLOSION) {
        Audio::get()->playMusic("title.ogg");
    }

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

bool EscenaLogo::totes_lletres_completes() const {
    // Quan global_progress = 1.0, totes les lletres tenen letra_progress = 1.0
    return temps_estat_actual_ >= DURACIO_ZOOM;
}

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

    // Comprovar si és el moment d'explotar 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_->explotar(
                lletra.forma,           // Forma a explotar
                lletra.posicio,         // Posició
                0.0F,                   // Angle (sense rotació)
                ESCALA_FINAL,           // Escala (lletres a escala final)
                VELOCITAT_EXPLOSIO,     // Velocitat base
                1.0F,                   // Brightness màxim (per defecte)
                {.x = 0.0F, .y = 0.0F}  // Sense velocitat (per defecte)
            );

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

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

void EscenaLogo::actualitzar(float delta_time) {
    temps_estat_actual_ += delta_time;

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

        case EstatAnimacio::ANIMATION: {
            // Reproduir so per cada lletra quan 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 quan 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(EstatAnimacio::POST_ANIMATION);
            }
            break;
        }

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

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

        case EstatAnimacio::POST_EXPLOSION:
            if (temps_estat_actual_ >= DURACIO_POST_EXPLOSION) {
                // Transició a pantalla de títol
                context_.canviar_escena(Escena::TITOL);
                GestorEscenes::actual = Escena::TITOL;
            }
            break;
    }

    // Verificar botones de skip (SHOOT P1/P2)
    if (checkSkipButtonPressed()) {
        context_.canviar_escena(Escena::TITOL, Opcio::JUMP_TO_TITLE_MAIN);
        GestorEscenes::actual = Escena::TITOL;
    }

    // Actualitzar animacions de debris
    debris_manager_->actualitzar(delta_time);
}

void EscenaLogo::dibuixar() {
    // Fons negre
    sdl_.neteja(0, 0, 0);

    // PRE_ANIMATION: Només pantalla negra
    if (estat_actual_ == EstatAnimacio::PRE_ANIMATION) {
        sdl_.presenta();
        return;  // No renderitzar lletres
    }

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

        const Punt 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;
            }

            Punt pos_actual;
            pos_actual.x =
                ORIGEN_ZOOM.x + ((lletra.posicio.x - ORIGEN_ZOOM.x) * letra_progress);
            pos_actual.y =
                ORIGEN_ZOOM.y + ((lletra.posicio.y - ORIGEN_ZOOM.y) * letra_progress);

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

            Rendering::render_shape(
                sdl_.obte_renderer(),
                lletra.forma,
                pos_actual,
                0.0F,
                escala_actual,
                true,
                1.0F);
        }
    }

    // EXPLOSION: Dibuixar només lletres que encara no han explotat
    if (estat_actual_ == EstatAnimacio::EXPLOSION) {
        // Crear conjunt de lletres ja explotades
        std::set<size_t> explotades;
        for (size_t i = 0; i < lletra_explosio_index_; i++) {
            explotades.insert(ordre_explosio_[i]);
        }

        // Dibuixar només 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_.obte_renderer(),
                    lletra.forma,
                    lletra.posicio,
                    0.0F,
                    ESCALA_FINAL,
                    true,
                    1.0F);
            }
        }
    }

    // POST_EXPLOSION: No dibuixar lletres, només debris (a baix)

    // Sempre dibuixar debris (si n'hi ha d'actius)
    debris_manager_->dibuixar();

    sdl_.presenta();
}

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

void EscenaLogo::processar_events(const SDL_Event& event) {
    // No procesar eventos genéricos aquí - la lógica se movió a actualitzar()
}
