// shape_renderer.cpp - Implementació del renderitzat de formes
// © 2025 Port a C++20 amb SDL3

#include "core/rendering/shape_renderer.hpp"

#include <cmath>

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

namespace Rendering {

// Helper: aplicar rotació 3D a un punt 2D (assumeix Z=0)
static Punt apply_3d_rotation(float x, float y, const Rotation3D& rot) {
    float z = 0.0f;  // Tots els punts 2D comencen a Z=0

    // Pitch (rotació eix X): cabeceo arriba/baix
    float cos_pitch = std::cos(rot.pitch);
    float sin_pitch = std::sin(rot.pitch);
    float y1 = y * cos_pitch - z * sin_pitch;
    float z1 = y * sin_pitch + z * cos_pitch;

    // Yaw (rotació eix Y): guiñada esquerra/dreta
    float cos_yaw = std::cos(rot.yaw);
    float sin_yaw = std::sin(rot.yaw);
    float x2 = x * cos_yaw + z1 * sin_yaw;
    float z2 = -x * sin_yaw + z1 * cos_yaw;

    // Roll (rotació eix Z): alabeo lateral
    float cos_roll = std::cos(rot.roll);
    float sin_roll = std::sin(rot.roll);
    float x3 = x2 * cos_roll - y1 * sin_roll;
    float y3 = x2 * sin_roll + y1 * cos_roll;

    // Proyecció perspectiva (Z-divide simple)
    // Naus volen cap al punt de fuga (320, 240) a "infinit" (Z → +∞)
    // Z més gran = més lluny = més petit a pantalla
    constexpr float perspective_factor = 500.0f;
    float scale_factor = perspective_factor / (perspective_factor + z2);

    return {x3 * scale_factor, y3 * scale_factor};
}

// Helper: transformar un punt amb rotació, escala i trasllació
static Punt transform_point(const Punt& point, const Punt& shape_centre, const Punt& posicio, float angle, float escala, const Rotation3D* rotation_3d) {
    // 1. Centrar el punt respecte al centre de la forma
    float centered_x = point.x - shape_centre.x;
    float centered_y = point.y - shape_centre.y;

    // 2. Aplicar rotació 3D (si es proporciona)
    if (rotation_3d && rotation_3d->has_rotation()) {
        Punt rotated_3d = apply_3d_rotation(centered_x, centered_y, *rotation_3d);
        centered_x = rotated_3d.x;
        centered_y = rotated_3d.y;
    }

    // 3. Aplicar escala al punt (després de rotació 3D)
    float scaled_x = centered_x * escala;
    float scaled_y = centered_y * escala;

    // 4. Aplicar rotació 2D (Z-axis, tradicional)
    // IMPORTANT: En el sistema original, angle=0 apunta AMUNT (no dreta)
    // Per això usem (angle - PI/2) per compensar
    // Però aquí angle ja ve en el sistema correcte del joc
    float cos_a = std::cos(angle);
    float sin_a = std::sin(angle);

    float rotated_x = scaled_x * cos_a - scaled_y * sin_a;
    float rotated_y = scaled_x * sin_a + scaled_y * cos_a;

    // 5. Aplicar trasllació a posició mundial
    return {rotated_x + posicio.x, rotated_y + posicio.y};
}

void render_shape(SDL_Renderer* renderer,
    const std::shared_ptr<Graphics::Shape>& shape,
    const Punt& posicio,
    float angle,
    float escala,
    bool dibuixar,
    float progress,
    float brightness,
    const Rotation3D* rotation_3d) {
    // Verificar que la forma és vàlida
    if (!shape || !shape->es_valida()) {
        return;
    }

    // Si progress < 1.0, no dibuixar (tot o res)
    if (progress < 1.0f) {
        return;
    }

    // Obtenir el centre de la forma per a transformacions
    const Punt& shape_centre = shape->get_centre();

    // Iterar sobre totes les primitives
    for (const auto& primitive : shape->get_primitives()) {
        if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
            // POLYLINE: connectar punts consecutius
            for (size_t i = 0; i < primitive.points.size() - 1; i++) {
                Punt p1 = transform_point(primitive.points[i], shape_centre, posicio, angle, escala, rotation_3d);
                Punt p2 = transform_point(primitive.points[i + 1], shape_centre, posicio, angle, escala, rotation_3d);

                linea(renderer, static_cast<int>(p1.x), static_cast<int>(p1.y), static_cast<int>(p2.x), static_cast<int>(p2.y), dibuixar, brightness);
            }
        } else {  // PrimitiveType::LINE
            // LINE: exactament 2 punts
            if (primitive.points.size() >= 2) {
                Punt p1 = transform_point(primitive.points[0], shape_centre, posicio, angle, escala, rotation_3d);
                Punt p2 = transform_point(primitive.points[1], shape_centre, posicio, angle, escala, rotation_3d);

                linea(renderer, static_cast<int>(p1.x), static_cast<int>(p1.y), static_cast<int>(p2.x), static_cast<int>(p2.y), dibuixar, brightness);
            }
        }
    }
}

}  // namespace Rendering
