//! Affine coordinate frames

#[cfg(feature = "derive_serdes")]
use serde::{Deserialize, Serialize};

use crate::geometry;
use super::*;

type LinearBasis2 <S> = NonZero2 <S>;

#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PlanarBasis2 <S> (Matrix2 <S>);

type LinearBasis3 <S> = NonZero3 <S>;

#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct PlanarBasis3 <S> ([NonZero3 <S>; 2]);

#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct EntireBasis3 <S> (Matrix3 <S>);

/// Coordinate frame for a 2D linear affine subspace
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Line2 <S> {
  pub origin : Point2 <S>,
  pub basis  : LinearBasis2 <S>
}

/// Coordinate frame for a 2D planar affine subspace
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Plane2 <S> {
  pub origin : Point2 <S>,
  pub basis  : PlanarBasis2 <S>
}

/// Coordinate frame for a 3D linear affine subspace
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Line3 <S> {
  pub origin : Point3 <S>,
  pub basis  : LinearBasis3 <S>
}

/// Coordinate frame for a 3D planar affine subspace
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Plane3 <S> {
  pub origin : Point3 <S>,
  pub basis  : PlanarBasis3 <S>
}

/// Coordinate frame for a 3D entire affine subspace
#[cfg_attr(feature = "derive_serdes", derive(Serialize, Deserialize))]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct Space3 <S> {
  pub origin : Point3 <S>,
  pub basis  : EntireBasis3 <S>
}

impl <S : Ring> Line2 <S> {
  pub fn point (&self, coordinate : S) -> Point2 <S> {
    self.origin + *self.basis * coordinate
  }
}
impl <S : OrderedRing> From <geometry::Line2 <S>> for Line2 <S> {
  fn from (line : geometry::Line2 <S>) -> Self {
    line.affine_line()
  }
}
impl <S : OrderedRing> From <geometry::Ray2 <S>> for Line2 <S> {
  fn from (ray : geometry::Ray2 <S>) -> Self {
    ray.affine_line()
  }
}
impl <S : OrderedRing> From <geometry::Segment2 <S>> for Line2 <S> {
  fn from (segment : geometry::Segment2 <S>) -> Self {
    segment.affine_line()
  }
}

impl <S : Ring> Plane2 <S> {
  pub fn point (&self, coordinates : Vector2 <S>) -> Point2 <S> {
    self.origin + *self.basis * coordinates
  }
}
impl <S : OrderedField> From <geometry::Triangle2 <S>> for Plane2 <S> {
  fn from (triangle : geometry::Triangle2 <S>) -> Self {
    triangle.affine_plane()
  }
}

impl <S : Ring> Line3 <S> {
  pub fn point (&self, coordinate : S) -> Point3 <S> {
    self.origin + *self.basis * coordinate
  }
}
impl <S : OrderedRing> From <geometry::Line3 <S>> for Line3 <S> {
  fn from (line : geometry::Line3 <S>) -> Self {
    line.affine_line()
  }
}
impl <S : OrderedRing> From <geometry::Ray3 <S>> for Line3 <S> {
  fn from (ray : geometry::Ray3 <S>) -> Self {
    ray.affine_line()
  }
}
impl <S : OrderedRing> From <geometry::Segment3 <S>> for Line3 <S> {
  fn from (segment : geometry::Segment3 <S>) -> Self {
    segment.affine_line()
  }
}

impl <S : Ring> Plane3 <S> {
  pub fn point (&self, coordinates : Vector2 <S>) -> Point3 <S> {
    self.origin + *self.basis[0] * coordinates.x + *self.basis[1] * coordinates.y
  }
}
impl <S : OrderedField> From <geometry::Triangle3 <S>> for Plane3 <S> {
  fn from (triangle : geometry::Triangle3 <S>) -> Self {
    triangle.affine_plane()
  }
}

impl <S> Space3 <S> {
  pub fn point (&self, coordinates : Vector3 <S>) -> Point3 <S> where S : Ring {
    self.origin + *self.basis * coordinates
  }
}

impl <S : Field> PlanarBasis2 <S> {
  /// Returns `None` if basis vectors are not linearly independent
  pub fn new (basis : Matrix2 <S>) -> Option <Self> {
    if LinearIso::is_invertible (basis) {
      Some (PlanarBasis2 (basis))
    } else {
      None
    }
  }
}
impl <S> std::ops::Deref for PlanarBasis2 <S> {
  type Target = Matrix2 <S>;
  fn deref (&self) -> &Self::Target {
    &self.0
  }
}

impl <S : OrderedRing> PlanarBasis3 <S> {
  /// Returns `None` if basis vectors are not linearly independent:
  ///
  /// ```
  /// # use math_utils::*;
  /// # use math_utils::frame::*;
  /// let basis = [
  ///   NonZero3::noisy (vector![1.0, 1.0, 1.0]),
  ///   NonZero3::noisy (vector![11.0, 11.0, 11.0])
  /// ];
  /// assert!(PlanarBasis3::new (basis).is_none());
  /// ```
  pub fn new (basis : [NonZero3 <S>; 2]) -> Option <Self> {
    if *basis[0].cross (*basis[1]).self_dot() != S::zero() {
      Some (PlanarBasis3 (basis))
    } else {
      None
    }
  }
}
impl <S> std::ops::Deref for PlanarBasis3 <S> {
  type Target = [NonZero3 <S>; 2];
  fn deref (&self) -> &Self::Target {
    &self.0
  }
}

impl <S : Field> EntireBasis3 <S> {
  /// Returns `None` if basis vectors are not linearly independent
  pub fn new (basis : Matrix3 <S>) -> Option <Self> {
    if LinearIso::is_invertible (basis) {
      Some (EntireBasis3 (basis))
    } else {
      None
    }
  }
}
impl <S> std::ops::Deref for EntireBasis3 <S> {
  type Target = Matrix3 <S>;
  fn deref (&self) -> &Self::Target {
    &self.0
  }
}
