use std::ops::Sub;

use super::{geometric_traits::CoverObject, vector::Vector};

#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
pub struct Aabb<const C: usize, T> {
    pub min: Vector<C, T>,
    pub max: Vector<C, T>,
}

impl<const C: usize, T: std::cmp::Ord + Copy> CoverObject<Vector<C, T>> for Aabb<C, T> {
    fn cover(&mut self, point: &Vector<C, T>) {
        #[allow(clippy::needless_range_loop)]
        for x in 0..C {
            self.min.values[x] = std::cmp::min(self.min.values[x], point.values[x]);
            self.max.values[x] = std::cmp::max(self.max.values[x], point.values[x]);
        }
    }
}

impl<const C: usize, T> Aabb<C, T>
where
    T: std::cmp::Ord + Copy,
    Vector<C, T>: Sub<Output = Vector<C, T>>,
{
    pub fn new(min: Vector<C, T>, max: Vector<C, T>) -> Self {
        Self { min, max }
    }

    #[allow(dead_code)]
    pub fn covering(points: &[Vector<C, T>]) -> Option<Self> {
        let mut iter = points.iter();
        if let Some(a) = iter.next() {
            let mut result = Self::new(*a, *a);

            for b in iter {
                result.cover(b);
            }

            Some(result)
        } else {
            None
        }
    }
    pub fn dim(&self) -> Vector<C, T> {
        self.max - self.min
    }
}

pub type Aabb2<T> = Aabb<2, T>;
pub type Aabb3<T> = Aabb<3, T>;
pub type Aabb4<T> = Aabb<4, T>;

#[cfg(test)]
mod tests {
    use crate::{aabb::Aabb2, vector::V2};

    #[test]
    fn aabb_covering() {
        let points = vec![V2::from_xy(2, 0), V2::from_xy(0, 2)];
        let aabb = Aabb2::covering(&points);
        assert_eq!(
            aabb.unwrap(),
            Aabb2::new(V2::from_xy(0, 0), V2::from_xy(2, 2))
        );
    }
}
