//! Intersection of Aabbs over Integers.
//!
//! Note that unlike Aabbs over Scalars, Aabbs over Integers are *closed* (they include
//! their boundaries).

use crate::*;
use crate::geometry::primitive::integer::*;

/// Discrete intersection test of 1D axis-aligned bounding boxes (intervals).
///
/// Shared boundary points will return intersection:
///
/// ```
/// # use math_utils::geometry::integer::*;
/// # use math_utils::geometry::intersect::integer::*;
/// let a = Interval::from_points ( 0, 1);
/// let b = Interval::from_points (-1, 0);
/// assert!(discrete_interval (a, b));
/// ```
#[inline]
pub fn discrete_interval <I : Integer> (a : Interval <I>, b : Interval <I>) -> bool {
  let (min_a, max_a) = (a.min(), a.max());
  let (min_b, max_b) = (b.min(), b.max());
  // intersection if intervals overlap
  max_a >= min_b && min_a <= max_b
}

/// Continuous intersection test of 1D axis-aligned bounding boxes (intervals).
///
/// Shared boundary points will return intersection:
///
/// ```
/// # use math_utils::geometry::integer::*;
/// # use math_utils::geometry::intersect::integer::*;
/// let a = Interval::from_points ( 0, 1);
/// let b = Interval::from_points (-1, 0);
/// assert_eq!(
///   continuous_interval (a, b).unwrap(),
///   Interval::from_points (0, 0));
/// ```
#[inline]
pub fn continuous_interval <I> (a : Interval <I>, b : Interval <I>)
  -> Option <Interval <I>>
where I : Integer + std::fmt::Debug {
  if discrete_interval (a, b) {
    Some (
      Interval::with_minmax (Ord::max (a.min(), b.min()), Ord::min (a.max(), b.max())))
  } else {
    None
  }
}

/// Discrete intersection test of 2D axis-aligned bounding boxes.
///
/// Shared boundary points will return intersection:
///
/// ```
/// # use math_utils::geometry::integer::*;
/// # use math_utils::geometry::intersect::integer::*;
/// let a = Aabb2::from_points ([ 0, 0].into(), [1, 1].into());
/// let b = Aabb2::from_points ([-1, 0].into(), [0, 1].into());
/// assert!(discrete_aabb2_aabb2 (a, b));
/// ```
#[inline]
pub fn discrete_aabb2_aabb2 <I : Integer> (a : Aabb2 <I>, b : Aabb2 <I>) -> bool {
  let (min_a, max_a) = (a.min(), a.max());
  let (min_b, max_b) = (b.min(), b.max());
  // intersection if overlap exists on both axes
  max_a.0.x >= min_b.0.x && min_a.0.x <= max_b.0.x &&
  max_a.0.y >= min_b.0.y && min_a.0.y <= max_b.0.y
}

/// Continuous intersection test of 2D axis-aligned bounding boxes.
///
/// Shared boundary points will return intersection:
///
/// ```
/// # use math_utils::geometry::integer::*;
/// # use math_utils::geometry::intersect::integer::*;
/// let a = Aabb2::from_points ([ 0, 0].into(), [1, 1].into());
/// let b = Aabb2::from_points ([-1, 0].into(), [0, 1].into());
/// assert_eq!(
///   continuous_aabb2_aabb2 (a, b).unwrap(),
///   Aabb2::from_points ([0,0].into(), [0, 1].into()));
/// ```
#[inline]
pub fn continuous_aabb2_aabb2 <I> (a : Aabb2 <I>, b : Aabb2 <I>) -> Option <Aabb2 <I>>
  where I : Integer + std::fmt::Debug
{
  if discrete_aabb2_aabb2 (a, b) {
    Some (
      Aabb2::with_minmax (point2_max (a.min(), b.min()), point2_min (a.max(), b.max())))
  } else {
    None
  }
}

/// Discrete intersection test of 3D axis-aligned bounding boxes.
///
/// Shared boundary points will return intersection:
///
/// ```
/// # use math_utils::geometry::integer::*;
/// # use math_utils::geometry::intersect::integer::*;
/// let a = Aabb3::from_points ([ 0, 0,  0].into(), [1, 1, 1].into());
/// let b = Aabb3::from_points ([-1, 0,  0].into(), [0, 1, 1].into());
/// assert!(discrete_aabb3_aabb3 (a, b));
/// ```
#[inline]
pub fn discrete_aabb3_aabb3 <I : Integer> (a : Aabb3 <I>, b : Aabb3 <I>) -> bool {
  let (min_a, max_a) = (a.min(), a.max());
  let (min_b, max_b) = (b.min(), b.max());
  // intersection if overlap exists on all three axes
  max_a.0.x >= min_b.0.x && min_a.0.x <= max_b.0.x &&
  max_a.0.y >= min_b.0.y && min_a.0.y <= max_b.0.y &&
  max_a.0.z >= min_b.0.z && min_a.0.z <= max_b.0.z
}

/// Continuous intersection test of 3D axis-aligned bounding boxes.
///
/// Shared boundary points will return intersection:
///
/// ```
/// # use math_utils::geometry::integer::*;
/// # use math_utils::geometry::intersect::integer::*;
/// let a = Aabb3::from_points ([ 0, 0,  0].into(), [1, 1, 1].into());
/// let b = Aabb3::from_points ([-1, 0,  0].into(), [0, 1, 1].into());
/// assert_eq!(
///   continuous_aabb3_aabb3 (a, b).unwrap(),
///   Aabb3::from_points ([0, 0, 0].into(), [0, 1, 1].into()));
/// ```
#[inline]
pub fn continuous_aabb3_aabb3 <I> (a : Aabb3 <I>, b : Aabb3 <I>) -> Option <Aabb3 <I>>
  where I : Integer + std::fmt::Debug
{
  if discrete_aabb3_aabb3 (a, b) {
    Some (
      Aabb3::with_minmax (point3_max (a.min(), b.min()), point3_min (a.max(), b.max())))
  } else {
    None
  }
}
