#include "icosahedron_shape.hpp"
#include "defines.hpp"
#include <cmath>
#include <vector>

void IcosahedronShape::generatePoints(int num_points, float screen_width, float screen_height) {
    num_points_ = num_points;
    radius_ = screen_height * ICOSAHEDRON_RADIUS_FACTOR;
    // Los 12 vértices del icosaedro se calculan en getPoint3D()
}

void IcosahedronShape::update(float delta_time, float screen_width, float screen_height) {
    // Recalcular radio por si cambió resolución (F4)
    radius_ = screen_height * ICOSAHEDRON_RADIUS_FACTOR;

    // Actualizar ángulos de rotación (triple rotación XYZ)
    angle_x_ += ICOSAHEDRON_ROTATION_SPEED_X * delta_time;
    angle_y_ += ICOSAHEDRON_ROTATION_SPEED_Y * delta_time;
    angle_z_ += ICOSAHEDRON_ROTATION_SPEED_Z * delta_time;
}

void IcosahedronShape::getPoint3D(int index, float& x, float& y, float& z) const {
    // Proporción áurea (golden ratio)
    const float phi = (1.0f + sqrtf(5.0f)) / 2.0f;

    // 12 vértices del icosaedro regular normalizado
    // Basados en 3 rectángulos áureos ortogonales
    static const float vertices[12][3] = {
        // Rectángulo XY
        {-1.0f,  phi,  0.0f},
        { 1.0f,  phi,  0.0f},
        {-1.0f, -phi,  0.0f},
        { 1.0f, -phi,  0.0f},
        // Rectángulo YZ
        { 0.0f, -1.0f,  phi},
        { 0.0f,  1.0f,  phi},
        { 0.0f, -1.0f, -phi},
        { 0.0f,  1.0f, -phi},
        // Rectángulo ZX
        { phi,  0.0f, -1.0f},
        { phi,  0.0f,  1.0f},
        {-phi,  0.0f, -1.0f},
        {-phi,  0.0f,  1.0f}
    };

    // Normalizar para esfera circunscrita
    const float normalization = sqrtf(1.0f + phi * phi);

    // Si tenemos 12 o menos puntos, usar solo vértices
    if (num_points_ <= 12) {
        int vertex_index = index % 12;
        float x_base = vertices[vertex_index][0] / normalization * radius_;
        float y_base = vertices[vertex_index][1] / normalization * radius_;
        float z_base = vertices[vertex_index][2] / normalization * radius_;

        // Aplicar rotaciones
        applyRotations(x_base, y_base, z_base, x, y, z);
        return;
    }

    // Para más de 12 puntos: subdividir caras triangulares
    // Distribuir puntos entre vértices (primero) y caras (después)
    if (index < 12) {
        // Primeros 12 puntos: vértices del icosaedro
        float x_base = vertices[index][0] / normalization * radius_;
        float y_base = vertices[index][1] / normalization * radius_;
        float z_base = vertices[index][2] / normalization * radius_;
        applyRotations(x_base, y_base, z_base, x, y, z);
        return;
    }

    // Puntos restantes: distribuir en caras usando interpolación
    // El icosaedro tiene 20 caras triangulares
    int remaining_points = index - 12;
    int points_per_face = (num_points_ - 12) / 20;
    if (points_per_face < 1) points_per_face = 1;

    int face_index = remaining_points / points_per_face;
    if (face_index >= 20) face_index = 19;

    int point_in_face = remaining_points % points_per_face;

    // Definir algunas caras del icosaedro (usando índices de vértices)
    // Solo necesitamos generar puntos, no renderizar caras completas
    static const int faces[20][3] = {
        {0, 11, 5}, {0, 5, 1}, {0, 1, 7}, {0, 7, 10}, {0, 10, 11},
        {1, 5, 9}, {5, 11, 4}, {11, 10, 2}, {10, 7, 6}, {7, 1, 8},
        {3, 9, 4}, {3, 4, 2}, {3, 2, 6}, {3, 6, 8}, {3, 8, 9},
        {4, 9, 5}, {2, 4, 11}, {6, 2, 10}, {8, 6, 7}, {9, 8, 1}
    };

    // Obtener vértices de la cara
    int v0 = faces[face_index][0];
    int v1 = faces[face_index][1];
    int v2 = faces[face_index][2];

    // Interpolar dentro del triángulo usando coordenadas baricéntricas simples
    float t = static_cast<float>(point_in_face) / static_cast<float>(points_per_face + 1);
    float u = sqrtf(t);
    float v = t - u;

    float x_interp = vertices[v0][0] * (1.0f - u - v) + vertices[v1][0] * u + vertices[v2][0] * v;
    float y_interp = vertices[v0][1] * (1.0f - u - v) + vertices[v1][1] * u + vertices[v2][1] * v;
    float z_interp = vertices[v0][2] * (1.0f - u - v) + vertices[v1][2] * u + vertices[v2][2] * v;

    // Proyectar a la esfera
    float len = sqrtf(x_interp * x_interp + y_interp * y_interp + z_interp * z_interp);
    if (len > 0.0001f) {
        x_interp /= len;
        y_interp /= len;
        z_interp /= len;
    }

    float x_base = x_interp * radius_;
    float y_base = y_interp * radius_;
    float z_base = z_interp * radius_;

    applyRotations(x_base, y_base, z_base, x, y, z);
}

void IcosahedronShape::applyRotations(float x_in, float y_in, float z_in, float& x_out, float& y_out, float& z_out) const {
    // Aplicar rotación en eje X
    float cos_x = cosf(angle_x_);
    float sin_x = sinf(angle_x_);
    float y_rot_x = y_in * cos_x - z_in * sin_x;
    float z_rot_x = y_in * sin_x + z_in * cos_x;

    // Aplicar rotación en eje Y
    float cos_y = cosf(angle_y_);
    float sin_y = sinf(angle_y_);
    float x_rot_y = x_in * cos_y - z_rot_x * sin_y;
    float z_rot_y = x_in * sin_y + z_rot_x * cos_y;

    // Aplicar rotación en eje Z
    float cos_z = cosf(angle_z_);
    float sin_z = sinf(angle_z_);
    float x_final = x_rot_y * cos_z - y_rot_x * sin_z;
    float y_final = x_rot_y * sin_z + y_rot_x * cos_z;

    x_out = x_final;
    y_out = y_final;
    z_out = z_rot_y;
}

float IcosahedronShape::getScaleFactor(float screen_height) const {
    // Factor de escala para física: proporcional al radio
    // Radio base = 72px (0.30 * 240px en resolución 320x240)
    const float BASE_RADIUS = 72.0f;
    float current_radius = screen_height * ICOSAHEDRON_RADIUS_FACTOR;
    return current_radius / BASE_RADIUS;
}
