// rigid_body.hpp - Cuerpo rígido 2D para el sistema de física
// © 2025 Orni Attack
//
// Estructura POD-like que encapsula el estado físico de una entidad:
// posición, velocidad lineal/angular, masa, restitución y damping.
// El integrador es semi-implícito de Euler (estable para juegos arcade).
//
// Convenciones:
//   - position: coordenadas lógicas (px), donde la entidad está en el mundo
//   - angle:    radianes; 0 apunta hacia arriba (eje Y negativo en SDL)
//   - velocity: px/s en cartesianas (NO polares — adiós a cos/sin por entidad)
//   - mass = 0 (inverse_mass = 0) representa un cuerpo estático (masa infinita)
//   - restitution 0 = inelástico, 1 = elástico perfecto
//   - linear_damping en s⁻¹ (fricción exponencial: v *= exp(-damping * dt))

#pragma once

#include "core/types.hpp"

namespace Physics {

struct RigidBody {
        // --- Estado cinemático ---
        Vec2 position{};                // Posición del centro (px)
        Vec2 velocity{};                // Velocidad lineal (px/s)
        float angle{0.0F};              // Orientación (rad)
        float angular_velocity{0.0F};   // Velocidad angular (rad/s)

        // --- Propiedades físicas ---
        float mass{1.0F};               // Masa (kg, escala libre)
        float inverse_mass{1.0F};       // 1/mass cacheado (0 = estático)
        float restitution{0.5F};        // Elasticidad (0..1)
        float linear_damping{0.0F};     // Fricción lineal (s⁻¹)
        float angular_damping{0.0F};    // Fricción angular (s⁻¹)
        float radius{0.0F};             // Radio de colisión (círculo)

        // --- Fuerzas acumuladas (reseteadas tras cada integrate) ---
        Vec2 force_accumulator{};
        float torque_accumulator{0.0F};

        // Configura la masa y precalcula inverse_mass.
        // mass <= 0 marca el cuerpo como estático (inmovible por impulsos).
        void setMass(float new_mass) {
            mass = new_mass;
            inverse_mass = (new_mass > 0.0F) ? 1.0F / new_mass : 0.0F;
        }

        // Marca el cuerpo como estático (paredes, obstáculos fijos).
        void setStatic() {
            mass = 0.0F;
            inverse_mass = 0.0F;
            velocity = Vec2{};
            angular_velocity = 0.0F;
        }

        [[nodiscard]] auto isStatic() const -> bool { return inverse_mass == 0.0F; }

        // Aplica una fuerza instantánea (acumulada para el siguiente integrate).
        void applyForce(const Vec2& force) { force_accumulator += force; }

        // Aplica un impulso (cambio inmediato de velocidad: Δv = J / m).
        void applyImpulse(const Vec2& impulse) {
            if (!isStatic()) {
                velocity += impulse * inverse_mass;
            }
        }

        // Resetea los acumuladores tras la integración.
        void clearAccumulators() {
            force_accumulator = Vec2{};
            torque_accumulator = 0.0F;
        }
};

}  // namespace Physics
