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

#include "core/rendering/shape_renderer.hpp"

#include <cmath>

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

namespace Rendering {

// Helper: aplicar rotación 3D a un point 2D (assumeix Z=0)
static Vec2 apply_3d_rotation(float x, float y, const Rotation3D& rot) {
    float z = 0.0F;  // Todos los points 2D comencen a Z=0

    // Pitch (rotación 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ón eix Y): guiñada izquierda/derecha
    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ón 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)
    // Naves quieren hacia el point de fuga (320, 240) a "infinit" (Z → +∞)
    // Z més grande = més lluny = més pequeño a pantalla
    constexpr float perspective_factor = 500.0F;
    float scale_factor = perspective_factor / (perspective_factor + z2);

    return {.x = x3 * scale_factor, .y = y3 * scale_factor};
}

// Helper: transformar un point con rotación, scale i traslación
static Vec2 transform_point(const Vec2& point, const Vec2& shape_centre, const Vec2& position, float angle, float scale, const Rotation3D* rotation_3d) {
    // 1. Centrar el point respecte al centro de la shape
    float centered_x = point.x - shape_centre.x;
    float centered_y = point.y - shape_centre.y;

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

    // 3. Aplicar scale al point (después de rotación 3D)
    float scaled_x = centered_x * scale;
    float scaled_y = centered_y * scale;

    // 4. Aplicar rotación 2D (Z-axis, tradicional)
    // IMPORTANT: En el sistema original, angle=0 apunta AMUNT (no derecha)
    // Per això usem (angle - PI/2) per compensar
    // Pero aquí angle ya ve en el sistema correcte del juego
    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 traslación a posición mundial
    return {.x = rotated_x + position.x, .y = rotated_y + position.y};
}

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

    const Vec2& SHAPE_CENTRE = shape->getCenter();

    for (const auto& primitive : shape->get_primitives()) {
        if (primitive.type == Graphics::PrimitiveType::POLYLINE) {
            // POLYLINE: conectar puntos consecutivos.
            for (size_t i = 0; i < primitive.points.size() - 1; i++) {
                const Vec2 P1 = transform_point(primitive.points[i], SHAPE_CENTRE, position, angle, scale, rotation_3d);
                const Vec2 P2 = transform_point(primitive.points[i + 1], SHAPE_CENTRE, position, angle, scale, rotation_3d);
                linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y),
                      static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color);
            }
        } else if (primitive.points.size() >= 2) {  // LINE
            const Vec2 P1 = transform_point(primitive.points[0], SHAPE_CENTRE, position, angle, scale, rotation_3d);
            const Vec2 P2 = transform_point(primitive.points[1], SHAPE_CENTRE, position, angle, scale, rotation_3d);
            linea(renderer, static_cast<int>(P1.x), static_cast<int>(P1.y),
                  static_cast<int>(P2.x), static_cast<int>(P2.y), brightness, 0.0F, color);
        }
    }
}

}  // namespace Rendering
