// starfield.cpp - Implementació del sistema d'estrelles de fons
// © 2025 Orni Attack

#include "core/graphics/starfield.hpp"

#include <cmath>
#include <cstdlib>
#include <iostream>

#include "core/defaults.hpp"
#include "core/graphics/shape_loader.hpp"
#include "core/rendering/shape_renderer.hpp"

namespace Graphics {

// Constructor
Starfield::Starfield(SDL_Renderer* renderer,
    const Punt& punt_fuga,
    const SDL_FRect& area,
    int densitat)
    : renderer_(renderer),
      punt_fuga_(punt_fuga),
      area_(area),
      densitat_(densitat) {
    // Carregar forma d'estrella amb ShapeLoader
    shape_estrella_ = ShapeLoader::load("star.shp");

    if (!shape_estrella_ || !shape_estrella_->es_valida()) {
        std::cerr << "ERROR: No s'ha pogut carregar star.shp" << std::endl;
        return;
    }

    // Configurar 3 capes amb diferents velocitats i escales
    // Capa 0: Fons llunyà (lenta, petita)
    capes_.push_back({20.0F, 0.3F, 0.8F, densitat / 3});

    // Capa 1: Profunditat mitjana
    capes_.push_back({40.0F, 0.5F, 1.2F, densitat / 3});

    // Capa 2: Primer pla (ràpida, gran)
    capes_.push_back({80.0F, 0.8F, 2.0F, densitat / 3});

    // Calcular radi màxim (distància del centre al racó més llunyà)
    float dx = std::max(punt_fuga_.x, area_.w - punt_fuga_.x);
    float dy = std::max(punt_fuga_.y, area_.h - punt_fuga_.y);
    radi_max_ = std::sqrt((dx * dx) + (dy * dy));

    // Inicialitzar estrelles amb posicions distribuïdes (pre-omplir pantalla)
    for (int capa_idx = 0; capa_idx < 3; capa_idx++) {
        int num = capes_[capa_idx].num_estrelles;
        for (int i = 0; i < num; i++) {
            Estrella estrella;
            estrella.capa = capa_idx;

            // Angle aleatori
            estrella.angle = (static_cast<float>(rand()) / RAND_MAX) * 2.0F * Defaults::Math::PI;

            // Distància aleatòria (0.0 a 1.0) per omplir tota la pantalla
            estrella.distancia_centre = static_cast<float>(rand()) / RAND_MAX;

            // Calcular posició des de la distància
            float radi = estrella.distancia_centre * radi_max_;
            estrella.posicio.x = punt_fuga_.x + (radi * std::cos(estrella.angle));
            estrella.posicio.y = punt_fuga_.y + (radi * std::sin(estrella.angle));

            estrelles_.push_back(estrella);
        }
    }
}

// Inicialitzar una estrella (nova o regenerada)
void Starfield::inicialitzar_estrella(Estrella& estrella) const {
    // Angle aleatori des del punt de fuga cap a fora
    estrella.angle = (static_cast<float>(rand()) / RAND_MAX) * 2.0F * Defaults::Math::PI;

    // Distància inicial petita (5% del radi màxim) - neix prop del centre
    estrella.distancia_centre = 0.05F;

    // Posició inicial: molt prop del punt de fuga
    float radi = estrella.distancia_centre * radi_max_;
    estrella.posicio.x = punt_fuga_.x + (radi * std::cos(estrella.angle));
    estrella.posicio.y = punt_fuga_.y + (radi * std::sin(estrella.angle));
}

// Verificar si una estrella està fora de l'àrea
bool Starfield::fora_area(const Estrella& estrella) const {
    return (estrella.posicio.x < area_.x ||
        estrella.posicio.x > area_.x + area_.w ||
        estrella.posicio.y < area_.y ||
        estrella.posicio.y > area_.y + area_.h);
}

// Calcular escala dinàmica segons distància del centre
float Starfield::calcular_escala(const Estrella& estrella) const {
    const CapaConfig& capa = capes_[estrella.capa];

    // Interpolació lineal basada en distància del centre
    // distancia_centre: 0.0 (centre) → 1.0 (vora)
    return capa.escala_min +
        ((capa.escala_max - capa.escala_min) * estrella.distancia_centre);
}

// Calcular brightness dinàmica segons distància del centre
float Starfield::calcular_brightness(const Estrella& estrella) const {
    // Interpolació lineal: estrelles properes (vora) més brillants
    // distancia_centre: 0.0 (centre, llunyanes) → 1.0 (vora, properes)
    float brightness_base = Defaults::Brightness::STARFIELD_MIN +
        ((Defaults::Brightness::STARFIELD_MAX - Defaults::Brightness::STARFIELD_MIN) *
            estrella.distancia_centre);

    // Aplicar multiplicador i limitar a 1.0
    return std::min(1.0F, brightness_base * multiplicador_brightness_);
}

// Actualitzar posicions de les estrelles
void Starfield::actualitzar(float delta_time) {
    for (auto& estrella : estrelles_) {
        // Obtenir configuració de la capa
        const CapaConfig& capa = capes_[estrella.capa];

        // Moure cap a fora des del centre
        float velocitat = capa.velocitat_base;
        float dx = velocitat * std::cos(estrella.angle) * delta_time;
        float dy = velocitat * std::sin(estrella.angle) * delta_time;

        estrella.posicio.x += dx;
        estrella.posicio.y += dy;

        // Actualitzar distància del centre
        float dx_centre = estrella.posicio.x - punt_fuga_.x;
        float dy_centre = estrella.posicio.y - punt_fuga_.y;
        float dist_px = std::sqrt((dx_centre * dx_centre) + (dy_centre * dy_centre));
        estrella.distancia_centre = dist_px / radi_max_;

        // Si ha sortit de l'àrea, regenerar-la
        if (fora_area(estrella)) {
            inicialitzar_estrella(estrella);
        }
    }
}

// Establir multiplicador de brightness
void Starfield::set_brightness(float multiplier) {
    multiplicador_brightness_ = std::max(0.0F, multiplier);  // Evitar valors negatius
}

// Dibuixar totes les estrelles
void Starfield::dibuixar() {
    if (!shape_estrella_->es_valida()) {
        return;
    }

    for (const auto& estrella : estrelles_) {
        // Calcular escala i brightness dinàmicament
        float escala = calcular_escala(estrella);
        float brightness = calcular_brightness(estrella);

        // Renderitzar estrella sense rotació
        Rendering::render_shape(
            renderer_,
            shape_estrella_,
            estrella.posicio,
            0.0F,       // angle (les estrelles no giren)
            escala,     // escala dinàmica
            true,       // dibuixar
            1.0F,       // progress (sempre visible)
            brightness  // brightness dinàmica
        );
    }
}

}  // namespace Graphics
