#include "atom_shape.hpp"

#include <algorithm>
#include <cmath>

#include "defines.hpp"

void AtomShape::generatePoints(int num_points, float screen_width, float screen_height) {
    num_points_ = num_points;
    nucleus_radius_ = screen_height * ATOM_NUCLEUS_RADIUS_FACTOR;
    orbit_radius_ = screen_height * ATOM_ORBIT_RADIUS_FACTOR;
    // Las posiciones se calculan en getPoint3D()
}

void AtomShape::update(float delta_time, float screen_width, float screen_height) {
    // Recalcular dimensiones por si cambió resolución (F4)
    nucleus_radius_ = screen_height * ATOM_NUCLEUS_RADIUS_FACTOR;
    orbit_radius_ = screen_height * ATOM_ORBIT_RADIUS_FACTOR;

    // Actualizar rotación global del átomo
    angle_y_ += ATOM_ROTATION_SPEED_Y * delta_time;

    // Actualizar fase de rotación de electrones en órbitas
    orbit_phase_ += ATOM_ORBIT_ROTATION_SPEED * delta_time;
}

void AtomShape::getPoint3D(int index, float& x, float& y, float& z) const {
    int num_orbits = static_cast<int>(ATOM_NUM_ORBITS);

    // Calcular cuántos puntos para núcleo vs órbitas
    int nucleus_points = (num_points_ < 10) ? 1 : (num_points_ / 10);  // 10% para núcleo
    nucleus_points = std::max(nucleus_points, 1);

    // Si estamos en el núcleo
    if (index < nucleus_points) {
        // Distribuir puntos en esfera pequeña (núcleo)
        float t = static_cast<float>(index) / static_cast<float>(nucleus_points);
        float phi = acosf(1.0f - (2.0f * t));
        float theta = PI * 2.0f * t * 3.61803398875f;  // Golden ratio

        float x_nuc = nucleus_radius_ * cosf(theta) * sinf(phi);
        float y_nuc = nucleus_radius_ * sinf(theta) * sinf(phi);
        float z_nuc = nucleus_radius_ * cosf(phi);

        // Aplicar rotación global
        float cos_y = cosf(angle_y_);
        float sin_y = sinf(angle_y_);
        x = x_nuc * cos_y - z_nuc * sin_y;
        y = y_nuc;
        z = x_nuc * sin_y + z_nuc * cos_y;
        return;
    }

    // Puntos restantes: distribuir en órbitas
    int orbit_points = num_points_ - nucleus_points;
    int points_per_orbit = orbit_points / num_orbits;
    points_per_orbit = std::max(points_per_orbit, 1);

    int orbit_index = (index - nucleus_points) / points_per_orbit;
    if (orbit_index >= num_orbits) {
        orbit_index = num_orbits - 1;
    }

    int point_in_orbit = (index - nucleus_points) % points_per_orbit;

    // Ángulo del electrón en su órbita
    float electron_angle = (static_cast<float>(point_in_orbit) / static_cast<float>(points_per_orbit)) * 2.0f * PI;
    electron_angle += orbit_phase_;  // Añadir rotación animada

    // Inclinación del plano orbital (cada órbita en ángulo diferente)
    float orbit_tilt = (static_cast<float>(orbit_index) / static_cast<float>(num_orbits)) * PI;

    // Posición del electrón en su órbita (plano XY local)
    float x_local = orbit_radius_ * cosf(electron_angle);
    float y_local = orbit_radius_ * sinf(electron_angle);
    float z_local = 0.0f;

    // Inclinar el plano orbital (rotación en eje X local)
    float cos_tilt = cosf(orbit_tilt);
    float sin_tilt = sinf(orbit_tilt);
    float y_tilted = (y_local * cos_tilt) - (z_local * sin_tilt);
    float z_tilted = (y_local * sin_tilt) + (z_local * cos_tilt);

    // Aplicar rotación global del átomo (eje Y)
    float cos_y = cosf(angle_y_);
    float sin_y = sinf(angle_y_);
    float x_rot = (x_local * cos_y) - (z_tilted * sin_y);
    float z_rot = (x_local * sin_y) + (z_tilted * cos_y);

    x = x_rot;
    y = y_tilted;
    z = z_rot;
}

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