use bevy::prelude::*;

#[derive(Copy, Clone, Default, Debug)]
pub struct Aabb {
    extents: Vec2,
    position: Vec2,
}

impl Aabb {
    pub fn new(extents: Vec2, position: Vec2) -> Self {
        Self {
            extents: extents.max(Vec2::ZERO),
            position,
        }
    }

    pub fn from_size(size: Vec2, position: Vec2) -> Self {
        Self {
            extents: size.max(Vec2::ZERO) / 2.0,
            position
        }
    }

    pub(crate) fn get_broad_box(&self, motion: Vec2) -> Aabb {
        let mut bbox = self.clone();

        let half_motion = motion * 0.5;

        bbox.extents = bbox.extents + half_motion.abs();
        bbox.position = self.position + half_motion;

        bbox
    }

    pub(crate) fn min_max(&self) -> (Vec2, Vec2) {
        (self.position - self.extents, self.position + self.extents)
    }

    pub(crate) fn collide_with(&self, other: &Aabb) -> bool {
        let (a_min, a_max) = self.min_max();
        let (b_min, b_max) = other.min_max();

        a_min.x < b_max.x
            && a_max.x > b_min.x
            && a_min.y < b_max.y
            && a_max.y > b_min.y
    }
}

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

    #[test]
    fn not_negative() {
        let a = Aabb::from_size(Vec2::new(-2.0, 2.0), Vec2::ONE);
        assert_eq!(a.extents, Vec2::new(0.0, 1.0));
    }

    #[test]
    fn collide() {
        let a = Aabb::from_size(Vec2::ONE * 5.0, Vec2::ONE);
        let b = Aabb::new(Vec2::ONE, Vec2::ZERO);

        assert!(a.collide_with(&b));
    }

    #[test]
    fn not_collide() {
        let a = Aabb::from_size(Vec2::ONE * 5.0, Vec2::ONE * 7.0);
        let b = Aabb::new(Vec2::ONE, Vec2::ZERO);

        assert!(!a.collide_with(&b));
    }
}