// ship_animator.cpp - Implementació del sistema d'animació de naus
// © 2025 Port a C++20 amb SDL3

#include "ship_animator.hpp"

#include <cmath>

#include "core/defaults.hpp"
#include "core/graphics/shape_loader.hpp"
#include "core/math/easing.hpp"

namespace Title {

ShipAnimator::ShipAnimator(SDL_Renderer* renderer)
    : renderer_(renderer) {
}

void ShipAnimator::inicialitzar() {
    // Carregar formes de naus amb perspectiva pre-calculada
    auto forma_p1 = Graphics::ShapeLoader::load("ship_p1.shp");   // Perspectiva esquerra
    auto forma_p2 = Graphics::ShapeLoader::load("ship2_p2.shp");  // Perspectiva dreta

    // Configurar nau P1
    naus_[0].jugador_id = 1;
    naus_[0].forma = forma_p1;
    configurar_nau_p1(naus_[0]);

    // Configurar nau P2
    naus_[1].jugador_id = 2;
    naus_[1].forma = forma_p2;
    configurar_nau_p2(naus_[1]);
}

void ShipAnimator::actualitzar(float delta_time) {
    // Dispatcher segons estat de cada nau
    for (auto& nau : naus_) {
        if (!nau.visible) continue;

        switch (nau.estat) {
            case EstatNau::ENTERING:
                actualitzar_entering(nau, delta_time);
                break;
            case EstatNau::FLOATING:
                actualitzar_floating(nau, delta_time);
                break;
            case EstatNau::EXITING:
                actualitzar_exiting(nau, delta_time);
                break;
        }
    }
}

void ShipAnimator::dibuixar() const {
    for (const auto& nau : naus_) {
        if (!nau.visible) continue;

        // Renderitzar nau (perspectiva ja incorporada a la forma)
        Rendering::render_shape(
            renderer_,
            nau.forma,
            nau.posicio_actual,
            0.0f,  // angle (rotació 2D no utilitzada)
            nau.escala_actual,
            true,  // dibuixar
            1.0f,  // progress (sempre visible)
            1.0f   // brightness (brillantor màxima)
        );
    }
}

void ShipAnimator::start_entry_animation() {
    using namespace Defaults::Title::Ships;

    // Configurar nau P1 per a l'animació d'entrada
    naus_[0].estat = EstatNau::ENTERING;
    naus_[0].temps_estat = 0.0f;
    naus_[0].posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_8_ANGLE);
    naus_[0].posicio_actual = naus_[0].posicio_inicial;
    naus_[0].escala_actual = naus_[0].escala_inicial;

    // Configurar nau P2 per a l'animació d'entrada
    naus_[1].estat = EstatNau::ENTERING;
    naus_[1].temps_estat = 0.0f;
    naus_[1].posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_4_ANGLE);
    naus_[1].posicio_actual = naus_[1].posicio_inicial;
    naus_[1].escala_actual = naus_[1].escala_inicial;
}

void ShipAnimator::trigger_exit_animation() {
    // Configurar ambdues naus per a l'animació de sortida
    for (auto& nau : naus_) {
        // Canviar estat a EXITING
        nau.estat = EstatNau::EXITING;
        nau.temps_estat = 0.0f;

        // Preservar posició actual (pot estar a mig camí si START es prem durant ENTERING)
        nau.posicio_inicial = nau.posicio_actual;

        // La escala objectiu es preserva per a calcular la interpolació
        // (escala_actual pot ser diferent si està en ENTERING)
    }
}

void ShipAnimator::trigger_exit_animation_for_player(int jugador_id) {
    // Trobar la nau del jugador especificat
    for (auto& nau : naus_) {
        if (nau.jugador_id == jugador_id) {
            // Canviar estat a EXITING només per aquesta nau
            nau.estat = EstatNau::EXITING;
            nau.temps_estat = 0.0f;

            // Preservar posició actual (pot estar a mig camí si START es prem durant ENTERING)
            nau.posicio_inicial = nau.posicio_actual;

            // La escala objectiu es preserva per a calcular la interpolació
            // (escala_actual pot ser diferent si està en ENTERING)
            break;  // Només una nau per jugador
        }
    }
}

void ShipAnimator::set_visible(bool visible) {
    for (auto& nau : naus_) {
        nau.visible = visible;
    }
}

bool ShipAnimator::is_animation_complete() const {
    // Comprovar si totes les naus són invisibles (han completat l'animació de sortida)
    for (const auto& nau : naus_) {
        if (nau.visible) {
            return false;  // Encara hi ha alguna nau visible
        }
    }
    return true;  // Totes les naus són invisibles
}

// Mètodes d'animació (stubs)
void ShipAnimator::actualitzar_entering(NauTitol& nau, float delta_time) {
    using namespace Defaults::Title::Ships;

    nau.temps_estat += delta_time;

    // Esperar al delay abans de començar l'animació
    if (nau.temps_estat < nau.entry_delay) {
        // Encara en delay: la nau es queda fora de pantalla (posició inicial)
        nau.posicio_actual = nau.posicio_inicial;
        nau.escala_actual = nau.escala_inicial;
        return;
    }

    // Càlcul del progrés (restant el delay)
    float elapsed = nau.temps_estat - nau.entry_delay;
    float progress = std::min(1.0f, elapsed / ENTRY_DURATION);

    // Aplicar easing (ease_out_quad per arribada suau)
    float eased_progress = Easing::ease_out_quad(progress);

    // Lerp posició (inicial → objectiu)
    nau.posicio_actual.x = Easing::lerp(nau.posicio_inicial.x, nau.posicio_objectiu.x, eased_progress);
    nau.posicio_actual.y = Easing::lerp(nau.posicio_inicial.y, nau.posicio_objectiu.y, eased_progress);

    // Lerp escala (gran → normal)
    nau.escala_actual = Easing::lerp(nau.escala_inicial, nau.escala_objectiu, eased_progress);

    // Transicionar a FLOATING quan completi
    if (elapsed >= ENTRY_DURATION) {
        nau.estat = EstatNau::FLOATING;
        nau.temps_estat = 0.0f;
        nau.fase_oscilacio = 0.0f;  // Reiniciar fase d'oscil·lació
    }
}

void ShipAnimator::actualitzar_floating(NauTitol& nau, float delta_time) {
    using namespace Defaults::Title::Ships;

    // Actualitzar temps i fase d'oscil·lació
    nau.temps_estat += delta_time;
    nau.fase_oscilacio += delta_time;

    // Oscil·lació sinusoïdal X/Y (paràmetres específics per nau)
    float offset_x = nau.amplitude_x * std::sin(2.0f * Defaults::Math::PI * nau.frequency_x * nau.fase_oscilacio);
    float offset_y = nau.amplitude_y * std::sin(2.0f * Defaults::Math::PI * nau.frequency_y * nau.fase_oscilacio + FLOAT_PHASE_OFFSET);

    // Aplicar oscil·lació a la posició objectiu
    nau.posicio_actual.x = nau.posicio_objectiu.x + offset_x;
    nau.posicio_actual.y = nau.posicio_objectiu.y + offset_y;

    // Escala constant (sense "breathing" per ara)
    nau.escala_actual = nau.escala_objectiu;
}

void ShipAnimator::actualitzar_exiting(NauTitol& nau, float delta_time) {
    using namespace Defaults::Title::Ships;

    nau.temps_estat += delta_time;

    // Calcular progrés (0.0 → 1.0)
    float progress = std::min(1.0f, nau.temps_estat / EXIT_DURATION);

    // Aplicar easing (ease_in_quad per acceleració cap al punt de fuga)
    float eased_progress = Easing::ease_in_quad(progress);

    // Punt de fuga (centre del starfield)
    constexpr Punt punt_fuga{VANISHING_POINT_X, VANISHING_POINT_Y};

    // Lerp posició cap al punt de fuga (preservar posició inicial actual)
    // Nota: posicio_inicial conté la posició on estava quan es va activar EXITING
    nau.posicio_actual.x = Easing::lerp(nau.posicio_inicial.x, punt_fuga.x, eased_progress);
    nau.posicio_actual.y = Easing::lerp(nau.posicio_inicial.y, punt_fuga.y, eased_progress);

    // Escala redueix a 0 (simula Z → infinit)
    nau.escala_actual = nau.escala_objectiu * (1.0f - eased_progress);

    // Marcar invisible quan l'animació completi
    if (progress >= 1.0f) {
        nau.visible = false;
    }
}

// Configuració
void ShipAnimator::configurar_nau_p1(NauTitol& nau) {
    using namespace Defaults::Title::Ships;

    // Estat inicial: FLOATING (per test estàtic)
    nau.estat = EstatNau::FLOATING;
    nau.temps_estat = 0.0f;

    // Posicions (clock 8, bottom-left)
    nau.posicio_objectiu = {P1_TARGET_X(), P1_TARGET_Y()};

    // Calcular posició inicial (fora de pantalla)
    nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_8_ANGLE);
    nau.posicio_actual = nau.posicio_inicial;  // Començar fora de pantalla

    // Escales
    nau.escala_objectiu = FLOATING_SCALE;
    nau.escala_actual = FLOATING_SCALE;
    nau.escala_inicial = ENTRY_SCALE_START;

    // Flotació
    nau.fase_oscilacio = 0.0f;

    // Paràmetres d'entrada
    nau.entry_delay = P1_ENTRY_DELAY;

    // Paràmetres d'oscil·lació específics P1
    nau.amplitude_x = FLOAT_AMPLITUDE_X;
    nau.amplitude_y = FLOAT_AMPLITUDE_Y;
    nau.frequency_x = FLOAT_FREQUENCY_X_BASE * P1_FREQUENCY_MULTIPLIER;
    nau.frequency_y = FLOAT_FREQUENCY_Y_BASE * P1_FREQUENCY_MULTIPLIER;

    // Visibilitat
    nau.visible = true;
}

void ShipAnimator::configurar_nau_p2(NauTitol& nau) {
    using namespace Defaults::Title::Ships;

    // Estat inicial: FLOATING (per test estàtic)
    nau.estat = EstatNau::FLOATING;
    nau.temps_estat = 0.0f;

    // Posicions (clock 4, bottom-right)
    nau.posicio_objectiu = {P2_TARGET_X(), P2_TARGET_Y()};

    // Calcular posició inicial (fora de pantalla)
    nau.posicio_inicial = calcular_posicio_fora_pantalla(CLOCK_4_ANGLE);
    nau.posicio_actual = nau.posicio_inicial;  // Començar fora de pantalla

    // Escales
    nau.escala_objectiu = FLOATING_SCALE;
    nau.escala_actual = FLOATING_SCALE;
    nau.escala_inicial = ENTRY_SCALE_START;

    // Flotació
    nau.fase_oscilacio = 0.0f;

    // Paràmetres d'entrada
    nau.entry_delay = P2_ENTRY_DELAY;

    // Paràmetres d'oscil·lació específics P2
    nau.amplitude_x = FLOAT_AMPLITUDE_X;
    nau.amplitude_y = FLOAT_AMPLITUDE_Y;
    nau.frequency_x = FLOAT_FREQUENCY_X_BASE * P2_FREQUENCY_MULTIPLIER;
    nau.frequency_y = FLOAT_FREQUENCY_Y_BASE * P2_FREQUENCY_MULTIPLIER;

    // Visibilitat
    nau.visible = true;
}

Punt ShipAnimator::calcular_posicio_fora_pantalla(float angle_rellotge) const {
    using namespace Defaults::Title::Ships;

    // Convertir angle del rellotge a radians (per exemple: 240° per clock 8)
    // Calcular posició en direcció radial des del centre, però més lluny
    // ENTRY_OFFSET es calcula automàticament: (SHIP_MAX_RADIUS * ENTRY_SCALE_START) + ENTRY_OFFSET_MARGIN
    float extended_radius = CLOCK_RADIUS + ENTRY_OFFSET;

    float x = Defaults::Game::WIDTH / 2.0f + extended_radius * std::cos(angle_rellotge);
    float y = Defaults::Game::HEIGHT / 2.0f + extended_radius * std::sin(angle_rellotge);

    return {x, y};
}

}  // namespace Title
