#include "cylinder_shape.hpp"

#include <algorithm>
#include <cmath>
#include <cstdlib>  // Para rand()

#include "defines.hpp"

void CylinderShape::generatePoints(int num_points, float screen_width, float screen_height) {
    num_points_ = num_points;
    radius_ = screen_height * CYLINDER_RADIUS_FACTOR;
    height_ = screen_height * CYLINDER_HEIGHT_FACTOR;

    // Inicializar timer de tumbling con valor aleatorio (3-5 segundos)
    tumble_timer_ = 3.0f + (rand() % 2000) / 1000.0f;
    // Las posiciones 3D se calculan en getPoint3D() usando ecuaciones paramétricas del cilindro
}

void CylinderShape::update(float delta_time, float screen_width, float screen_height) {
    // Recalcular dimensiones por si cambió resolución (F4)
    radius_ = screen_height * CYLINDER_RADIUS_FACTOR;
    height_ = screen_height * CYLINDER_HEIGHT_FACTOR;

    // Actualizar ángulo de rotación en eje Y (siempre activo)
    angle_y_ += CYLINDER_ROTATION_SPEED_Y * delta_time;

    // Sistema de tumbling ocasional
    if (is_tumbling_) {
        // Estamos en tumble: animar angle_x hacia el objetivo
        tumble_duration_ += delta_time;
        float tumble_progress = tumble_duration_ / 1.5f;  // 1.5 segundos de duración

        if (tumble_progress >= 1.0f) {
            // Tumble completado
            angle_x_ = tumble_target_;
            is_tumbling_ = false;
            tumble_timer_ = 3.0f + (rand() % 2000) / 1000.0f;  // Nuevo timer (3-5s)
        } else {
            // Interpolación suave con ease-in-out
            float t = tumble_progress;
            float ease = t < 0.5f
                ? 2.0f * t * t
                : 1.0f - ((-2.0f * t + 2.0f) * (-2.0f * t + 2.0f) / 2.0f);
            angle_x_ = ease * tumble_target_;
        }
    } else {
        // No estamos en tumble: contar tiempo
        tumble_timer_ -= delta_time;
        if (tumble_timer_ <= 0.0f) {
            // Iniciar nuevo tumble
            is_tumbling_ = true;
            tumble_duration_ = 0.0f;
            // Objetivo: PI/2 radianes (90°) o -PI/2
            tumble_target_ = angle_x_ + ((rand() % 2) == 0 ? PI * 0.5f : -PI * 0.5f);
        }
    }
}

void CylinderShape::getPoint3D(int index, float& x, float& y, float& z) const {
    // Distribuir puntos uniformemente en la superficie del cilindro
    // Calcular número de anillos (altura) y puntos por anillo (circunferencia)

    int num_rings = static_cast<int>(sqrtf(static_cast<float>(num_points_) * 0.5f));
    num_rings = std::max(num_rings, 2);

    int points_per_ring = num_points_ / num_rings;
    points_per_ring = std::max(points_per_ring, 3);

    // Obtener parámetros u (ángulo) y v (altura) del índice
    int ring = index / points_per_ring;
    int point_in_ring = index % points_per_ring;

    // Si nos pasamos del número de anillos, usar el último
    if (ring >= num_rings) {
        ring = num_rings - 1;
        point_in_ring = index - (ring * points_per_ring);
        if (point_in_ring >= points_per_ring) {
            point_in_ring = points_per_ring - 1;
        }
    }

    // Parámetro u (ángulo alrededor del cilindro): [0, 2π]
    float u = (static_cast<float>(point_in_ring) / static_cast<float>(points_per_ring)) * 2.0f * PI;

    // Parámetro v (altura normalizada): [-1, 1]
    float v = ((static_cast<float>(ring) / static_cast<float>(num_rings - 1)) * 2.0f) - 1.0f;
    if (num_rings == 1) {
        v = 0.0f;
    }

    // Ecuaciones paramétricas del cilindro
    // x = radius * cos(u)
    // y = height * v
    // z = radius * sin(u)
    float x_base = radius_ * cosf(u);
    float y_base = (height_ * 0.5f) * v;  // Centrar verticalmente
    float z_base = radius_ * sinf(u);

    // Aplicar rotación en eje Y (principal, siempre activa)
    float cos_y = cosf(angle_y_);
    float sin_y = sinf(angle_y_);
    float x_rot_y = (x_base * cos_y) - (z_base * sin_y);
    float z_rot_y = (x_base * sin_y) + (z_base * cos_y);

    // Aplicar rotación en eje X (tumbling ocasional)
    float cos_x = cosf(angle_x_);
    float sin_x = sinf(angle_x_);
    float y_rot = (y_base * cos_x) - (z_rot_y * sin_x);
    float z_rot = (y_base * sin_x) + (z_rot_y * cos_x);

    // Retornar coordenadas finales con ambas rotaciones
    x = x_rot_y;
    y = y_rot;
    z = z_rot;
}

auto CylinderShape::getScaleFactor(float screen_height) const -> float {
    // Factor de escala para física: proporcional a la dimensión mayor (altura)
    // Altura base = 120px (0.5 * 240px en resolución 320x240)
    const float BASE_HEIGHT = 120.0f;
    float current_height = screen_height * CYLINDER_HEIGHT_FACTOR;
    return current_height / BASE_HEIGHT;
}
