// shape_renderer.cpp - Implementació del renderizado de formes
// © 2026 JailDesigner

#include "core/rendering/shape_renderer.hpp"

#include <algorithm>
#include <cmath>

#include "core/defaults/effects.hpp"
#include "core/graphics/shape.hpp"
#include "core/rendering/line_renderer.hpp"

namespace Rendering {

    // Helper: transformar un point con rotación, scale i traslación
    static auto transformPoint(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale) -> Vec2 {
        const float CENTERED_X = point.x - shape_centre.x;
        const float CENTERED_Y = point.y - shape_centre.y;

        const float SCALED_X = CENTERED_X * scale;
        const float SCALED_Y = CENTERED_Y * scale;

        const float COS_A = std::cos(angle);
        const float SIN_A = std::sin(angle);

        const float ROTATED_X = (SCALED_X * COS_A) - (SCALED_Y * SIN_A);
        const float ROTATED_Y = (SCALED_X * SIN_A) + (SCALED_Y * COS_A);

        return {.x = ROTATED_X + position.x, .y = ROTATED_Y + position.y};
    }

    // Una passada de renderitzat: itera primitives de la shape i emet línies
    // amb el thickness/alpha indicats. Es crida N vegades en glow mode (una
    // per pass de halo + core), o 1 vegada quan glow=false.
    static void renderSinglePass(Rendering::Renderer* renderer,
        const std::shared_ptr<Graphics::Shape>& shape,
        const Vec2& position,
        float angle,
        float scale,
        float brightness,
        SDL_Color color,
        float thickness,
        float alpha) {
        const Vec2& shape_centre = shape->getCenter();

        // Petita extensió a línies gruixudes per tapar forats entre segments.
        // A vèrtex aguts (~108°) un valor alt produeix "espigues" — 15%.
        const float EFFECTIVE_T = (thickness > 0.0F) ? thickness : getLineThickness();
        const float EXTEND = (EFFECTIVE_T > 2.0F) ? (EFFECTIVE_T * 0.15F) : 0.0F;

        for (const auto& primitive : shape->getPrimitives()) {
            if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
                for (size_t i = 0; i < primitive.points.size() - 1; i++) {
                    Vec2 p1 = transformPoint(primitive.points[i], shape_centre, position, angle, scale);
                    Vec2 p2 = transformPoint(primitive.points[i + 1], shape_centre, position, angle, scale);
                    if (EXTEND > 0.0F) {
                        const float DX = p2.x - p1.x;
                        const float DY = p2.y - p1.y;
                        const float LEN = std::sqrt((DX * DX) + (DY * DY));
                        if (LEN > 1e-6F) {
                            const float UX = (DX / LEN) * EXTEND;
                            const float UY = (DY / LEN) * EXTEND;
                            p1.x -= UX;
                            p1.y -= UY;
                            p2.x += UX;
                            p2.y += UY;
                        }
                    }
                    linea(renderer, static_cast<int>(p1.x), static_cast<int>(p1.y), static_cast<int>(p2.x), static_cast<int>(p2.y), brightness, thickness, color, alpha);
                }
            } else if (primitive.points.size() >= 2) {  // LINE
                const Vec2 P1 = transformPoint(primitive.points[0], shape_centre, position, angle, scale);
                const Vec2 P2 = transformPoint(primitive.points[1], shape_centre, position, angle, scale);
                linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y), static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, thickness, color, alpha);
            }
        }
    }

    void renderShape(Rendering::Renderer* renderer,
        const std::shared_ptr<Graphics::Shape>& shape,
        const Vec2& position,
        float angle,
        float scale,
        float progress,
        float brightness,
        SDL_Color color,
        float thickness,
        float alpha,
        bool glow) {
        if (!shape || !shape->isValid()) {
            return;
        }
        if (progress < 1.0F) {
            return;
        }

        if (!glow) {
            renderSinglePass(renderer, shape, position, angle, scale, brightness, color, thickness, alpha);
            return;
        }

        // Glow: multi-pass amb halos translúcids proporcionals al tamany de
        // la shape. Cada pass amb thickness_ratio<0 usa el thickness/alpha
        // que ha passat el caller (és el "core" / línia real). Saturem la
        // mida de referència a MAX_REFERENCE_RADIUS perquè shapes molt
        // grans (logos) no tinguin halo desproporcionat.
        const float RAW_REF = shape->getBoundingRadius() * scale;
        const float REFERENCE_SIZE = std::min(RAW_REF, Defaults::FX::Glow::MAX_REFERENCE_RADIUS);
        for (const auto& pass : Defaults::FX::Glow::PASSES) {
            float pass_thickness = thickness;
            float pass_alpha = alpha;
            if (pass.thickness_ratio > 0.0F) {
                pass_thickness = REFERENCE_SIZE * pass.thickness_ratio;
                pass_alpha = pass.alpha * alpha;  // respecta el master alpha del caller
            }
            renderSinglePass(renderer, shape, position, angle, scale, brightness, color, pass_thickness, pass_alpha);
        }
    }

}  // namespace Rendering
