// starfield_parallax.cpp - Implementació del starfield 2D amb parallax
// © 2026 JailDesigner

#include "core/graphics/starfield_parallax.hpp"

#include <cstdlib>

#include "core/defaults.hpp"
#include "core/rendering/line_renderer.hpp"

namespace Graphics {

    namespace {

        auto randUniform(float min_v, float max_v) -> float {
            const float NORM = static_cast<float>(std::rand()) / static_cast<float>(RAND_MAX);
            return min_v + (NORM * (max_v - min_v));
        }

    }  // namespace

    StarfieldParallax::StarfieldParallax(Rendering::Renderer* renderer)
        : renderer_(renderer) {
        buildStars();
    }

    void StarfieldParallax::buildStars() {
        const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
        const float MIN_X = zona.x;
        const float MAX_X = zona.x + zona.w;
        const float MIN_Y = zona.y;
        const float MAX_Y = zona.y + zona.h;

        // Color únic per a totes les estrelles: el mateix blanc-blau gel
        // del starfield del títol (Defaults::Title::Colors::STARFIELD).
        const auto FILL_LAYER = [&](int layer, int count, int& idx) {
            for (int i = 0; i < count; i++) {
                stars_[idx++] = Star{
                    .x = randUniform(MIN_X, MAX_X),
                    .y = randUniform(MIN_Y, MAX_Y),
                    .layer = layer,
                    .color = Defaults::Title::Colors::STARFIELD};
            }
        };

        int idx = 0;
        FILL_LAYER(0, Defaults::StarfieldParallax::Far::COUNT, idx);
        FILL_LAYER(1, Defaults::StarfieldParallax::Mid::COUNT, idx);
        FILL_LAYER(2, Defaults::StarfieldParallax::Near::COUNT, idx);
    }

    auto StarfieldParallax::layerBrightness(int layer) -> float {
        switch (layer) {
            case 0:
                return Defaults::StarfieldParallax::Far::BRIGHTNESS;
            case 1:
                return Defaults::StarfieldParallax::Mid::BRIGHTNESS;
            case 2:
                return Defaults::StarfieldParallax::Near::BRIGHTNESS;
            default:
                return 0.0F;
        }
    }

    auto StarfieldParallax::layerParallax(int layer) -> float {
        switch (layer) {
            case 0:
                return Defaults::StarfieldParallax::Far::PARALLAX_FACTOR;
            case 1:
                return Defaults::StarfieldParallax::Mid::PARALLAX_FACTOR;
            case 2:
                return Defaults::StarfieldParallax::Near::PARALLAX_FACTOR;
            default:
                return 0.0F;
        }
    }

    auto StarfieldParallax::layerSize(int layer) -> int {
        switch (layer) {
            case 0:
                return Defaults::StarfieldParallax::Far::SIZE_PX;
            case 1:
                return Defaults::StarfieldParallax::Mid::SIZE_PX;
            case 2:
                return Defaults::StarfieldParallax::Near::SIZE_PX;
            default:
                return 1;
        }
    }

    void StarfieldParallax::update(float delta_time, Vec2 world_velocity) {
        const SDL_FRect& zona = Defaults::Zones::PLAYAREA;
        const float MIN_X = zona.x;
        const float MAX_X = zona.x + zona.w;
        const float MIN_Y = zona.y;
        const float MAX_Y = zona.y + zona.h;
        const float W = zona.w;
        const float H = zona.h;

        for (auto& star : stars_) {
            const float FACTOR = layerParallax(star.layer);
            star.x += world_velocity.x * FACTOR * delta_time;
            star.y += world_velocity.y * FACTOR * delta_time;

            // Wraparound (PLAYAREA torica).
            while (star.x < MIN_X) {
                star.x += W;
            }
            while (star.x > MAX_X) {
                star.x -= W;
            }
            while (star.y < MIN_Y) {
                star.y += H;
            }
            while (star.y > MAX_Y) {
                star.y -= H;
            }
        }
    }

    void StarfieldParallax::draw() const {
        for (const auto& star : stars_) {
            const float B = layerBrightness(star.layer);
            const int SIZE = layerSize(star.layer);
            const int X = static_cast<int>(star.x);
            const int Y = static_cast<int>(star.y);

            if (SIZE <= 1) {
                // Punt d'1 px: línia degenerada horitzontal de 1 px.
                Rendering::linea(renderer_, X, Y, X + 1, Y, B, 0.0F, star.color);
            } else {
                // Creu "+" amb extensió HALF des del centre en cada direcció.
                const int HALF = SIZE - 1;  // SIZE=2 → ±1 (creu 3x3); SIZE=3 → ±2 (creu 5x5)
                Rendering::linea(renderer_, X - HALF, Y, X + HALF + 1, Y, B, 0.0F, star.color);
                Rendering::linea(renderer_, X, Y - HALF, X, Y + HALF + 1, B, 0.0F, star.color);
            }
        }
    }

}  // namespace Graphics
