// spawn_controller.cpp - Implementació del controlador de spawn
// © 2026 JailDesigner

#include "spawn_controller.hpp"

#include <algorithm>
#include <array>
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <utility>

#include "core/types.hpp"
#include "game/entities/enemy.hpp"
#include "stage_config.hpp"

namespace StageSystem {

SpawnController::SpawnController() = default;

void SpawnController::configure(const StageConfig* config) {
    config_ = config;
}

void SpawnController::start() {
    if (config_ == nullptr) {
        std::cerr << "[SpawnController] Error: config_ es null" << '\n';
        return;
    }

    reset();
    generateSpawnEvents();

    std::cout << "[SpawnController] Stage " << static_cast<int>(config_->stage_id)
              << ": generats " << spawn_queue_.size() << " spawn events" << '\n';
}

void SpawnController::reset() {
    spawn_queue_.clear();
    temps_transcorregut_ = 0.0F;
    index_spawn_actual_ = 0;
}

void SpawnController::update(float delta_time, std::array<Enemy, 15>& orni_array, bool pausar) {
    if ((config_ == nullptr) || spawn_queue_.empty()) {
        return;
    }

    // Increment timer only when not paused
    if (!pausar) {
        temps_transcorregut_ += delta_time;
    }

    // Process spawn events
    while (index_spawn_actual_ < spawn_queue_.size()) {
        SpawnEvent& event = spawn_queue_[index_spawn_actual_];

        if (event.spawnejat) {
            index_spawn_actual_++;
            continue;
        }

        if (temps_transcorregut_ >= event.temps_spawn) {
            // Find first inactive enemy
            for (auto& enemy : orni_array) {
                if (!enemy.isActive()) {
                    spawnEnemy(enemy, event.type, ship_position_);
                    event.spawnejat = true;
                    index_spawn_actual_++;
                    break;
                }
            }

            // If no slot available, try next frame
            if (!event.spawnejat) {
                break;
            }
        } else {
            // Not yet time for this spawn
            break;
        }
    }
}

auto SpawnController::allEnemiesSpawned() const -> bool {
    return index_spawn_actual_ >= spawn_queue_.size();
}

auto SpawnController::allEnemiesDestroyed(const std::array<Enemy, 15>& orni_array) const -> bool {
    if (!allEnemiesSpawned()) {
        return false;
    }
    return std::ranges::all_of(orni_array, [](const Enemy& enemy) { return !enemy.isActive(); });
}

auto SpawnController::getAliveEnemyCount(const std::array<Enemy, 15>& orni_array) -> uint8_t {
    uint8_t count = 0;
    for (const auto& enemy : orni_array) {
        if (enemy.isActive()) {
            count++;
        }
    }
    return count;
}

auto SpawnController::countSpawnedEnemies() const -> uint8_t {
    return static_cast<uint8_t>(index_spawn_actual_);
}

void SpawnController::generateSpawnEvents() {
    if (config_ == nullptr) {
        return;
    }

    for (uint8_t i = 0; i < config_->total_enemies; i++) {
        float spawn_time = config_->config_spawn.delay_inicial +
            (i * config_->config_spawn.interval_spawn);

        EnemyType type = selectRandomType();

        spawn_queue_.push_back({spawn_time, type, false});
    }
}

auto SpawnController::selectRandomType() const -> EnemyType {
    if (config_ == nullptr) {
        return EnemyType::PENTAGON;
    }

    // Weighted random selection based on distribution
    int rand_val = std::rand() % 100;

    if (std::cmp_less(rand_val, config_->distribucio.pentagon)) {
        return EnemyType::PENTAGON;
    }
    if (rand_val < config_->distribucio.pentagon + config_->distribucio.cuadrado) {
        return EnemyType::QUADRAT;
    }
    return EnemyType::MOLINILLO;
}

void SpawnController::spawnEnemy(Enemy& enemy, EnemyType type, const Vec2* ship_pos) {
    // Initialize enemy (with safe spawn if ship_pos provided)
    enemy.init(type, ship_pos);

    // Apply difficulty multipliers
    applyMultipliers(enemy);
}

void SpawnController::applyMultipliers(Enemy& enemy) const {
    if (config_ == nullptr) {
        return;
    }

    // Apply velocity multiplier
    float base_vel = enemy.getBaseVelocity();
    enemy.setVelocity(base_vel * config_->multiplicadors.velocity);

    // Apply rotation multiplier
    float base_rot = enemy.getBaseRotation();
    enemy.setRotation(base_rot * config_->multiplicadors.rotation);

    // Apply tracking strength (only affects QUADRAT)
    enemy.setTrackingStrength(config_->multiplicadors.tracking_strength);
}

}  // namespace StageSystem
