// 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/mouse.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()),
      nau_(sdl.obte_renderer()),
      itocado_(0),
      text_(sdl.obte_renderer()) {
    // Consumir opcions (preparació per MODE_DEMO futur)
    auto opcio = context_.consumir_opcio();
    (void)opcio;  // Suprimir warning de variable no usada

    // 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();

        // 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;
            }

            // Processament específic del joc (SPACE per disparar)
            processar_input(event);
        }

        // 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();

    // Inicialitzar estat de col·lisió
    itocado_ = 0;

    // Initialize lives and game over state
    num_vides_ = Defaults::Game::STARTING_LIVES;
    game_over_ = false;
    game_over_timer_ = 0.0f;

    // Set spawn point to center of play area
    Constants::obtenir_centre_zona(punt_spawn_.x, punt_spawn_.y);

    // Inicialitzar nau
    nau_.inicialitzar();

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

    // Inicialitzar bales
    for (auto& bala : bales_) {
        bala.inicialitzar();
    }

    // Iniciar música de joc (sense stopMusic, ja s'ha parat en destructor de TITOL)
    Audio::get()->playMusic("game.ogg");
}

void EscenaJoc::actualitzar(float delta_time) {
    // 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);
        return;
    }

    // Check death sequence state
    if (itocado_ > 0.0f) {
        // Death sequence active: update timer
        itocado_ += delta_time;

        // Check if death duration completed
        if (itocado_ >= Defaults::Game::DEATH_DURATION) {
            // *** PHASE 3: RESPAWN OR GAME OVER ***

            // Decrement lives
            num_vides_--;

            if (num_vides_ > 0) {
                // Respawn ship
                nau_.inicialitzar(&punt_spawn_);
                itocado_ = 0.0f;
            } else {
                // Game over
                game_over_ = true;
                game_over_timer_ = Defaults::Game::GAME_OVER_DURATION;
                itocado_ = 0.0f;
            }
        }

        // 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);
        return;
    }

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

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

    switch (estat) {
        case StageSystem::EstatStage::LEVEL_START:
            // Frozen gameplay, countdown timer only
            stage_manager_->actualitzar(delta_time);
            break;

        case StageSystem::EstatStage::PLAYING: {
            // [NEW] Update stage manager (spawns enemies, pass pause flag)
            bool pausar_spawn = (itocado_ > 0.0f);  // Pause during death animation
            stage_manager_->get_spawn_controller().actualitzar(delta_time, orni_, pausar_spawn);

            // [NEW] Check stage completion (only when not in death sequence)
            if (itocado_ == 0.0f) {
                auto& spawn_ctrl = stage_manager_->get_spawn_controller();
                if (spawn_ctrl.tots_enemics_destruits(orni_)) {
                    stage_manager_->stage_completat();
                    break;
                }
            }

            // [EXISTING] Normal gameplay
            nau_.processar_input(delta_time);
            nau_.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_nau_enemics();
            debris_manager_.actualitzar(delta_time);
            break;
        }

        case StageSystem::EstatStage::LEVEL_COMPLETED:
            // Frozen gameplay, countdown timer only
            stage_manager_->actualitzar(delta_time);
            break;
    }
}

void EscenaJoc::dibuixar() {
    // Draw borders (always visible)
    dibuixar_marges();

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

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

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

        debris_manager_.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::LEVEL_START:
            dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_START);
            dibuixar_marcador();
            break;

        case StageSystem::EstatStage::PLAYING:
            // [EXISTING] Normal rendering
            if (itocado_ == 0.0f) {
                nau_.dibuixar();
            }

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

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

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

        case StageSystem::EstatStage::LEVEL_COMPLETED:
            dibuixar_missatge_stage(StageSystem::Constants::MISSATGE_LEVEL_COMPLETED);
            dibuixar_marcador();
            break;
    }
}

void EscenaJoc::processar_input(const SDL_Event& event) {
    // Ignore ship controls during game over
    if (game_over_) {
        return;
    }

    // Ignore ship controls during death sequence
    if (itocado_ > 0.0f) {
        return;
    }

    // Processament d'input per events puntuals (no continus)
    // L'input continu (fletxes) es processa en actualitzar() amb
    // SDL_GetKeyboardState()

    if (event.type == SDL_EVENT_KEY_DOWN) {
        switch (event.key.key) {
            case SDLK_SPACE: {
                // No disparar si la nau està morta
                if (!nau_.esta_viva()) {
                    break;
                }

                // Disparar bala des del front de la nau
                // El ship.shp té el front a (0, -12) en coordenades locals

                // 1. Calcular posició del front de la nau
                constexpr float LOCAL_TIP_X = 0.0f;
                constexpr float LOCAL_TIP_Y = -12.0f;

                const Punt& ship_centre = nau_.get_centre();
                float ship_angle = nau_.get_angle();

                // Aplicar transformació: rotació + trasllació
                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};

                // 2. Buscar primera bala inactiva i disparar
                for (auto& bala : bales_) {
                    if (!bala.esta_activa()) {
                        bala.disparar(posicio_dispar, ship_angle);
                        break;  // Només una bala per polsació
                    }
                }
                break;
            }

            default:
                break;
        }
    }
}

void EscenaJoc::tocado() {
    // Death sequence: 3 phases
    // Phase 1: First call (itocado_ == 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_ == 0.0f) {
        // *** PHASE 1: TRIGGER DEATH ***

        // Mark ship as dead (stops rendering and input)
        nau_.marcar_tocada();

        // Create ship explosion
        const Punt& ship_pos = nau_.get_centre();
        float ship_angle = nau_.get_angle();

        debris_manager_.explotar(
            nau_.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
        );

        // Start death timer (non-zero to avoid re-triggering)
        itocado_ = 0.001f;
    }
    // Phase 2 is automatic (debris updates in actualitzar())
    // Phase 3 is handled in actualitzar() when itocado_ >= 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() {
    // [MODIFIED] Display current stage number from stage manager
    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);

    std::string text = "SCORE: 01000  LIFES: " + std::to_string(num_vides_) +
                       "  LEVEL: " + stage_str;

    // 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::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 = 50.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. Destruir enemic (marca com inactiu)
                enemic.destruir();

                // 2. Crear explosió de fragments
                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)
                );

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

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

void EscenaJoc::detectar_col·lisio_nau_enemics() {
    // Only check collisions if ship is alive
    if (!nau_.esta_viva()) {
        return;
    }

    // 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;

    const Punt& pos_nau = nau_.get_centre();

    // Check collision with all active enemies
    for (const auto& enemic : orni_) {
        if (!enemic.esta_actiu()) {
            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();  // Trigger death sequence
            return;    // Only one collision per frame
        }
    }
}

// [NEW] Stage system helper methods

void EscenaJoc::dibuixar_missatge_stage(const std::string& missatge) {
    constexpr float escala = 1.5f;
    constexpr float spacing = 3.0f;

    float text_width = text_.get_text_width(missatge, 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;

    Punt pos = {static_cast<float>(x), static_cast<float>(y)};
    text_.render(missatge, pos, escala, spacing);
}
