//! # Affine2
//!
//! A 2D affine transform, with a linear part (rotation, scale, shear) plus a translation.
//!
//! # Examples
//! ```rust
//! # use roofy::{Affine2, Vec2};
//! let transform: Affine2 = Affine2::from_tas_degrees(
//!     Vec2::new(1.0, 2.0),
//!     90.0,
//!     Vec2::splat(2.0),
//! );
//!
//! let point: Vec2 = transform.transform_point(Vec2::X);
//! let dir: Vec2 = transform.transform_vector(Vec2::X);
//!
//! assert!(point.approx_eq(Vec2::new(1.0, 4.0)));
//! assert!(dir.approx_eq(Vec2::new(0.0, 2.0)));
//! ```

use std::{
    fmt::Display,
    ops::{Mul, MulAssign},
};

use crate::{Mat2, Mat3, Vec2, Vec3};

/// A 2D affine transform, with a linear part (rotation, scale, shear) plus a translation.
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
pub struct Affine2 {
    /// The linear part of the transform (rotation, scale, shear).
    pub matrix: Mat2,
    /// The translation part of the transform.
    pub translation: Vec2,
}

impl Affine2 {
    /// Threshold for treating a value as zero.
    pub const EPSILON: f32 = 1e-6;

    /// An Affine2 with matrix and translation all `NaN`.
    pub const NAN: Self = Self::from_mat2_translation(Mat2::NAN, Vec2::NAN);

    /// An Affine2 with matrix and translation all `0.0`.
    pub const ZERO: Self = Self::from_mat2_translation(Mat2::ZERO, Vec2::ZERO);

    /// The identity transform.
    pub const IDENTITY: Self = Self::from_mat2_translation(Mat2::IDENTITY, Vec2::ZERO);

    /// Creates an `Affine2` from a `Mat2`.
    #[inline(always)]
    #[must_use]
    pub const fn from_mat2(m: Mat2) -> Self {
        Self::from_mat2_translation(m, Vec2::ZERO)
    }

    /// Creates an `Affine2` from a `matrix` and `translation`.
    #[inline(always)]
    #[must_use]
    pub const fn from_mat2_translation(matrix: Mat2, translation: Vec2) -> Self {
        Self {
            matrix,
            translation,
        }
    }

    /// Creates an `Affine2` from 2 columns and `translation`.
    #[inline]
    #[must_use]
    pub const fn from_cols(col0: Vec2, col1: Vec2, translation: Vec2) -> Self {
        Self::from_mat2_translation(Mat2::from_cols(col0, col1), translation)
    }

    /// Creates an `Affine2` from a `[f32; 6]`, in column-major order:
    /// `[col0, col1, translation]` with each column as `[x, y]`.
    #[inline]
    #[must_use]
    pub const fn from_array(a: [f32; 6]) -> Self {
        Self::from_mat2_translation(
            Mat2::from_cols(Vec2::new(a[0], a[1]), Vec2::new(a[2], a[3])),
            Vec2::new(a[4], a[5]),
        )
    }

    /// Returns a copy of the components as `[col0, col1, translation]`,
    /// with each column as `[x, y]`, in column-major order.
    #[inline]
    #[must_use]
    pub const fn to_array(self) -> [f32; 6] {
        let m: Mat2 = self.matrix;
        let t: Vec2 = self.translation;

        [m.col0.x, m.col0.y, m.col1.x, m.col1.y, t.x, t.y]
    }

    /// Creates an `Affine2` from first 6 values of `slice`, in column-major order.
    ///
    /// # Panics
    /// Panics if `slice` is less than 6 elements long.
    #[inline]
    #[must_use]
    pub const fn from_slice(slice: &[f32]) -> Self {
        Self::from_mat2_translation(
            Mat2::from_cols(Vec2::new(slice[0], slice[1]), Vec2::new(slice[2], slice[3])),
            Vec2::new(slice[4], slice[5]),
        )
    }

    /// Writes the columns of `self` to the first 6 elements of `slice`.
    ///
    /// # Panics
    /// Panics if `slice` is less than 6 elements long.
    #[inline]
    pub const fn write_to_slice(self, slice: &mut [f32]) {
        slice[0] = self.matrix.col0.x;
        slice[1] = self.matrix.col0.y;
        slice[2] = self.matrix.col1.x;
        slice[3] = self.matrix.col1.y;
        slice[4] = self.translation.x;
        slice[5] = self.translation.y;
    }

    /// Creates an `Affine2` that translates by `translation`.
    #[inline]
    #[must_use]
    pub const fn from_translation(translation: Vec2) -> Self {
        Self::from_mat2_translation(Mat2::IDENTITY, translation)
    }

    /// Creates an `Affine2` that scales by `x` and `y` components of `scale` along each axis.
    #[inline]
    #[must_use]
    pub const fn from_scale(scale: Vec2) -> Self {
        Self::from_mat2_translation(Mat2::from_diagonal(scale), Vec2::ZERO)
    }

    /// Creates an `Affine2` that rotates by `angle` in radians.
    #[inline]
    #[must_use]
    pub fn from_angle(angle: f32) -> Self {
        Self::from_mat2_translation(Mat2::from_angle(angle), Vec2::ZERO)
    }

    /// Creates an `Affine2` that rotates by `angle` in degrees.
    #[inline]
    #[must_use]
    pub fn from_angle_degrees(angle: f32) -> Self {
        Self::from_mat2_translation(Mat2::from_angle_degrees(angle), Vec2::ZERO)
    }

    /// Creates an `Affine2` from a `translation`, an `angle` in radians and a `scale`.
    ///
    /// Applied in order: scale -> rotate -> translate.
    #[inline]
    #[must_use]
    pub fn from_tas(translation: Vec2, angle: f32, scale: Vec2) -> Self {
        Self::from_mat2_translation(Mat2::from_scale_angle(scale, angle), translation)
    }

    /// Creates an `Affine2` from a `translation`, an `angle` in degrees and a `scale`.
    ///
    /// Applied in order: scale -> rotate -> translate.
    #[inline]
    #[must_use]
    pub fn from_tas_degrees(translation: Vec2, angle: f32, scale: Vec2) -> Self {
        Self::from_mat2_translation(Mat2::from_scale_angle_degrees(scale, angle), translation)
    }

    /// Creates an `Affine2` from a `Mat3`, discarding its projective row.
    #[inline]
    #[must_use]
    pub const fn from_mat3(m: Mat3) -> Self {
        Self::from_mat2_translation(
            Mat2::from_cols(Vec2::new(m.col0.x, m.col0.y), Vec2::new(m.col1.x, m.col1.y)),
            Vec2::new(m.col2.x, m.col2.y),
        )
    }

    /// Returns the equivalent `Mat3` of `self`.
    #[inline]
    #[must_use]
    pub const fn to_mat3(self) -> Mat3 {
        let m: Mat2 = self.matrix;
        let t: Vec2 = self.translation;

        Mat3::from_cols(
            Vec3::new(m.col0.x, m.col0.y, 0.0),
            Vec3::new(m.col1.x, m.col1.y, 0.0),
            Vec3::new(t.x, t.y, 1.0),
        )
    }

    /// Returns the composition of `self` and `rhs`, applying `rhs` first.
    #[inline]
    #[must_use]
    pub const fn mul(self, rhs: Self) -> Self {
        Self::from_mat2_translation(
            self.matrix.mul(rhs.matrix),
            self.matrix.transform(rhs.translation).add(self.translation),
        )
    }

    /// Sets `self` to the composition of `self` and `rhs`, applying `rhs` first.
    #[inline]
    pub const fn mul_assign(&mut self, rhs: Self) {
        *self = self.mul(rhs)
    }

    /// Returns `rhs` transformed as a point by `self`.
    ///
    /// Applies the full transform including translation.
    ///
    /// Equivalent to [`transform_point`](Self::transform_point).
    #[inline]
    #[must_use]
    pub const fn mul_vec2(self, rhs: Vec2) -> Vec2 {
        self.transform_point(rhs)
    }

    /// Returns the inverse of `self` or `None` if `self` is not invertible.
    ///
    /// # Edge cases
    /// If the linear part of `self` is not invertible, returns `None`.
    #[inline]
    #[must_use]
    pub const fn try_inverse(self) -> Option<Self> {
        let Some(inv_matrix) = self.matrix.try_inverse() else {
            return None;
        };
        let inv_translation: Vec2 = inv_matrix.mul_vec2(self.translation).neg();

        Some(Self::from_mat2_translation(inv_matrix, inv_translation))
    }

    /// Returns the inverse of `self`.
    ///
    /// # Panics
    /// Panics if `self` is not invertible.
    #[inline]
    #[must_use]
    pub const fn inverse(self) -> Self {
        self.try_inverse().expect("affine transform not invertible")
    }

    /// Returns `true` if `self` is invertible.
    #[inline]
    #[must_use]
    pub const fn is_invertible(self) -> bool {
        self.matrix.is_invertible()
    }

    /// Returns `v` transformed as a point by `self`.
    ///
    /// Applies the full transform including translation.
    #[inline]
    #[must_use]
    pub const fn transform_point(self, v: Vec2) -> Vec2 {
        Vec2::new(
            self.matrix.col0.x * v.x + self.matrix.col1.x * v.y + self.translation.x,
            self.matrix.col0.y * v.x + self.matrix.col1.y * v.y + self.translation.y,
        )
    }

    /// Returns `v` transformed as a direction by `self`.
    ///
    /// Applies only the linear part of `self`, ignoring translation.
    /// Use this for directions, normals, and offsets.
    #[inline]
    #[must_use]
    pub const fn transform_vector(self, v: Vec2) -> Vec2 {
        self.matrix.transform(v)
    }

    /// Returns `true` if `self` and `other` approximately represent the same transform.
    #[inline]
    #[must_use]
    pub fn approx_eq(self, other: Self) -> bool {
        self.matrix.approx_eq(other.matrix) && self.translation.approx_eq(other.translation)
    }

    /// Returns `true` if all components of `self` are finite.
    #[inline]
    #[must_use]
    pub const fn is_finite(self) -> bool {
        self.matrix.is_finite() && self.translation.is_finite()
    }

    /// Returns `true` if any component of `self` is `NaN`.
    #[inline]
    #[must_use]
    pub const fn is_nan(self) -> bool {
        self.matrix.is_nan() || self.translation.is_nan()
    }
}

impl Mul for Affine2 {
    type Output = Self;

    /// Returns the composition of `self` and `rhs`, applying `rhs` first.
    ///
    /// Equivalent to [`mul`](Self::mul()).
    #[inline]
    fn mul(self, rhs: Self) -> Self::Output {
        self.mul(rhs)
    }
}

impl MulAssign for Affine2 {
    /// Sets `self` to the composition of `self` and `rhs`, applying `rhs` first.
    ///
    /// Equivalent to [`mul_assign`](Self::mul_assign()).
    #[inline]
    fn mul_assign(&mut self, rhs: Self) {
        self.mul_assign(rhs);
    }
}

impl Mul<Vec2> for Affine2 {
    type Output = Vec2;

    /// Returns `rhs` transformed as a point by `self`.
    ///
    /// Applies the full transform including translation.
    ///
    /// Equivalent to [`mul_vec2`](Self::mul_vec2()).
    #[inline]
    fn mul(self, rhs: Vec2) -> Self::Output {
        self.mul_vec2(rhs)
    }
}

impl Display for Affine2 {
    /// Formats `self` as `[col0, col1, translation]`,
    /// with each column as `(x, y)`.
    ///
    /// Respects the precision of `f`.
    #[inline]
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        if let Some(p) = f.precision() {
            write!(
                f,
                "[{:.*}, {:.*}, {:.*}]",
                p, self.matrix.col0, p, self.matrix.col1, p, self.translation
            )
        } else {
            write!(
                f,
                "[{}, {}, {}]",
                self.matrix.col0, self.matrix.col1, self.translation
            )
        }
    }
}

impl Default for Affine2 {
    /// Default is [`IDENTITY`](Self::IDENTITY).
    #[inline]
    fn default() -> Self {
        Self::IDENTITY
    }
}

impl From<[f32; 6]> for Affine2 {
    /// Creates an `Affine2` from a `[f32; 6]`, in order:
    /// `[col0, col1, translation]` with each column as `[x, y]`.
    ///
    /// Equivalent to [`from_array`](Self::from_array()).
    #[inline]
    fn from(v: [f32; 6]) -> Self {
        Self::from_array(v)
    }
}

impl From<Affine2> for [f32; 6] {
    /// Returns a copy of the components as `[col0, col1, translation]`,
    /// with each column as `[x, y]`.
    ///
    /// Equivalent to [`to_array`](Affine2::to_array()).
    #[inline]
    fn from(v: Affine2) -> Self {
        v.to_array()
    }
}

impl From<Mat3> for Affine2 {
    /// Creates an `Affine2` from a `Mat3`, discarding its projective row.
    ///
    /// Equivalent to [`from_mat3`](Self::from_mat3()).
    #[inline]
    fn from(v: Mat3) -> Self {
        Self::from_mat3(v)
    }
}

#[cfg(feature = "serde")]
impl serde::Serialize for Affine2 {
    /// Serializes as a `[f32; 6]`, the first 4 elements are the linear part (`Mat2`)
    /// in column-major order, the last 2 are the translation.
    ///
    /// # Format
    /// Encoded as a flat 6-element array. Component order matches [`to_array`](Self::to_array()).
    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
        self.to_array().serialize(serializer)
    }
}

#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Affine2 {
    /// Deserializes from a `[f32; 6]`, the first 4 elements are the linear part (`Mat2`)
    /// in column-major order, the last 2 are the translation.
    ///
    /// # Format
    /// Decoded as a flat 6-element array. Component order matches [`from_array`](Self::from_array()).
    ///
    /// # Errors
    /// Returns an error if array is not exactly 6 elements.
    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
        <[f32; 6]>::deserialize(deserializer).map(Self::from_array)
    }
}

#[cfg(feature = "bytemuck")]
unsafe impl bytemuck::Zeroable for Affine2 {}

#[cfg(feature = "bytemuck")]
unsafe impl bytemuck::Pod for Affine2 {}
