//! # Vec4
//!
//! 4-dimensional vector math for game-engine development,
//! primarily used for homogeneous coordinates.
//!
//! # Example
//!
//! ```rust
//! # use roofy::math::Vec4;
//! let red: Vec4 = Vec4::new(1.0, 0.0, 0.0, 1.0);
//! let blue: Vec4 = Vec4::new(0.0, 0.0, 1.0, 1.0);
//!
//! let mixed: Vec4 = red.lerp(blue, 0.5);
//!
//! assert_eq!(mixed, Vec4::new(0.5, 0.0, 0.5, 1.0));
//! ```
use std::{
    fmt::Display,
    ops::{
        Add, AddAssign, Div, DivAssign, Index, IndexMut, Mul, MulAssign, Neg, Rem, RemAssign, Sub,
        SubAssign,
    },
};

use crate::math::{Vec2, Vec3};

/// 4-dimensional vector, with `f32` components.
/// Same memory layout as `[f32; 4]`, making it safe for GPU uploads.
#[derive(Clone, Copy, Debug, PartialEq)]
#[repr(C)]
pub struct Vec4 {
    /// X component of the vector.
    pub x: f32,
    /// Y component of the vector.
    pub y: f32,
    /// Z component of the vector.
    pub z: f32,
    /// W component of the vector.
    pub w: f32,
}

impl Vec4 {
    /// Threshold for treating a value as zero.
    pub const EPSILON: f32 = 1e-6;
    /// [`EPSILON`](Self::EPSILON) squared.
    pub const EPSILON_SQ: f32 = Self::EPSILON * Self::EPSILON;

    /// Shorthand for writing `Vec4::splat(0.0)`.
    pub const ZERO: Self = Self::splat(0.0);
    /// Shorthand for writing `Vec4::splat(1.0)`.
    pub const ONE: Self = Self::splat(1.0);
    /// Shorthand for writing `Vec4::splat(-1.0)`.
    pub const NEG_ONE: Self = Self::splat(-1.0);
    /// Shorthand for writing `Vec4::splat(f32::INFINITY)`.
    pub const INFINITY: Self = Self::splat(f32::INFINITY);
    /// Shorthand for writing `Vec4::splat(f32::NEG_INFINITY)`.
    pub const NEG_INFINITY: Self = Self::splat(f32::NEG_INFINITY);
    /// Shorthand for writing `Vec4::splat(f32::NAN)`.
    pub const NAN: Self = Self::splat(f32::NAN);

    /// Shorthand for writing `Vec4::new(1.0, 0.0, 0.0, 0.0)`.
    pub const X: Self = Self::new(1.0, 0.0, 0.0, 0.0);
    /// Shorthand for writing `Vec4::new(-1.0, 0.0, 0.0, 0.0)`.
    pub const NEG_X: Self = Self::new(-1.0, 0.0, 0.0, 0.0);
    /// Shorthand for writing `Vec4::new(0.0, 1.0, 0.0, 0.0)`.
    pub const Y: Self = Self::new(0.0, 1.0, 0.0, 0.0);
    /// Shorthand for writing `Vec4::new(0.0, -1.0, 0.0, 0.0)`.
    pub const NEG_Y: Self = Self::new(0.0, -1.0, 0.0, 0.0);
    /// Shorthand for writing `Vec4::new(0.0, 0.0, 1.0, 0.0)`.
    pub const Z: Self = Self::new(0.0, 0.0, 1.0, 0.0);
    /// Shorthand for writing `Vec4::new(0.0, 0.0, -1.0, 0.0)`.
    pub const NEG_Z: Self = Self::new(0.0, 0.0, -1.0, 0.0);
    /// Shorthand for writing `Vec4::new(0.0, 0.0, 0.0, 1.0)`.
    pub const W: Self = Self::new(0.0, 0.0, 0.0, 1.0);
    /// Shorthand for writing `Vec4::new(0.0, 0.0, 0.0, -1.0)`.
    pub const NEG_W: Self = Self::new(0.0, 0.0, 0.0, -1.0);

    /// Creates a `Vec4` with `x`, `y`, `z` and `w` components.
    #[inline]
    #[must_use]
    pub const fn new(x: f32, y: f32, z: f32, w: f32) -> Self {
        Self { x, y, z, w }
    }

    /// Creates a `Vec4` with all components `v`.
    #[inline]
    #[must_use]
    pub const fn splat(v: f32) -> Self {
        Self::new(v, v, v, v)
    }

    /// Returns the length (magnitude) of `self`.
    #[inline]
    #[must_use]
    pub fn length(self) -> f32 {
        self.dot(self).sqrt()
    }

    /// Returns the squared length (magnitude) of `self`.
    #[inline]
    #[must_use]
    pub const fn length_squared(self) -> f32 {
        self.dot(self)
    }

    /// Normalizes `self` to have a length of 1, while preserving direction.
    ///
    /// # Edge cases
    /// If `self` is near-zero, sets `self` to [`ZERO`](Self::ZERO).
    #[inline]
    pub fn normalize(&mut self) {
        let len_sq: f32 = self.length_squared();

        if len_sq < Self::EPSILON_SQ {
            *self = Self::ZERO;
            return;
        }

        *self *= 1.0 / len_sq.sqrt();
    }

    /// Returns a unit-length `Vec4` with direction of `self`.
    ///
    /// # Edge cases
    /// If `self` is near-zero, returns [`ZERO`](Self::ZERO).
    #[inline]
    #[must_use]
    pub fn normalized(self) -> Self {
        let len_sq: f32 = self.length_squared();

        if len_sq < Self::EPSILON_SQ {
            return Self::ZERO;
        }

        self * (1.0 / len_sq.sqrt())
    }

    /// Returns `true` if `self` approximately has a length of `1.0`.
    ///
    /// Uses [`EPSILON`](Self::EPSILON) as threshold.
    #[inline]
    #[must_use]
    pub const fn is_normalized(self) -> bool {
        (self.length_squared() - 1.0).abs() < Self::EPSILON
    }

    /// Returns the dot product of `self` and `other`.
    #[inline]
    #[must_use]
    pub const fn dot(self, other: Self) -> f32 {
        self.x * other.x + self.y * other.y + self.z * other.z + self.w * other.w
    }

    /// Returns the distance from `self` to `to`.
    #[inline]
    #[must_use]
    pub fn distance(self, to: Self) -> f32 {
        (self - to).length()
    }

    /// Returns the distance squared from `self` to `to`.
    #[inline]
    #[must_use]
    pub fn distance_squared(self, to: Self) -> f32 {
        (self - to).length_squared()
    }

    /// Returns `self` projected onto `normal`.
    ///
    /// # Edge cases
    /// If `normal` is near-zero, returns [`ZERO`](Self::ZERO).
    #[inline]
    #[must_use]
    pub fn project(self, normal: Self) -> Self {
        let len_sq: f32 = normal.length_squared();

        if len_sq < Self::EPSILON_SQ {
            return Self::ZERO;
        }

        normal * (self.dot(normal) / len_sq)
    }

    /// Returns a `Vec4` with the smallest components of `self` and `other`.
    #[inline]
    #[must_use]
    pub const fn min(self, other: Self) -> Self {
        Self::new(
            self.x.min(other.x),
            self.y.min(other.y),
            self.z.min(other.z),
            self.w.min(other.w),
        )
    }

    /// Returns a `Vec4` with the largest components of `self` and `other`.
    #[inline]
    #[must_use]
    pub const fn max(self, other: Self) -> Self {
        Self::new(
            self.x.max(other.x),
            self.y.max(other.y),
            self.z.max(other.z),
            self.w.max(other.w),
        )
    }

    /// Returns a `Vec4` with components of `self` clamped between corresponding components of `min` and `max`.
    ///
    /// # Precondition
    /// Expects each component of `min` to be less than or equal to the corresponding component of `max`.
    #[inline]
    #[must_use]
    pub const fn clamp(self, min: Self, max: Self) -> Self {
        self.max(min).min(max)
    }

    /// Returns a `Vec4` with the absolute value of each component of `self`.
    #[inline]
    #[must_use]
    pub const fn abs(self) -> Self {
        Self::new(self.x.abs(), self.y.abs(), self.z.abs(), self.w.abs())
    }

    /// Returns a `Vec4` with each component of `self` raised to the power of `n`.
    #[inline]
    #[must_use]
    pub fn powf(self, n: f32) -> Self {
        Self::new(
            self.x.powf(n),
            self.y.powf(n),
            self.z.powf(n),
            self.w.powf(n),
        )
    }

    /// Returns a `Vec4` with the square root value of each component of `self`.
    #[inline]
    #[must_use]
    pub fn sqrt(self) -> Self {
        Self::new(self.x.sqrt(), self.y.sqrt(), self.z.sqrt(), self.w.sqrt())
    }

    /// Returns a `Vec4` with the sine value of each component of `self`.
    #[inline]
    #[must_use]
    pub fn sin(self) -> Self {
        Self::new(self.x.sin(), self.y.sin(), self.z.sin(), self.w.sin())
    }

    /// Returns a `Vec4` with the cosine value of each component of `self`.
    #[inline]
    #[must_use]
    pub fn cos(self) -> Self {
        Self::new(self.x.cos(), self.y.cos(), self.z.cos(), self.w.cos())
    }

    /// Returns a `(Vec4, Vec4)` with the sine and cosine value of each component of `self`.
    #[inline]
    #[must_use]
    pub fn sin_cos(self) -> (Self, Self) {
        let (sin_x, cos_x): (f32, f32) = self.x.sin_cos();
        let (sin_y, cos_y): (f32, f32) = self.y.sin_cos();
        let (sin_z, cos_z): (f32, f32) = self.z.sin_cos();
        let (sin_w, cos_w): (f32, f32) = self.w.sin_cos();

        (
            Self::new(sin_x, sin_y, sin_z, sin_w),
            Self::new(cos_x, cos_y, cos_z, cos_w),
        )
    }

    /// Returns a `Vec4` with the reciprocal value of each component of `self`.
    #[inline]
    #[must_use]
    pub const fn recip(self) -> Self {
        Self::new(
            self.x.recip(),
            self.y.recip(),
            self.z.recip(),
            self.w.recip(),
        )
    }

    /// Returns a `Vec4` linearly interpolated between `self` and `to`, with factor `t`.
    #[inline]
    #[must_use]
    pub fn lerp(self, to: Self, t: f32) -> Self {
        self + (to - self) * t
    }

    /// Returns a `Vec4` linearly interpolated between `self` and `to`, with factor `t`.
    ///
    /// Clamps `t` between `0.0` and `1.0`.
    #[inline]
    #[must_use]
    pub fn lerp_clamped(self, to: Self, t: f32) -> Self {
        self.lerp(to, t.clamp(0.0, 1.0))
    }

    /// Returns a `Vec4` incrementally moved from `self` to `to`, by at most `max_delta` distance,
    /// without overshooting.
    #[inline]
    #[must_use]
    pub fn move_towards(self, to: Self, max_delta: f32) -> Self {
        let delta: Self = to - self;
        let distance: f32 = delta.length();

        if distance < Self::EPSILON || distance <= max_delta {
            return to;
        }

        self + delta / distance * max_delta
    }

    /// Returns a `[f32; 4]` copy of the components.
    #[inline]
    #[must_use]
    pub const fn to_array(self) -> [f32; 4] {
        [self.x, self.y, self.z, self.w]
    }

    /// Returns `true` if `self` and `other` approximately represent the same point or direction.
    ///
    /// Uses [`EPSILON`](Self::EPSILON) as threshold.
    #[inline]
    #[must_use]
    pub fn approx_eq(self, other: Self) -> bool {
        self.distance_squared(other) < Self::EPSILON_SQ
    }

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

    /// Returns `true` if any components of `self` is `NaN`.
    #[inline]
    #[must_use]
    pub const fn is_nan(self) -> bool {
        self.x.is_nan() || self.y.is_nan() || self.z.is_nan() || self.w.is_nan()
    }
}

impl Add for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with the coresponding components of `rhs`
    /// added to `self`.
    #[inline]
    fn add(self, rhs: Self) -> Self::Output {
        Self::new(
            self.x + rhs.x,
            self.y + rhs.y,
            self.z + rhs.z,
            self.w + rhs.w,
        )
    }
}

impl AddAssign for Vec4 {
    /// Adds the corresponding components of `rhs` to `self`.
    #[inline]
    fn add_assign(&mut self, rhs: Self) {
        self.x += rhs.x;
        self.y += rhs.y;
        self.z += rhs.z;
        self.w += rhs.w;
    }
}

impl Add<f32> for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with `rhs` added to each component of `self`.
    #[inline]
    fn add(self, rhs: f32) -> Self::Output {
        Self::new(self.x + rhs, self.y + rhs, self.z + rhs, self.w + rhs)
    }
}

impl AddAssign<f32> for Vec4 {
    /// Adds `rhs` to each component of `self`.
    #[inline]
    fn add_assign(&mut self, rhs: f32) {
        self.x += rhs;
        self.y += rhs;
        self.z += rhs;
        self.w += rhs;
    }
}

impl Sub for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with the corresponding components of `rhs`
    /// subtracted from `self`.
    #[inline]
    fn sub(self, rhs: Self) -> Self::Output {
        Self::new(
            self.x - rhs.x,
            self.y - rhs.y,
            self.z - rhs.z,
            self.w - rhs.w,
        )
    }
}

impl SubAssign for Vec4 {
    /// Subtracts the corresponding components of `rhs` from `self`.
    #[inline]
    fn sub_assign(&mut self, rhs: Self) {
        self.x -= rhs.x;
        self.y -= rhs.y;
        self.z -= rhs.z;
        self.w -= rhs.w;
    }
}

impl Sub<f32> for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with `rhs` subtracted from each component of `self`.
    #[inline]
    fn sub(self, rhs: f32) -> Self::Output {
        Self::new(self.x - rhs, self.y - rhs, self.z - rhs, self.w - rhs)
    }
}

impl SubAssign<f32> for Vec4 {
    /// Subtracts `rhs` from each component of `self`.
    #[inline]
    fn sub_assign(&mut self, rhs: f32) {
        self.x -= rhs;
        self.y -= rhs;
        self.z -= rhs;
        self.w -= rhs;
    }
}

impl Mul for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with the coresponding components of `self`
    /// multiplied by `rhs`.
    #[inline]
    fn mul(self, rhs: Self) -> Self::Output {
        Self::new(
            self.x * rhs.x,
            self.y * rhs.y,
            self.z * rhs.z,
            self.w * rhs.w,
        )
    }
}

impl MulAssign for Vec4 {
    /// Multiplies the corresponding components of `self` by `rhs`.
    #[inline]
    fn mul_assign(&mut self, rhs: Self) {
        self.x *= rhs.x;
        self.y *= rhs.y;
        self.z *= rhs.z;
        self.w *= rhs.w;
    }
}

impl Mul<f32> for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with each component of `self`
    /// multiplied by `rhs`.
    #[inline]
    fn mul(self, rhs: f32) -> Self::Output {
        Self::new(self.x * rhs, self.y * rhs, self.z * rhs, self.w * rhs)
    }
}

impl MulAssign<f32> for Vec4 {
    /// Multiplies each component of `self` by `rhs`.
    #[inline]
    fn mul_assign(&mut self, rhs: f32) {
        self.x *= rhs;
        self.y *= rhs;
        self.z *= rhs;
        self.w *= rhs;
    }
}

impl Div for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with the coresponding components of `self`
    /// divided by `rhs`.
    #[inline]
    fn div(self, rhs: Self) -> Self::Output {
        Self::new(
            self.x / rhs.x,
            self.y / rhs.y,
            self.z / rhs.z,
            self.w / rhs.w,
        )
    }
}

impl DivAssign for Vec4 {
    /// Divides the corresponding components of `self` by `rhs`.
    #[inline]
    fn div_assign(&mut self, rhs: Self) {
        self.x /= rhs.x;
        self.y /= rhs.y;
        self.z /= rhs.z;
        self.w /= rhs.w;
    }
}

impl Div<f32> for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with each component of `self`
    /// divided by `rhs`.
    #[inline]
    fn div(self, rhs: f32) -> Self::Output {
        Self::new(self.x / rhs, self.y / rhs, self.z / rhs, self.w / rhs)
    }
}

impl DivAssign<f32> for Vec4 {
    /// Divides each component of `self` by `rhs`.
    #[inline]
    fn div_assign(&mut self, rhs: f32) {
        self.x /= rhs;
        self.y /= rhs;
        self.z /= rhs;
        self.w /= rhs;
    }
}

impl Rem for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with the remainder of dividing corresponding components
    /// of `self` by `rhs`.
    #[inline]
    fn rem(self, rhs: Self) -> Self::Output {
        Self::new(
            self.x % rhs.x,
            self.y % rhs.y,
            self.z % rhs.z,
            self.w % rhs.w,
        )
    }
}

impl RemAssign for Vec4 {
    /// Sets each component of `self` to the remainder of dividing it by
    /// the corresponding component of `rhs`.
    #[inline]
    fn rem_assign(&mut self, rhs: Self) {
        self.x %= rhs.x;
        self.y %= rhs.y;
        self.z %= rhs.z;
        self.w %= rhs.w;
    }
}

impl Rem<f32> for Vec4 {
    type Output = Self;

    /// Returns a `Vec4` with the remainder of dividing each component
    /// of `self` by `rhs`.
    #[inline]
    fn rem(self, rhs: f32) -> Self::Output {
        Self::new(self.x % rhs, self.y % rhs, self.z % rhs, self.w % rhs)
    }
}

impl RemAssign<f32> for Vec4 {
    /// Sets each component of `self` to the remainder of dividing it by `rhs`.
    #[inline]
    fn rem_assign(&mut self, rhs: f32) {
        self.x %= rhs;
        self.y %= rhs;
        self.z %= rhs;
        self.w %= rhs;
    }
}

impl Neg for Vec4 {
    type Output = Self;

    /// Negates all components of `self`.
    #[inline]
    fn neg(self) -> Self::Output {
        Self::new(-self.x, -self.y, -self.z, -self.w)
    }
}

impl Index<usize> for Vec4 {
    type Output = f32;

    /// Returns a reference to the component at `index`.
    ///
    /// `0` maps to `x`, `1` maps to `y`, `2` maps to `z`, `3` maps to `w`.
    ///
    /// # Panics
    /// Panics if `index` is greater than `3`.
    #[inline]
    fn index(&self, index: usize) -> &Self::Output {
        match index {
            0 => &self.x,
            1 => &self.y,
            2 => &self.z,
            3 => &self.w,
            _ => panic!("index out of bounds"),
        }
    }
}

impl IndexMut<usize> for Vec4 {
    /// Returns a mutable reference to the component at `index`.
    ///
    /// `0` maps to `x`, `1` maps to `y`, `2` maps to `z`, `3` maps to `w`.
    ///
    /// # Panics
    /// Panics if `index` is greater than `3`.
    #[inline]
    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
        match index {
            0 => &mut self.x,
            1 => &mut self.y,
            2 => &mut self.z,
            3 => &mut self.w,
            _ => panic!("index out of bounds"),
        }
    }
}

impl AsRef<[f32; 4]> for Vec4 {
    /// Returns a `&[f32; 4]` reference to the `Vec4` components.
    /// Elements map to `[x, y, z, w]`.
    #[inline]
    fn as_ref(&self) -> &[f32; 4] {
        unsafe { &*(self as *const Vec4 as *const [f32; 4]) }
    }
}

impl AsMut<[f32; 4]> for Vec4 {
    /// Returns a `&[f32; 4]` mutable reference to the `Vec4` components.
    /// Elements map to `[x, y, z, w]`.
    #[inline]
    fn as_mut(&mut self) -> &mut [f32; 4] {
        unsafe { &mut *(self as *mut Vec4 as *mut [f32; 4]) }
    }
}

impl Display for Vec4 {
    /// Formats `self` as `(x, y, z, w)`.
    ///
    /// 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.x, p, self.y, p, self.z, p, self.w
            )
        } else {
            write!(f, "({}, {}, {}, {})", self.x, self.y, self.z, self.w)
        }
    }
}

impl From<[f32; 4]> for Vec4 {
    /// Creates a `Vec4` from a `[f32; 4]`.
    /// Elements map to `(x, y, z, w)`.
    #[inline]
    fn from(v: [f32; 4]) -> Self {
        Self::new(v[0], v[1], v[2], v[3])
    }
}

impl From<Vec4> for [f32; 4] {
    /// Creates a `[f32; 4]` from a `Vec4`.
    /// Elements map to `[x, y, z, w]`.
    #[inline]
    fn from(v: Vec4) -> Self {
        [v.x, v.y, v.z, v.w]
    }
}

impl From<&[f32]> for Vec4 {
    /// Creates a `Vec4` from a `&[f32]`.
    /// First 4 elements map to `(x, y, z, w)`.
    ///
    /// # Panics
    /// Panics if `v` is less than 4 elements long.
    #[inline]
    fn from(v: &[f32]) -> Self {
        Self::new(v[0], v[1], v[2], v[3])
    }
}

impl From<Vec2> for Vec4 {
    /// Creates a `Vec4` from a `Vec2`.
    /// Elements map to `(x, y, 0.0, 0.0)`.
    #[inline]
    fn from(v: Vec2) -> Self {
        Self::new(v.x, v.y, 0.0, 0.0)
    }
}

impl From<Vec3> for Vec4 {
    /// Creates a `Vec4` from a `Vec3`.
    /// Elements map to `(x, y, z, 0.0)`.
    #[inline]
    fn from(v: Vec3) -> Self {
        Self::new(v.x, v.y, v.z, 0.0)
    }
}

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