#![allow(dead_code)]

use std::convert::{AsMut, AsRef};
use std::fmt::{Debug, Formatter, Result as FmtResult};
use std::ops::{Add, Deref, DerefMut, Mul, Sub};

#[cfg(feature = "serde")]
use serde::{
    de::{Deserialize, Deserializer, Error},
    ser::{Serialize, Serializer},
};

pub use super::{
    angle::Angle,
    numeric::{Float, Numeric},
};

macro_rules! first_ptr {
    ($this:ident, $first:ident $(, $other:ident)*) => {
        unsafe { &$this.$first }
    };
}

macro_rules! define_vec {
    ($Name:ident, $size:tt, $($T:ident => $i:tt => $f:ident),*) => {
        #[repr(C, packed)]
        pub struct $Name<T> {
            $(pub $f: T,)+
        }

        impl<T> $Name<T> {
            #[inline]
            pub fn new($($f: T,)+) -> Self {
                Self { $($f,)+ }
            }

            #[inline]
            pub fn as_ptr(&self) -> * const T {
                first_ptr!(self $(, $f)+)
            }
        }

        impl<T> Default for $Name<T>
        where
            T: Default
        {
            #[inline]
            fn default() -> Self {
                Self {
                    $($f: T::default(),)+
                }
            }
        }

        impl<T> Debug for $Name<T>
        where
            T: Debug
        {
            fn fmt(&self, fmt: &mut Formatter<'_>) -> FmtResult {
                fmt.debug_list().entries(self.as_ref().iter()).finish()
            }
        }

        impl<T, S> PartialEq<$Name<S>> for $Name<T>
        where
            S: PartialEq<T>,
        {
            fn eq(&self, other: &$Name<S>) -> bool {
                unsafe { true $(&& other.$f.eq(&self.$f))+ }
            }
        }

        impl<T> Eq for $Name<T>
        where
            T: Eq
            { }

        impl<T> Deref for $Name<T> {
            type Target = [T; $size];

            #[inline]
            fn deref(&self) -> &Self::Target {
                self.as_ref()
            }
        }

        impl<T> DerefMut for $Name<T> {
            #[inline]
            fn deref_mut(&mut self) -> &mut Self::Target {
                self.as_mut()
            }
        }

        impl<T> AsRef<[T; $size]> for $Name<T> {
            #[inline]
            fn as_ref(&self) -> &[T; $size] {
                unsafe {
                    let raw: * const _ = self;
                    let raw = raw as * const [T; $size];

                    &*raw
                }
            }
        }

        impl<T> AsMut<[T; $size]> for $Name<T> {
            #[inline]
            fn as_mut(&mut self) -> &mut [T; $size] {
                unsafe {
                    let raw: * mut _ = self;
                    let raw = raw as * mut [T; $size];

                    &mut *raw
                }
            }
        }

        impl<T> Clone for $Name<T>
        where
            T: Clone
        {
            #[inline]
            fn clone(&self) -> Self {
                unsafe {
                    Self {
                        $($f: self.$f.clone(),)+
                    }
                }
            }
        }

        impl<T> Copy for $Name<T>
        where
            T: Copy
            { }

        impl<T> From<[T; $size]> for $Name<T>
        where
            T: Copy,
        {
            #[inline]
            fn from(arr: [T; $size]) -> Self {
                Self {
                    $($f: arr[$i],)+
                }
            }
        }

        impl<T> From<T> for $Name<T>
        where
            T: Copy,
        {
            #[inline]
            fn from(value: T) -> Self {
                Self {
                    $($f: value,)+
                }
            }
        }

        impl<T> From<($($T,)+)> for $Name<T> {
            #[inline]
            fn from(($($f,)+): ($($T,)+)) -> Self {
                Self {
                    $($f,)+
                }
            }
        }

        #[cfg(feature = "serde")]
        impl<T> Serialize for $Name<T>
        where
            T: Serialize,
        {
            fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
            where
                S: Serializer
            {
                unsafe { vec![$(&self.$f,)+].serialize(serializer) }
            }
        }

        #[cfg(feature = "serde")]
        impl<'de, T> Deserialize<'de> for $Name<T>
        where
            T: Deserialize<'de>,
        {
            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
            where
                D: Deserializer<'de>
            {
                let mut data = Vec::<T>::deserialize(deserializer)?.into_iter();

                Ok(Self {
                    $($f: data.next().ok_or_else(|| D::Error::custom("Vector is missing some elements!"))?,)+
                })
            }
        }
    };
}

define_vec!(Vector2, 2, T => 0 => x, T => 1 => y);
define_vec!(Vector3, 3, T => 0 => x, T => 1 => y, T => 2 => z);
define_vec!(Vector4, 4, T => 0 => x, T => 1 => y, T => 2 => z, T => 3 => w);

pub type Vector2f = Vector2<gl::GLfloat>;
pub type Vector3f = Vector3<gl::GLfloat>;
pub type Vector4f = Vector4<gl::GLfloat>;

pub type Vector2d = Vector2<gl::GLdouble>;
pub type Vector3d = Vector3<gl::GLdouble>;
pub type Vector4d = Vector4<gl::GLdouble>;

pub type Vector2i = Vector2<gl::GLint>;
pub type Vector3i = Vector3<gl::GLint>;
pub type Vector4i = Vector4<gl::GLint>;

/* Vector2 */

impl<T> Vector2<T>
where
    T: Numeric,
{
    #[inline]
    pub fn multiply(mut self, v: T) -> Self {
        self.x = self.x * v;
        self.y = self.y * v;

        self
    }

    #[inline]
    pub fn length_sqr(&self) -> T {
        self.x * self.x + self.y * self.y
    }

    #[inline]
    pub fn length(&self) -> T {
        Numeric::sqrt(self.length_sqr())
    }

    #[inline]
    pub fn normalize(mut self) -> Self {
        let len = self.length();

        self.x = self.x / len;
        self.y = self.y / len;

        self
    }

    #[inline]
    pub fn scalar(&self, other: &Self) -> T {
        self.x * other.x + self.y * other.y
    }
}

impl<T> Vector2<T>
where
    T: Float,
{
    #[inline]
    pub fn angle(&self, other: &Self) -> Angle<T> {
        let s = self.scalar(other);
        let l1 = self.length();
        let l2 = self.length();

        Angle::Rad(T::acos(s / (l1 + l2)))
    }

    #[inline]
    pub fn angle2(&self, other: &Self) -> Angle<T> {
        Angle::Rad(T::atan2(
            other.x * self.y - other.y * self.x,
            other.x * self.x + other.y * self.y,
        ))
    }
}

impl<T> Add for Vector2<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn add(mut self, other: Self) -> Self::Output {
        self.x = self.x + other.x;
        self.y = self.y + other.y;

        self
    }
}

impl<T> Sub for Vector2<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn sub(mut self, other: Self) -> Self::Output {
        self.x = self.x - other.x;
        self.y = self.y - other.y;

        self
    }
}

impl<T> Mul for Vector2<T>
where
    T: Numeric,
{
    type Output = T;

    #[inline]
    fn mul(self, rhs: Self) -> Self::Output {
        self.scalar(&rhs)
    }
}

impl<T> Mul<T> for Vector2<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn mul(self, rhs: T) -> Self::Output {
        self.multiply(rhs)
    }
}

/* Vector3 */

impl<T> Vector3<T>
where
    T: Numeric,
{
    #[inline]
    pub fn multiply(mut self, v: T) -> Self {
        self.x = self.x * v;
        self.y = self.y * v;
        self.z = self.z * v;

        self
    }

    #[inline]
    pub fn length_sqr(&self) -> T {
        self.x * self.x + self.y * self.y + self.z * self.z
    }

    #[inline]
    pub fn length(&self) -> T {
        Numeric::sqrt(self.length_sqr())
    }

    #[inline]
    pub fn normalize(mut self) -> Self {
        let len = self.length();

        self.x = self.x / len;
        self.y = self.y / len;
        self.z = self.z / len;

        self
    }

    #[inline]
    pub fn scalar(&self, other: &Self) -> T {
        self.x * other.x + self.y * other.y + self.z * other.z
    }

    #[inline]
    pub fn cross(&self, other: &Self) -> Self {
        Self {
            x: self.y * other.z - self.z * other.y,
            y: self.z * other.x - self.x * other.z,
            z: self.x * other.y - self.y * other.x,
        }
    }
}

impl<T> Vector3<T>
where
    T: Float,
{
    #[inline]
    pub fn angle(&self, other: &Self) -> Angle<T> {
        let s = self.scalar(other);
        let l1 = self.length();
        let l2 = self.length();

        Angle::Rad(T::acos(s / (l1 + l2)))
    }
}

impl<T> Add for Vector3<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn add(mut self, other: Self) -> Self::Output {
        self.x = self.x + other.x;
        self.y = self.y + other.y;
        self.z = self.z + other.z;

        self
    }
}

impl<T> Sub for Vector3<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn sub(mut self, other: Self) -> Self::Output {
        self.x = self.x - other.x;
        self.y = self.y - other.y;
        self.z = self.z - other.z;

        self
    }
}

impl<T> Mul for Vector3<T>
where
    T: Numeric,
{
    type Output = T;

    #[inline]
    fn mul(self, rhs: Self) -> Self::Output {
        self.scalar(&rhs)
    }
}

impl<T> Mul<T> for Vector3<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn mul(self, rhs: T) -> Self::Output {
        self.multiply(rhs)
    }
}

/* Vector4 */

impl<T> Vector4<T>
where
    T: Numeric,
{
    #[inline]
    pub fn multiply(mut self, v: T) -> Self {
        self.x = self.x * v;
        self.y = self.y * v;
        self.z = self.z * v;

        self
    }

    #[inline]
    pub fn length_sqr(self) -> T {
        self.xyz().length_sqr()
    }

    #[inline]
    pub fn length(self) -> T {
        self.xyz().length()
    }

    #[inline]
    pub fn normalize(mut self) -> Self {
        let len = self.length();

        if unsafe { !Numeric::is_zero(&self.w) } {
            self.x = self.x / self.w;
            self.y = self.y / self.w;
            self.z = self.z / self.w;
        } else {
            self.x = Numeric::zero();
            self.y = Numeric::zero();
            self.z = Numeric::zero();
        }

        self.x = self.x / len;
        self.y = self.y / len;
        self.z = self.z / len;
        self.w = Numeric::one();

        self
    }

    #[inline]
    pub fn scalar(&self, other: &Self) -> T {
        self.xyz().scalar(&other.xyz())
    }

    #[inline]
    pub fn cross(&self, other: &Self) -> Self {
        (self.xyz().cross(&other.xyz()), Numeric::one()).into()
    }

    #[inline]
    pub fn xyz(self) -> Vector3<T> {
        if unsafe { Numeric::is_zero(&self.w) } {
            Vector3 {
                x: Numeric::zero(),
                y: Numeric::zero(),
                z: Numeric::zero(),
            }
        } else {
            Vector3 {
                x: self.x / self.w,
                y: self.y / self.w,
                z: self.z / self.w,
            }
        }
    }
}

impl<T> Vector4<T>
where
    T: Float,
{
    #[inline]
    pub fn angle(&self, other: &Self) -> Angle<T> {
        self.xyz().angle(&other.xyz())
    }
}

impl<T> Add for Vector4<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn add(self, other: Self) -> Self::Output {
        (self.xyz() + other.xyz(), T::one()).into()
    }
}

impl<T> Sub for Vector4<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn sub(self, other: Self) -> Self::Output {
        (self.xyz() - other.xyz(), T::one()).into()
    }
}

impl<T> Mul for Vector4<T>
where
    T: Numeric,
{
    type Output = T;

    fn mul(self, rhs: Self) -> Self::Output {
        self.scalar(&rhs)
    }
}

impl<T> Mul<T> for Vector4<T>
where
    T: Numeric,
{
    type Output = Self;

    #[inline]
    fn mul(self, rhs: T) -> Self::Output {
        self.multiply(rhs)
    }
}

impl<T> From<(Vector3<T>, T)> for Vector4<T> {
    fn from((vec3, w): (Vector3<T>, T)) -> Self {
        Self {
            x: vec3.x,
            y: vec3.y,
            z: vec3.z,
            w,
        }
    }
}

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

    #[test]
    fn add() {
        let v1 = Vector2f::from((1.0, 0.0));
        let v2 = Vector2f::from((0.0, 1.0));
        let ex = Vector2f::from((1.0, 1.0));

        assert_eq!(ex, v1 + v2);

        let v1 = Vector3f::from((1.0, 0.0, 1.8));
        let v2 = Vector3f::from((0.0, 2.0, 1.2));
        let ex = Vector3f::from((1.0, 2.0, 3.0));

        assert_eq!(ex, v1 + v2);

        let v1 = Vector4f::from((1.0, 0.0, 1.8, 0.5));
        let v2 = Vector4f::from((0.0, 2.0, 1.2, 1.0));
        let ex = Vector4f::from((2.0, 2.0, 4.8, 1.0));

        assert_eq!(ex, v1 + v2);
    }

    #[test]
    fn sub() {
        let v1 = Vector2f::from((1.0, 0.0));
        let v2 = Vector2f::from((0.0, 1.0));
        let ex = Vector2f::from((1.0, -1.0));

        assert_eq!(ex, v1 - v2);

        let v1 = Vector3d::from((1.0, 0.0, 1.75));
        let v2 = Vector3d::from((0.0, 2.0, 1.0));
        let ex = Vector3d::from((1.0, -2.0, 0.75));

        assert_eq!(ex, v1 - v2);

        let v1 = Vector4d::from((1.0, 0.0, 1.0, 0.5));
        let v2 = Vector4d::from((0.0, 2.0, -2.0, 1.0));
        let ex = Vector4d::from((2.0, -2.0, 4.0, 1.0));

        assert_eq!(ex, v1 - v2);
    }

    #[test]
    fn multiply() {
        let v1 = Vector2f::from((1.0, 0.0)).multiply(2.0);
        let ex = Vector2f::from((2.0, 0.0));

        assert_eq!(ex, v1);

        let v1 = Vector3d::from((1.0, 0.0, 1.75)).multiply(2.0);
        let ex = Vector3d::from((2.0, 0.0, 3.5));

        assert_eq!(ex, v1);

        let v1 = Vector4d::from((1.0, 0.0, 0.5, 0.5)).multiply(3.0);
        let ex = Vector4d::from((3.0, 0.0, 1.5, 0.5));

        assert_eq!(ex, v1);
    }

    #[test]
    fn scalar() {
        let v1 = Vector2f::from((1.0, 0.0));
        let v2 = Vector2f::from((0.0, 1.0));

        assert_eq!(0.0, v1.scalar(&v2));

        let v1 = Vector3f::from((1.0, 0.0, 0.0));
        let v2 = Vector3f::from((0.0, 1.0, 0.0));

        assert_eq!(0.0, v1.scalar(&v2));

        let v1 = Vector4f::from((3.0, 0.0, 0.0, 1.0));
        let v2 = Vector4f::from((0.0, 2.0, 0.0, 0.8));

        assert_eq!(0.0, v1.scalar(&v2));
    }

    #[test]
    fn normalize() {
        let v1 = Vector2f::from((1.0, 0.0)).normalize();
        let ex = Vector2f::from((1.0, 0.0));

        assert_eq!(ex, v1);

        let v1 = Vector3d::from((1.0, 0.0, 1.75)).normalize();
        let ex = Vector3d::from((0.49613893835683387, 0.0, 0.8682431421244593));

        assert_eq!(ex, v1);

        let v1 = Vector4d::from((1.0, 0.0, 0.5, 0.5)).normalize();
        let ex = Vector4d::from((0.8944271909999159, 0.0, 0.4472135954999579, 1.0));

        assert_eq!(ex, v1);
    }

    #[test]
    fn cross() {
        let v1 = Vector3d::from((1.0, 0.0, 0.0));
        let v2 = Vector3d::from((0.0, 1.0, 0.0));
        let ex = Vector3d::from((0.0, 0.0, 1.0));

        assert_eq!(ex, v1.cross(&v2));

        let v1 = Vector4d::from((1.0, 0.0, 0.0, 0.5));
        let v2 = Vector4d::from((0.0, 3.0, 0.0, 1.0));
        let ex = Vector4d::from((0.0, 0.0, 6.0, 1.0));

        assert_eq!(ex, v1.cross(&v2));
    }
}
