// escena_joc.cpp - Implementació de la lògica del joc
// © 1999 Visente i Sergi (versió Pascal)
// © 2025 Port a C++20 amb SDL3

#include "escena_joc.hpp"

#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <vector>

#include "core/audio/audio.hpp"
#include "core/input/input.hpp"
#include "core/input/mouse.hpp"
#include "core/math/easing.hpp"
#include "core/rendering/line_renderer.hpp"
#include "core/system/context_escenes.hpp"
#include "core/system/global_events.hpp"
#include "game/stage_system/stage_loader.hpp"

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

EscenaJoc::EscenaJoc(SDLManager& sdl, ContextEscenes& context)
    : sdl_(sdl),
      context_(context),
      debris_manager_(sdl.obte_renderer()),
      gestor_puntuacio_(sdl.obte_renderer()),
      text_(sdl.obte_renderer()),
      init_hud_rect_sound_played_(false) {
    // Recuperar configuració de partida des del context
    config_partida_ = context_.get_config_partida();

    // Debug output de la configuració
    std::cout << "[EscenaJoc] Configuració de partida - P1: "
              << (config_partida_.jugador1_actiu ? "ACTIU" : "INACTIU")
              << ", P2: "
              << (config_partida_.jugador2_actiu ? "ACTIU" : "INACTIU")
              << std::endl;

    // Consumir opcions (preparació per MODE_DEMO futur)
    auto opcio = context_.consumir_opcio();
    (void)opcio;  // Suprimir warning de variable no usada

    // Inicialitzar naus amb renderer (P1=ship.shp, P2=ship2.shp)
    naus_[0] = Nau(sdl.obte_renderer(), "ship.shp");   // Jugador 1: nave estàndar
    naus_[1] = Nau(sdl.obte_renderer(), "ship2.shp");  // Jugador 2: interceptor amb ales

    // Inicialitzar bales amb renderer
    for (auto& bala : bales_) {
        bala = Bala(sdl.obte_renderer());
    }

    // Inicialitzar enemics amb renderer
    for (auto& enemy : orni_) {
        enemy = Enemic(sdl.obte_renderer());
    }
}

void EscenaJoc::executar() {
    std::cout << "Escena Joc: Inicialitzant...\n";

    // Inicialitzar estat del joc
    inicialitzar();

    SDL_Event event;
    Uint64 last_time = SDL_GetTicks();

    while (GestorEscenes::actual == Escena::JOC) {
        // 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
        if (delta_time > 0.05f) {
            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)
            GlobalEvents::handle(event, sdl_, context_);
        }

        // Actualitzar física del joc amb delta_time real
        actualitzar(delta_time);

        // Actualitzar sistema d'audio
        Audio::update();

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

        // Netejar pantalla (usa color oscil·lat)
        sdl_.neteja(0, 0, 0);

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

        // Dibuixar joc
        dibuixar();

        // Presentar renderer (swap buffers)
        sdl_.presenta();
    }

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

void EscenaJoc::inicialitzar() {
    // Inicialitzar generador de números aleatoris
    // Basat en el codi Pascal original: line 376
    std::srand(static_cast<unsigned>(std::time(nullptr)));

    // [NEW] Load stage configuration (only once)
    if (!stage_config_) {
        stage_config_ = StageSystem::StageLoader::carregar("data/stages/stages.yaml");
        if (!stage_config_) {
            std::cerr << "[EscenaJoc] Error: no s'ha pogut carregar stages.yaml" << std::endl;
            // Continue without stage system (will crash, but helps debugging)
        }
    }

    // [NEW] Initialize stage manager
    stage_manager_ = std::make_unique<StageSystem::StageManager>(stage_config_.get());
    stage_manager_->inicialitzar();

    // [NEW] Set ship position reference for safe spawn (P1 for now, TODO: dual tracking)
    stage_manager_->get_spawn_controller().set_ship_position(&naus_[0].get_centre());

    // Inicialitzar timers de muerte per jugador
    itocado_per_jugador_[0] = 0.0f;
    itocado_per_jugador_[1] = 0.0f;

    // Initialize lives and game over state (independent lives per player)
    vides_per_jugador_[0] = Defaults::Game::STARTING_LIVES;
    vides_per_jugador_[1] = Defaults::Game::STARTING_LIVES;
    game_over_ = false;
    game_over_timer_ = 0.0f;

    // Initialize scores (separate per player)
    puntuacio_per_jugador_[0] = 0;
    puntuacio_per_jugador_[1] = 0;
    gestor_puntuacio_.reiniciar();

    // DEPRECATED: punt_spawn_ ja no s'usa, es calcula dinàmicament amb obtenir_punt_spawn(player_id)
    // const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
    // punt_spawn_.x = zona.x + zona.w * 0.5f;
    // punt_spawn_.y = zona.y + zona.h * Defaults::Game::INIT_HUD_SHIP_START_Y_RATIO;

    // Inicialitzar naus segons configuració (només jugadors actius)
    for (uint8_t i = 0; i < 2; i++) {
        bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;

        if (jugador_actiu) {
            // Jugador actiu: inicialitzar normalment
            Punt spawn_pos = obtenir_punt_spawn(i);
            naus_[i].inicialitzar(&spawn_pos, false);  // No invulnerability at start
            std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inicialitzat\n";
        } else {
            // Jugador inactiu: marcar com a mort permanent
            naus_[i].marcar_tocada();
            itocado_per_jugador_[i] = 999.0f;  // Valor sentinella (permanent inactiu)
            vides_per_jugador_[i] = 0;         // Sense vides
            std::cout << "[EscenaJoc] Jugador " << (i + 1) << " inactiu\n";
        }
    }

    // [MODIFIED] Initialize enemies as inactive (stage system will spawn them)
    for (auto& enemy : orni_) {
        enemy = Enemic(sdl_.obte_renderer());
        enemy.set_ship_position(&naus_[0].get_centre());  // Set ship reference (P1 for now)
        // DON'T call enemy.inicialitzar() here - stage system handles spawning
    }

    // Inicialitzar bales (now 6 instead of 3)
    for (auto& bala : bales_) {
        bala.inicialitzar();
    }

    // [ELIMINAT] Iniciar música de joc (ara es gestiona en stage_manager)
    // La música s'inicia quan es transiciona de INIT_HUD a LEVEL_START
    // Audio::get()->playMusic("game.ogg");

    // Reset flag de sons d'animació
    init_hud_rect_sound_played_ = false;
}

void EscenaJoc::actualitzar(float delta_time) {
    // Processar disparos (state-based, no event-based)
    if (!game_over_) {
        auto* input = Input::get();

        // Jugador 1 dispara (només si està actiu)
        if (config_partida_.jugador1_actiu) {
            if (input->checkActionPlayer1(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
                disparar_bala(0);
            }
        }

        // Jugador 2 dispara (només si està actiu)
        if (config_partida_.jugador2_actiu) {
            if (input->checkActionPlayer2(InputAction::SHOOT, Input::DO_NOT_ALLOW_REPEAT)) {
                disparar_bala(1);
            }
        }
    }

    // Check game over state first
    if (game_over_) {
        // Game over: only update timer, enemies, bullets, and debris
        game_over_timer_ -= delta_time;

        if (game_over_timer_ <= 0.0f) {
            // Aturar música de joc abans de tornar al títol
            Audio::get()->stopMusic();
            // Transició a pantalla de títol
            context_.canviar_escena(Escena::TITOL);
            GestorEscenes::actual = Escena::TITOL;
            return;
        }

        // Enemies and bullets continue moving during game over
        for (auto& enemy : orni_) {
            enemy.actualitzar(delta_time);
        }

        for (auto& bala : bales_) {
            bala.actualitzar(delta_time);
        }

        debris_manager_.actualitzar(delta_time);
        gestor_puntuacio_.actualitzar(delta_time);
        return;
    }

    // Check death sequence state for BOTH players
    bool algun_jugador_mort = false;
    for (uint8_t i = 0; i < 2; i++) {
        if (itocado_per_jugador_[i] > 0.0f && itocado_per_jugador_[i] < 999.0f) {
            algun_jugador_mort = true;
            // Death sequence active: update timer
            itocado_per_jugador_[i] += delta_time;

            // Check if death duration completed (only trigger ONCE using sentinel value)
            if (itocado_per_jugador_[i] >= Defaults::Game::DEATH_DURATION) {
                // *** PHASE 3: RESPAWN OR GAME OVER ***

                // Decrement lives for this player (only once)
                vides_per_jugador_[i]--;

                if (vides_per_jugador_[i] > 0) {
                    // Respawn ship en spawn position con invulnerabilidad
                    Punt spawn_pos = obtenir_punt_spawn(i);
                    naus_[i].inicialitzar(&spawn_pos, true);
                    itocado_per_jugador_[i] = 0.0f;
                } else {
                    // Player is permanently dead (out of lives)
                    // Set sentinel value to prevent re-entering this block
                    itocado_per_jugador_[i] = 999.0f;

                    // Check if ALL ACTIVE players are dead (game over)
                    bool p1_dead = !config_partida_.jugador1_actiu || vides_per_jugador_[0] <= 0;
                    bool p2_dead = !config_partida_.jugador2_actiu || vides_per_jugador_[1] <= 0;

                    if (p1_dead && p2_dead) {
                        game_over_ = true;
                        game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
                    }
                }
            }
        }
    }

    // If any player is dead, still update enemies/bullets/effects
    if (algun_jugador_mort) {
        // Enemies and bullets continue moving during death sequence
        for (auto& enemy : orni_) {
            enemy.actualitzar(delta_time);
        }

        for (auto& bala : bales_) {
            bala.actualitzar(delta_time);
        }

        debris_manager_.actualitzar(delta_time);
        gestor_puntuacio_.actualitzar(delta_time);

        // Don't return - allow alive players to continue playing
    }

    // *** STAGE SYSTEM STATE MACHINE ***

    StageSystem::EstatStage estat = stage_manager_->get_estat();

    switch (estat) {
        case StageSystem::EstatStage::INIT_HUD: {
            // Update stage manager timer (pot canviar l'estat!)
            stage_manager_->actualitzar(delta_time);

            // [FIX] Si l'estat ha canviat durant actualitzar(), sortir immediatament
            // per evitar recalcular la posició de la nau amb el nou timer
            if (stage_manager_->get_estat() != StageSystem::EstatStage::INIT_HUD) {
                break;
            }

            // Calcular global progress (0.0 al inicio → 1.0 al final)
            float global_progress = 1.0f - (stage_manager_->get_timer_transicio() / Defaults::Game::INIT_HUD_DURATION);
            global_progress = std::min(1.0f, global_progress);

            // [NEW] Calcular progress independiente para cada nave
            float ship1_progress = calcular_progress_rango(
                global_progress,
                Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
                Defaults::Game::INIT_HUD_SHIP1_RATIO_END);

            float ship2_progress = calcular_progress_rango(
                global_progress,
                Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
                Defaults::Game::INIT_HUD_SHIP2_RATIO_END);

            // [MODIFICAT] Animar AMBAS naus con sus progress respectivos
            if (config_partida_.jugador1_actiu && ship1_progress < 1.0f) {
                Punt pos_p1 = calcular_posicio_nau_init_hud(ship1_progress, 0);
                naus_[0].set_centre(pos_p1);
            }

            if (config_partida_.jugador2_actiu && ship2_progress < 1.0f) {
                Punt pos_p2 = calcular_posicio_nau_init_hud(ship2_progress, 1);
                naus_[1].set_centre(pos_p2);
            }

            // Una vegada l'animació acaba, permetre control normal
            // però mantenir la posició inicial especial fins LEVEL_START

            break;
        }

        case StageSystem::EstatStage::LEVEL_START: {
            // [DEBUG] Log entrada a LEVEL_START
            static bool first_entry = true;
            if (first_entry) {
                std::cout << "[LEVEL_START] ENTERED with P1 pos.y=" << naus_[0].get_centre().y << std::endl;
                first_entry = false;
            }

            // Update countdown timer
            stage_manager_->actualitzar(delta_time);

            // [NEW] Allow both ships movement and shooting during intro
            for (uint8_t i = 0; i < 2; i++) {
                bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
                if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {  // Only active, alive players
                    naus_[i].processar_input(delta_time, i);
                    naus_[i].actualitzar(delta_time);
                }
            }

            // [NEW] Update bullets
            for (auto& bala : bales_) {
                bala.actualitzar(delta_time);
            }

            // [NEW] Update debris
            debris_manager_.actualitzar(delta_time);
            break;
        }

        case StageSystem::EstatStage::PLAYING: {
            // [NEW] Update stage manager (spawns enemies, pause if BOTH dead)
            bool pausar_spawn = (itocado_per_jugador_[0] > 0.0f && itocado_per_jugador_[1] > 0.0f);
            stage_manager_->get_spawn_controller().actualitzar(delta_time, orni_, pausar_spawn);

            // [NEW] Check stage completion (only when at least one player alive)
            bool algun_jugador_viu = (itocado_per_jugador_[0] == 0.0f || itocado_per_jugador_[1] == 0.0f);
            if (algun_jugador_viu) {
                auto& spawn_ctrl = stage_manager_->get_spawn_controller();
                if (spawn_ctrl.tots_enemics_destruits(orni_)) {
                    stage_manager_->stage_completat();
                    Audio::get()->playSound(Defaults::Sound::GOOD_JOB_COMMANDER, Audio::Group::GAME);
                    break;
                }
            }

            // [EXISTING] Normal gameplay - update active players
            for (uint8_t i = 0; i < 2; i++) {
                bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
                if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {  // Only active, alive players
                    naus_[i].processar_input(delta_time, i);
                    naus_[i].actualitzar(delta_time);
                }
            }

            for (auto& enemy : orni_) {
                enemy.actualitzar(delta_time);
            }

            for (auto& bala : bales_) {
                bala.actualitzar(delta_time);
            }

            detectar_col·lisions_bales_enemics();
            detectar_col·lisio_naus_enemics();
            debris_manager_.actualitzar(delta_time);
            gestor_puntuacio_.actualitzar(delta_time);
            break;
        }

        case StageSystem::EstatStage::LEVEL_COMPLETED:
            // Update countdown timer
            stage_manager_->actualitzar(delta_time);

            // [NEW] Allow both ships movement and shooting during outro
            for (uint8_t i = 0; i < 2; i++) {
                bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
                if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {  // Only active, alive players
                    naus_[i].processar_input(delta_time, i);
                    naus_[i].actualitzar(delta_time);
                }
            }

            // [NEW] Update bullets (allow last shots to continue)
            for (auto& bala : bales_) {
                bala.actualitzar(delta_time);
            }

            // [NEW] Update debris (from last destroyed enemies)
            debris_manager_.actualitzar(delta_time);
            gestor_puntuacio_.actualitzar(delta_time);
            break;
    }
}

void EscenaJoc::dibuixar() {
    // Check game over state
    if (game_over_) {
        // Game over: draw enemies, bullets, debris, and "GAME OVER" text
        dibuixar_marges();

        for (const auto& enemy : orni_) {
            enemy.dibuixar();
        }

        for (const auto& bala : bales_) {
            bala.dibuixar();
        }

        debris_manager_.dibuixar();
        gestor_puntuacio_.dibuixar();

        // Draw centered "GAME OVER" text
        const std::string game_over_text = "GAME OVER";
        constexpr float escala = 2.0f;
        constexpr float spacing = 4.0f;

        float text_width = text_.get_text_width(game_over_text, escala, spacing);
        float text_height = text_.get_text_height(escala);

        const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
        float x = play_area.x + (play_area.w - text_width) / 2.0f;
        float y = play_area.y + (play_area.h - text_height) / 2.0f;

        text_.render(game_over_text, {x, y}, escala, spacing);

        dibuixar_marcador();
        return;
    }

    // [NEW] Stage state rendering
    StageSystem::EstatStage estat = stage_manager_->get_estat();

    switch (estat) {
        case StageSystem::EstatStage::INIT_HUD: {
            // Calcular progrés de cada animació independent
            float timer = stage_manager_->get_timer_transicio();
            float total_time = Defaults::Game::INIT_HUD_DURATION;
            float global_progress = 1.0f - (timer / total_time);

            // [NEW] Calcular progress independiente para cada elemento
            float rect_progress = calcular_progress_rango(
                global_progress,
                Defaults::Game::INIT_HUD_RECT_RATIO_INIT,
                Defaults::Game::INIT_HUD_RECT_RATIO_END);

            float score_progress = calcular_progress_rango(
                global_progress,
                Defaults::Game::INIT_HUD_SCORE_RATIO_INIT,
                Defaults::Game::INIT_HUD_SCORE_RATIO_END);

            float ship1_progress = calcular_progress_rango(
                global_progress,
                Defaults::Game::INIT_HUD_SHIP1_RATIO_INIT,
                Defaults::Game::INIT_HUD_SHIP1_RATIO_END);

            float ship2_progress = calcular_progress_rango(
                global_progress,
                Defaults::Game::INIT_HUD_SHIP2_RATIO_INIT,
                Defaults::Game::INIT_HUD_SHIP2_RATIO_END);

            // Dibuixar elements animats
            if (rect_progress > 0.0f) {
                // [NOU] Reproduir so quan comença l'animació del rectangle
                if (!init_hud_rect_sound_played_) {
                    Audio::get()->playSound(Defaults::Sound::LOGO, Audio::Group::GAME);
                    init_hud_rect_sound_played_ = true;
                }

                dibuixar_marges_animat(rect_progress);
            }

            if (score_progress > 0.0f) {
                dibuixar_marcador_animat(score_progress);
            }

            // [MODIFICAT] Dibuixar naus amb progress independent
            if (ship1_progress > 0.0f && config_partida_.jugador1_actiu && !naus_[0].esta_tocada()) {
                naus_[0].dibuixar();
            }

            if (ship2_progress > 0.0f && config_partida_.jugador2_actiu && !naus_[1].esta_tocada()) {
                naus_[1].dibuixar();
            }

            break;
        }

        case StageSystem::EstatStage::LEVEL_START:
            dibuixar_marges();
            // [NEW] Draw both ships if active and alive
            for (uint8_t i = 0; i < 2; i++) {
                bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
                if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {
                    naus_[i].dibuixar();
                }
            }

            // [NEW] Draw bullets
            for (const auto& bala : bales_) {
                bala.dibuixar();
            }

            // [NEW] Draw debris
            debris_manager_.dibuixar();
            gestor_puntuacio_.dibuixar();

            // [EXISTING] Draw intro message and score
            dibuixar_missatge_stage(stage_manager_->get_missatge_level_start());
            dibuixar_marcador();
            break;

        case StageSystem::EstatStage::PLAYING:
            dibuixar_marges();

            // [EXISTING] Normal rendering - active ships
            for (uint8_t i = 0; i < 2; i++) {
                bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
                if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {
                    naus_[i].dibuixar();
                }
            }

            for (const auto& enemy : orni_) {
                enemy.dibuixar();
            }

            for (const auto& bala : bales_) {
                bala.dibuixar();
            }

            debris_manager_.dibuixar();
            gestor_puntuacio_.dibuixar();
            dibuixar_marcador();
            break;

        case StageSystem::EstatStage::LEVEL_COMPLETED:
            dibuixar_marges();
            // [NEW] Draw both ships if active and alive
            for (uint8_t i = 0; i < 2; i++) {
                bool jugador_actiu = (i == 0) ? config_partida_.jugador1_actiu : config_partida_.jugador2_actiu;
                if (jugador_actiu && itocado_per_jugador_[i] == 0.0f) {
                    naus_[i].dibuixar();
                }
            }

            // [NEW] Draw bullets (allow last shots to be visible)
            for (const auto& bala : bales_) {
                bala.dibuixar();
            }

            // [NEW] Draw debris (from last destroyed enemies)
            debris_manager_.dibuixar();
            gestor_puntuacio_.dibuixar();

            // [EXISTING] Draw completion message and score
            dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_COMPLETED);
            dibuixar_marcador();
            break;
    }
}

void EscenaJoc::tocado(uint8_t player_id) {
    // Death sequence: 3 phases
    // Phase 1: First call (itocado_per_jugador_[player_id] == 0) - trigger explosion
    // Phase 2: Animation (0 < itocado_ < 3.0s) - debris animation
    // Phase 3: Respawn or game over (itocado_ >= 3.0s) - handled in actualitzar()

    if (itocado_per_jugador_[player_id] == 0.0f) {
        // *** PHASE 1: TRIGGER DEATH ***

        // Mark ship as dead (stops rendering and input)
        naus_[player_id].marcar_tocada();

        // Create ship explosion
        const Punt& ship_pos = naus_[player_id].get_centre();
        float ship_angle = naus_[player_id].get_angle();
        Punt vel_nau = naus_[player_id].get_velocitat_vector();
        // Reduir a 80% la velocitat heretada per la nau (més realista)
        Punt vel_nau_80 = {vel_nau.x * 0.8f, vel_nau.y * 0.8f};

        debris_manager_.explotar(
            naus_[player_id].get_forma(),               // Ship shape (3 lines)
            ship_pos,                                   // Center position
            ship_angle,                                 // Ship orientation
            1.0f,                                       // Normal scale
            Defaults::Physics::Debris::VELOCITAT_BASE,  // 80 px/s
            naus_[player_id].get_brightness(),          // Heredar brightness
            vel_nau_80,                                 // Heredar 80% velocitat
            0.0f,                                       // Nave: trayectorias rectas (sin drotacio)
            0.0f                                        // Sin herencia visual (rotación aleatoria)
        );

        Audio::get()->playSound(Defaults::Sound::EXPLOSION, Audio::Group::GAME);

        // Start death timer (non-zero to avoid re-triggering)
        itocado_per_jugador_[player_id] = 0.001f;
    }
    // Phase 2 is automatic (debris updates in actualitzar())
    // Phase 3 is handled in actualitzar() when itocado_per_jugador_ >= DEATH_DURATION
}

void EscenaJoc::dibuixar_marges() const {
    // Dibuixar rectangle de la zona de joc
    const SDL_FRect& zona = Defaults::Zones::PLAYAREA;

    // Coordenades dels cantons
    int x1 = static_cast<int>(zona.x);
    int y1 = static_cast<int>(zona.y);
    int x2 = static_cast<int>(zona.x + zona.w);
    int y2 = static_cast<int>(zona.y + zona.h);

    // 4 línies per formar el rectangle
    Rendering::linea(sdl_.obte_renderer(), x1, y1, x2, y1, true);  // Top
    Rendering::linea(sdl_.obte_renderer(), x1, y2, x2, y2, true);  // Bottom
    Rendering::linea(sdl_.obte_renderer(), x1, y1, x1, y2, true);  // Left
    Rendering::linea(sdl_.obte_renderer(), x2, y1, x2, y2, true);  // Right
}

void EscenaJoc::dibuixar_marcador() {
    // Construir text del marcador
    std::string text = construir_marcador();

    // Paràmetres de renderització
    const float escala = 0.85f;
    const float spacing = 0.0f;

    // Calcular dimensions del text
    float text_width = text_.get_text_width(text, escala, spacing);
    float text_height = text_.get_text_height(escala);

    // Centrat horitzontal dins de la zona del marcador
    float x = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f;

    // Centrat vertical dins de la zona del marcador
    float y = Defaults::Zones::SCOREBOARD.y +
        (Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f;

    // Renderitzar
    text_.render(text, {x, y}, escala, spacing);
}

void EscenaJoc::dibuixar_marges_animat(float progress) const {
    // Animació seqüencial del rectangle amb efecte de "pinzell"
    // Dos pinzells comencen al centre superior i baixen pels laterals

    const SDL_FRect& zona = Defaults::Zones::PLAYAREA;

    // Aplicar easing al progrés global
    float eased_progress = Easing::ease_out_quad(progress);

    // Coordenades del rectangle complet
    int x1 = static_cast<int>(zona.x);
    int y1 = static_cast<int>(zona.y);
    int x2 = static_cast<int>(zona.x + zona.w);
    int y2 = static_cast<int>(zona.y + zona.h);
    int cx = (x1 + x2) / 2;

    // Dividir en 3 fases de 33% cada una
    constexpr float PHASE_1_END = 0.33f;
    constexpr float PHASE_2_END = 0.66f;

    // --- FASE 1: Línies horitzontals superiors (0-33%) ---
    if (eased_progress > 0.0f) {
        float phase1_progress = std::min(eased_progress / PHASE_1_END, 1.0f);

        // Línia esquerra: creix des del centre cap a l'esquerra
        int x1_phase1 = static_cast<int>(cx - (cx - x1) * phase1_progress);
        Rendering::linea(sdl_.obte_renderer(), cx, y1, x1_phase1, y1, true);

        // Línia dreta: creix des del centre cap a la dreta
        int x2_phase1 = static_cast<int>(cx + (x2 - cx) * phase1_progress);
        Rendering::linea(sdl_.obte_renderer(), cx, y1, x2_phase1, y1, true);
    }

    // --- FASE 2: Línies verticals laterals (33-66%) ---
    if (eased_progress > PHASE_1_END) {
        float phase2_progress = std::min((eased_progress - PHASE_1_END) / (PHASE_2_END - PHASE_1_END), 1.0f);

        // Línia esquerra: creix des de dalt cap a baix
        int y2_phase2 = static_cast<int>(y1 + (y2 - y1) * phase2_progress);
        Rendering::linea(sdl_.obte_renderer(), x1, y1, x1, y2_phase2, true);

        // Línia dreta: creix des de dalt cap a baix
        Rendering::linea(sdl_.obte_renderer(), x2, y1, x2, y2_phase2, true);
    }

    // --- FASE 3: Línies horitzontals inferiors (66-100%) ---
    if (eased_progress > PHASE_2_END) {
        float phase3_progress = (eased_progress - PHASE_2_END) / (1.0f - PHASE_2_END);

        // Línia esquerra: creix des de l'esquerra cap al centre
        int x_left_phase3 = static_cast<int>(x1 + (cx - x1) * phase3_progress);
        Rendering::linea(sdl_.obte_renderer(), x1, y2, x_left_phase3, y2, true);

        // Línia dreta: creix des de la dreta cap al centre
        int x_right_phase3 = static_cast<int>(x2 - (x2 - cx) * phase3_progress);
        Rendering::linea(sdl_.obte_renderer(), x2, y2, x_right_phase3, y2, true);
    }
}

void EscenaJoc::dibuixar_marcador_animat(float progress) {
    // Animació del marcador pujant des de baix amb easing

    // Calcular progrés amb easing
    float eased_progress = Easing::ease_out_quad(progress);

    // Construir text
    std::string text = construir_marcador();

    // Paràmetres
    const float escala = 0.85f;
    const float spacing = 0.0f;

    // Calcular dimensions
    float text_width = text_.get_text_width(text, escala, spacing);
    float text_height = text_.get_text_height(escala);

    // Posició X final (centrada horitzontalment)
    float x_final = (Defaults::Zones::SCOREBOARD.w - text_width) / 2.0f;

    // Posició Y final (centrada verticalment en la zona de scoreboard)
    float y_final = Defaults::Zones::SCOREBOARD.y +
        (Defaults::Zones::SCOREBOARD.h - text_height) / 2.0f;

    // Posició Y inicial (offscreen, sota de la pantalla)
    float y_inicial = static_cast<float>(Defaults::Game::HEIGHT) + text_height;

    // Interpolació amb easing
    float y_animada = y_inicial + (y_final - y_inicial) * eased_progress;

    // Renderitzar en posició animada
    text_.render(text, {x_final, y_animada}, escala, spacing);
}

Punt EscenaJoc::calcular_posicio_nau_init_hud(float progress, uint8_t player_id) const {
    // Animació de la nau pujant des de baix amb easing
    // [MODIFICAT] Ambas naves usan ease_out_quad (desfase temporal via INIT/END)

    // Aplicar easing (uniforme para ambas naves)
    float eased_progress = Easing::ease_out_quad(progress);

    const SDL_FRect& zona = Defaults::Zones::PLAYAREA;

    // Calcular posició final segons jugador (reutilitza obtenir_punt_spawn)
    Punt spawn_final = obtenir_punt_spawn(player_id);
    float x_final = spawn_final.x;
    float y_final = spawn_final.y;

    // Y inicial: offscreen, 50px sota la zona de joc
    float y_inicial = zona.y + zona.h + 50.0f;

    // X no canvia (destí segons player_id)
    // Y interpola amb easing
    float y_animada = y_inicial + (y_final - y_inicial) * eased_progress;

    return {x_final, y_animada};
}

float EscenaJoc::calcular_progress_rango(float global_progress, float ratio_init, float ratio_end) const {
    // Convierte global_progress (0.0→1.0) a element_progress usando ventana [INIT, END]
    //
    // Casos:
    // - global_progress < INIT → 0.0 (no ha empezado)
    // - global_progress > END → 1.0 (completado)
    // - INIT ≤ global_progress ≤ END → interpola linealmente 0.0→1.0

    // Validación de parámetros (evita división por cero)
    if (ratio_init >= ratio_end) {
        return (global_progress >= ratio_end) ? 1.0f : 0.0f;
    }

    if (global_progress < ratio_init) {
        return 0.0f;
    }

    if (global_progress > ratio_end) {
        return 1.0f;
    }

    // Normalizar rango [INIT, END] a [0.0, 1.0]
    return (global_progress - ratio_init) / (ratio_end - ratio_init);
}

std::string EscenaJoc::construir_marcador() const {
    // Puntuació P1 (6 dígits) - mostrar zeros si inactiu
    std::string score_p1;
    std::string vides_p1;
    if (config_partida_.jugador1_actiu) {
        score_p1 = std::to_string(puntuacio_per_jugador_[0]);
        score_p1 = std::string(6 - std::min(6, static_cast<int>(score_p1.length())), '0') + score_p1;
        vides_p1 = (vides_per_jugador_[0] < 10)
            ? "0" + std::to_string(vides_per_jugador_[0])
            : std::to_string(vides_per_jugador_[0]);
    } else {
        score_p1 = "000000";
        vides_p1 = "00";
    }

    // Nivell (2 dígits)
    uint8_t stage_num = stage_manager_->get_stage_actual();
    std::string stage_str = (stage_num < 10) ? "0" + std::to_string(stage_num)
                                             : std::to_string(stage_num);

    // Puntuació P2 (6 dígits) - mostrar zeros si inactiu
    std::string score_p2;
    std::string vides_p2;
    if (config_partida_.jugador2_actiu) {
        score_p2 = std::to_string(puntuacio_per_jugador_[1]);
        score_p2 = std::string(6 - std::min(6, static_cast<int>(score_p2.length())), '0') + score_p2;
        vides_p2 = (vides_per_jugador_[1] < 10)
            ? "0" + std::to_string(vides_per_jugador_[1])
            : std::to_string(vides_per_jugador_[1]);
    } else {
        score_p2 = "000000";
        vides_p2 = "00";
    }

    // Format: "123456 03  LEVEL 01  654321 02"
    // Nota: dos espais entre seccions, mantenir ambdós slots sempre visibles
    return score_p1 + " " + vides_p1 + "  LEVEL " + stage_str + "  " + score_p2 + " " + vides_p2;
}

void EscenaJoc::detectar_col·lisions_bales_enemics() {
    // Constants amplificades per hitbox més generós (115%)
    constexpr float RADI_BALA = Defaults::Entities::BULLET_RADIUS;
    constexpr float RADI_ENEMIC = Defaults::Entities::ENEMY_RADIUS;
    constexpr float SUMA_RADIS = (RADI_BALA + RADI_ENEMIC) * 1.15f;  // 28.75 px
    constexpr float SUMA_RADIS_QUADRAT = SUMA_RADIS * SUMA_RADIS;    // 826.56

    // Velocitat d'explosió reduïda per efecte suau
    constexpr float VELOCITAT_EXPLOSIO = 80.0f;  // px/s (en lloc de 80.0f per defecte)

    // Iterar per totes les bales actives
    for (auto& bala : bales_) {
        if (!bala.esta_activa()) {
            continue;
        }

        const Punt& pos_bala = bala.get_centre();

        // Comprovar col·lisió amb tots els enemics actius
        for (auto& enemic : orni_) {
            if (!enemic.esta_actiu()) {
                continue;
            }

            const Punt& pos_enemic = enemic.get_centre();

            // Calcular distància quadrada (evita sqrt)
            float dx = pos_bala.x - pos_enemic.x;
            float dy = pos_bala.y - pos_enemic.y;
            float distancia_quadrada = dx * dx + dy * dy;

            // Comprovar col·lisió
            if (distancia_quadrada <= SUMA_RADIS_QUADRAT) {
                // *** COL·LISIÓ DETECTADA ***

                // 1. Calculate score for enemy type
                int punts = 0;
                switch (enemic.get_tipus()) {
                    case TipusEnemic::PENTAGON:
                        punts = Defaults::Enemies::Scoring::PENTAGON_SCORE;
                        break;
                    case TipusEnemic::QUADRAT:
                        punts = Defaults::Enemies::Scoring::QUADRAT_SCORE;
                        break;
                    case TipusEnemic::MOLINILLO:
                        punts = Defaults::Enemies::Scoring::MOLINILLO_SCORE;
                        break;
                }

                // 2. Add score to the player who shot it
                uint8_t owner_id = bala.get_owner_id();
                puntuacio_per_jugador_[owner_id] += punts;

                // 3. Create floating score number
                gestor_puntuacio_.crear(punts, pos_enemic);

                // 4. Destruir enemic (marca com inactiu)
                enemic.destruir();

                // 2. Crear explosió de fragments
                Punt vel_enemic = enemic.get_velocitat_vector();
                debris_manager_.explotar(
                    enemic.get_forma(),       // Forma vectorial del pentàgon
                    pos_enemic,               // Posició central
                    0.0f,                     // Angle (enemic té rotació interna)
                    1.0f,                     // Escala normal
                    VELOCITAT_EXPLOSIO,       // 50 px/s (explosió suau)
                    enemic.get_brightness(),  // Heredar brightness
                    vel_enemic,               // Heredar velocitat
                    enemic.get_drotacio(),    // Heredar velocitat angular (trayectorias curvas)
                    0.0f                      // Sin herencia visual (rotación aleatoria)
                );

                // 3. Desactivar bala
                bala.desactivar();

                // 4. Eixir del bucle intern (bala només destrueix 1 enemic)
                break;
            }
        }
    }
}

void EscenaJoc::detectar_col·lisio_naus_enemics() {
    // Generous collision detection (80% hitbox)
    constexpr float RADI_NAU = Defaults::Entities::SHIP_RADIUS;
    constexpr float RADI_ENEMIC = Defaults::Entities::ENEMY_RADIUS;
    constexpr float SUMA_RADIS =
        (RADI_NAU + RADI_ENEMIC) * Defaults::Game::COLLISION_SHIP_ENEMY_AMPLIFIER;
    constexpr float SUMA_RADIS_QUADRAT = SUMA_RADIS * SUMA_RADIS;

    // Check collision for BOTH players
    for (uint8_t i = 0; i < 2; i++) {
        // Skip collisions if player is dead or invulnerable
        if (itocado_per_jugador_[i] > 0.0f) continue;
        if (!naus_[i].esta_viva()) continue;
        if (naus_[i].es_invulnerable()) continue;

        const Punt& pos_nau = naus_[i].get_centre();

        // Check collision with all active enemies
        for (const auto& enemic : orni_) {
            if (!enemic.esta_actiu()) {
                continue;
            }

            // Skip collision if enemy is invulnerable
            if (enemic.es_invulnerable()) {
                continue;
            }

            const Punt& pos_enemic = enemic.get_centre();

            // Calculate squared distance (avoid sqrt)
            float dx = static_cast<float>(pos_nau.x - pos_enemic.x);
            float dy = static_cast<float>(pos_nau.y - pos_enemic.y);
            float distancia_quadrada = dx * dx + dy * dy;

            // Check collision
            if (distancia_quadrada <= SUMA_RADIS_QUADRAT) {
                tocado(i);  // Trigger death sequence for player i
                break;      // Only one collision per player per frame
            }
        }
    }
}

// [NEW] Stage system helper methods

void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
    constexpr float escala_base = 1.0f;
    constexpr float spacing = 2.0f;
    constexpr float max_width_ratio = 0.9f;  // 90% del ancho disponible

    const SDL_FRect& play_area = Defaults::Zones::PLAYAREA;
    const float max_width = play_area.w * max_width_ratio;  // 558px

    // ========== TYPEWRITER EFFECT (PARAMETRIZED) ==========
    // Get state-specific timing configuration
    float total_time;
    float typing_ratio;

    if (stage_manager_->get_estat() == StageSystem::EstatStage::LEVEL_START) {
        total_time = Defaults::Game::LEVEL_START_DURATION;
        typing_ratio = Defaults::Game::LEVEL_START_TYPING_RATIO;
    } else {  // LEVEL_COMPLETED
        total_time = Defaults::Game::LEVEL_COMPLETED_DURATION;
        typing_ratio = Defaults::Game::LEVEL_COMPLETED_TYPING_RATIO;
    }

    // Calculate progress from timer (0.0 at start → 1.0 at end)
    float remaining_time = stage_manager_->get_timer_transicio();
    float progress = 1.0f - (remaining_time / total_time);

    // Determine how many characters to show
    size_t visible_chars;

    if (typing_ratio > 0.0f && progress < typing_ratio) {
        // Typewriter phase: show partial text
        float typing_progress = progress / typing_ratio;  // Normalize to 0.0-1.0
        visible_chars = static_cast<size_t>(missatge.length() * typing_progress);
        if (visible_chars == 0 && progress > 0.0f) {
            visible_chars = 1;  // Show at least 1 character after first frame
        }
    } else {
        // Display phase: show complete text
        // (Either after typing phase, or immediately if typing_ratio == 0.0)
        visible_chars = missatge.length();
    }

    // Create partial message (substring for typewriter)
    std::string partial_message = missatge.substr(0, visible_chars);
    // ===================================================

    // Calculate text width at base scale (using FULL message for position calculation)
    float text_width_at_base = text_.get_text_width(missatge, escala_base, spacing);

    // Auto-scale if text exceeds max width
    float escala = (text_width_at_base <= max_width)
        ? escala_base
        : max_width / text_width_at_base;

    // Recalculate dimensions with final scale (using FULL message for centering)
    float full_text_width = text_.get_text_width(missatge, escala, spacing);
    float text_height = text_.get_text_height(escala);

    // Calculate position as if FULL text was there (for fixed position typewriter)
    float x = play_area.x + (play_area.w - full_text_width) / 2.0f;
    float y = play_area.y + (play_area.h * 0.25f) - (text_height / 2.0f);

    // Render only the partial message (typewriter effect)
    Punt pos = {x, y};
    text_.render(partial_message, pos, escala, spacing);
}

// ========================================
// Helper methods for 2-player support
// ========================================

Punt EscenaJoc::obtenir_punt_spawn(uint8_t player_id) const {
    const SDL_FRect& zona = Defaults::Zones::PLAYAREA;

    float x_ratio;
    if (config_partida_.es_un_jugador()) {
        // Un sol jugador: spawn al centre (50%)
        x_ratio = 0.5f;
    } else {
        // Dos jugadors: spawn a posicions separades
        x_ratio = (player_id == 0)
            ? Defaults::Game::P1_SPAWN_X_RATIO   // 33%
            : Defaults::Game::P2_SPAWN_X_RATIO;  // 67%
    }

    return {
        zona.x + zona.w * x_ratio,
        zona.y + zona.h * Defaults::Game::SPAWN_Y_RATIO};
}

void EscenaJoc::disparar_bala(uint8_t player_id) {
    // Verificar que el jugador está vivo
    if (itocado_per_jugador_[player_id] > 0.0f) return;
    if (!naus_[player_id].esta_viva()) return;

    // Calcular posición en la punta de la nave
    const Punt& ship_centre = naus_[player_id].get_centre();
    float ship_angle = naus_[player_id].get_angle();

    constexpr float LOCAL_TIP_X = 0.0f;
    constexpr float LOCAL_TIP_Y = -12.0f;
    float cos_a = std::cos(ship_angle);
    float sin_a = std::sin(ship_angle);
    float tip_x = LOCAL_TIP_X * cos_a - LOCAL_TIP_Y * sin_a + ship_centre.x;
    float tip_y = LOCAL_TIP_X * sin_a + LOCAL_TIP_Y * cos_a + ship_centre.y;
    Punt posicio_dispar = {tip_x, tip_y};

    // Buscar primera bala inactiva en el pool del jugador
    int start_idx = player_id * 3;  // P1=[0,1,2], P2=[3,4,5]
    for (int i = start_idx; i < start_idx + 3; i++) {
        if (!bales_[i].esta_activa()) {
            bales_[i].disparar(posicio_dispar, ship_angle, player_id);
            break;
        }
    }
}
