use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct PhysicsMaterial {
    pub name: String,
    pub friction: f32,
    pub restitution: f32,
    pub density: f32,
}

impl PhysicsMaterial {
    pub fn new(name: String, friction: f32, restitution: f32, density: f32) -> Self {
        Self {
            name,
            friction,
            restitution,
            density,
        }
    }

    pub fn default_material() -> Self {
        Self::new("Default".to_string(), 0.5, 0.2, 1.0)
    }

    pub fn ice() -> Self {
        Self::new("Ice".to_string(), 0.02, 0.95, 0.9)
    }

    pub fn rubber() -> Self {
        Self::new("Rubber".to_string(), 1.0, 0.9, 0.95)
    }

    pub fn metal() -> Self {
        Self::new("Metal".to_string(), 0.4, 0.1, 7.8)
    }

    pub fn wood() -> Self {
        Self::new("Wood".to_string(), 0.6, 0.3, 0.6)
    }

    pub fn concrete() -> Self {
        Self::new("Concrete".to_string(), 0.8, 0.1, 2.4)
    }

    pub fn plastic() -> Self {
        Self::new("Plastic".to_string(), 0.3, 0.4, 0.9)
    }

    pub fn glass() -> Self {
        Self::new("Glass".to_string(), 0.9, 0.05, 2.5)
    }

    pub fn bouncy_ball() -> Self {
        Self::new("Bouncy Ball".to_string(), 0.8, 0.95, 0.5)
    }

    pub fn sand() -> Self {
        Self::new("Sand".to_string(), 0.9, 0.0, 1.6)
    }

    pub fn get_predefined_materials() -> Vec<PhysicsMaterial> {
        vec![
            Self::default_material(),
            Self::ice(),
            Self::rubber(),
            Self::metal(),
            Self::wood(),
            Self::concrete(),
            Self::plastic(),
            Self::glass(),
            Self::bouncy_ball(),
            Self::sand(),
        ]
    }

    pub fn apply_to_collider(&self, collider: &mut super::ColliderComponent) {
        collider.friction = self.friction;
        collider.restitution = self.restitution;
        collider.density = self.density;
    }

    pub fn apply_to_collider_builder(
        &self,
        mut collider: super::ColliderComponent,
    ) -> super::ColliderComponent {
        collider.friction = self.friction;
        collider.restitution = self.restitution;
        collider.density = self.density;
        collider
    }
}

impl Default for PhysicsMaterial {
    fn default() -> Self {
        Self::default_material()
    }
}

#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct PhysicsMaterialComponent {
    pub material: PhysicsMaterial,
}

impl PhysicsMaterialComponent {
    pub fn new(material: PhysicsMaterial) -> Self {
        Self { material }
    }

    pub fn with_material_name(material_name: &str) -> Self {
        let material = match material_name {
            "Ice" => PhysicsMaterial::ice(),
            "Rubber" => PhysicsMaterial::rubber(),
            "Metal" => PhysicsMaterial::metal(),
            "Wood" => PhysicsMaterial::wood(),
            "Concrete" => PhysicsMaterial::concrete(),
            "Plastic" => PhysicsMaterial::plastic(),
            "Glass" => PhysicsMaterial::glass(),
            "Bouncy Ball" => PhysicsMaterial::bouncy_ball(),
            "Sand" => PhysicsMaterial::sand(),
            _ => PhysicsMaterial::default_material(),
        };
        Self::new(material)
    }
}
