//! Types canoniques pour le système de collision.
//!
//! Ces types sont indépendants de tout backend physique (Avian, Rapier, etc.)
//! et stockent la *configuration* d'un collider, pas sa représentation runtime.

use serde::{Deserialize, Serialize};

/// Formes de collider supportées.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ColliderShape {
    Cuboid { half_x: f32, half_y: f32, half_z: f32 },
    Sphere  { radius: f32 },
    Capsule { half_length: f32, radius: f32 },
    Cylinder { half_height: f32, radius: f32 },
    /// Convex hull calculé depuis un mesh asset (chargement async, phase future).
    ConvexHull { mesh_path: String },
    /// Trimesh concave statique (chargement async, phase future).
    TriMesh    { mesh_path: String },
}

impl Default for ColliderShape {
    fn default() -> Self {
        ColliderShape::Cuboid { half_x: 0.5, half_y: 0.5, half_z: 0.5 }
    }
}

impl ColliderShape {
    /// Nom court pour l'UI.
    pub fn label(&self) -> &'static str {
        match self {
            ColliderShape::Cuboid { .. }   => "Cuboid",
            ColliderShape::Sphere { .. }   => "Sphere",
            ColliderShape::Capsule { .. }  => "Capsule",
            ColliderShape::Cylinder { .. } => "Cylinder",
            ColliderShape::ConvexHull { .. } => "ConvexHull",
            ColliderShape::TriMesh { .. }  => "TriMesh",
        }
    }

    /// Retourne true si la forme nécessite un mesh externe (non éditable en MVP).
    pub fn needs_mesh(&self) -> bool {
        matches!(self, ColliderShape::ConvexHull { .. } | ColliderShape::TriMesh { .. })
    }
}

/// Mode du corps rigide.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum PhysicsBodyType {
    #[default]
    Static,
    Dynamic,
    Kinematic,
}

impl PhysicsBodyType {
    pub fn label(self) -> &'static str {
        match self {
            PhysicsBodyType::Static    => "Static",
            PhysicsBodyType::Dynamic   => "Dynamic",
            PhysicsBodyType::Kinematic => "Kinematic",
        }
    }
}

/// Politique de combinaison pour friction / restitution.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
pub enum CombinePolicy {
    #[default]
    Average,
    Minimum,
    Maximum,
    Multiply,
}

impl CombinePolicy {
    pub fn label(self) -> &'static str {
        match self {
            CombinePolicy::Average  => "Avg",
            CombinePolicy::Minimum  => "Min",
            CombinePolicy::Maximum  => "Max",
            CombinePolicy::Multiply => "Mul",
        }
    }

    pub fn all() -> &'static [CombinePolicy] {
        &[CombinePolicy::Average, CombinePolicy::Minimum, CombinePolicy::Maximum, CombinePolicy::Multiply]
    }
}

/// Matériau physique réutilisable.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct PhysicsMaterial {
    pub name: String,
    pub friction: f32,
    pub friction_combine: CombinePolicy,
    pub restitution: f32,
    pub restitution_combine: CombinePolicy,
}

impl Default for PhysicsMaterial {
    fn default() -> Self {
        Self {
            name: "Défaut".to_string(),
            friction: 0.5,
            friction_combine: CombinePolicy::Average,
            restitution: 0.0,
            restitution_combine: CombinePolicy::Average,
        }
    }
}

/// Profil de collision nommé (preset, style Unreal).
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CollisionProfile {
    pub name: String,
    /// Bitmask memberships (layers sur lesquels l'objet est présent).
    pub memberships: u32,
    /// Bitmask filters (layers avec lesquels l'objet interagit).
    pub filters: u32,
    pub is_sensor: bool,
}

/// Configuration complète d'un collider pour une entité.
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, Default)]
pub struct ColliderConfig {
    pub shape: ColliderShape,
    pub body_type: PhysicsBodyType,
    /// Sensor = trigger (pas de résolution physique, détection seule).
    pub is_sensor: bool,
    /// Bitmask des layers sur lesquels l'objet est présent.
    pub memberships: u32,
    /// Bitmask des layers que l'objet détecte / avec lesquels il entre en collision.
    pub filters: u32,
    /// Nom du matériau physique référencé (None = matériau par défaut).
    pub material_name: Option<String>,
}

impl ColliderConfig {
    /// Applique un profil (remplace memberships, filters, is_sensor).
    pub fn apply_profile(&mut self, profile: &CollisionProfile) {
        self.memberships = profile.memberships;
        self.filters     = profile.filters;
        self.is_sensor   = profile.is_sensor;
    }
}
