use collide::Bounded;
use collide_sphere::Sphere;
use inner_space::InnerSpace;
use scalars::{One, Zero};

use crate::Convex;

impl<V: Copy + InnerSpace> Bounded<Sphere<V>> for Convex<V> {
    fn bounding_volume(&self) -> Sphere<V> {
        if self.points.is_empty() {
            return Sphere::new(V::zero(), V::Scalar::zero());
        }

        let mut count = V::Scalar::zero();
        let sum = self.points.iter().copied().fold(V::zero(), |a, b| {
            count = count + V::Scalar::one();
            a + b
        });
        let center = sum / count;

        let max_distance = self
            .points
            .iter()
            .map(|&p| (p - center).magnitude())
            .fold(V::Scalar::zero(), |a, b| if a > b { a } else { b });

        Sphere::new(center, max_distance + self.rad)
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use simple_vectors::Vector;

    type Vec3 = Vector<f32, 3>;

    #[test]
    fn triangle_bounding_sphere() {
        let convex = Convex::new(
            0.1,
            vec![
                Vec3::from([-1.0, 0.0, 0.0]),
                Vec3::from([1.0, 0.0, 0.0]),
                Vec3::from([0.0, 1.0, 0.0]),
            ]
            .into(),
        );
        let sphere: Sphere<Vec3> = convex.bounding_volume();

        assert!(sphere.radius > 1.0);
        assert!((sphere.center - Vec3::from([0.0, 1.0 / 3.0, 0.0])).magnitude() < 0.01);
    }

    #[test]
    fn single_point_bounding_sphere() {
        let convex = Convex::sphere(Vec3::from([1.0, 2.0, 3.0]), 0.5);
        let sphere: Sphere<Vec3> = convex.bounding_volume();

        assert!((sphere.center - Vec3::from([1.0, 2.0, 3.0])).magnitude() < 0.001);
        assert!((sphere.radius - 0.5).abs() < 0.001);
    }
}
