use crate::{Alignment, Length, Scalar, SupportedLength, Vector};

/// Creates a [`Vector`]2 with the given elements.
///
/// If only a single element is given, it will be used for all elements of the
/// [`Vector`].
///
/// ## Examples
///
/// ```
/// use ggmath::{f32::Vec2f, vec2};
///
/// let v: Vec2f = vec2!(1.0, 2.0);
/// assert_eq!(v, Vec2f::from_array([1.0, 2.0]));
///
/// let v: Vec2f = vec2!(1.0);
/// assert_eq!(v, Vec2f::from_array([1.0, 1.0]));
/// ```
#[macro_export]
macro_rules! vec2 {
    ($($arg:expr),*$(,)?) => {
        $crate::Vector::<2, _, _>::from(($($arg,)*))
    };
}

pub use vec2;

/// Creates a [`Vector`]3 with the given elements. Also accepts [`Vector`]s as
/// arguments, as long as their lengths sum up to 3.
///
/// If only a single element is given, it will be used for all elements of the
/// [`Vector`].
///
/// ## Examples
///
/// ```
/// use ggmath::{f32::Vec3f, vec2, vec3};
///
/// let v: Vec3f = vec3!(1.0, 2.0, 3.0);
/// assert_eq!(v, Vec3f::from_array([1.0, 2.0, 3.0]));
///
/// let v: Vec3f = vec3!(1.0, vec2!(2.0, 3.0));
/// assert_eq!(v, Vec3f::from_array([1.0, 2.0, 3.0]));
///
/// let v: Vec3f = vec3!(1.0);
/// assert_eq!(v, Vec3f::from_array([1.0, 1.0, 1.0]));
/// ```
#[macro_export]
macro_rules! vec3 {
    ($($arg:expr),*$(,)?) => {
        $crate::Vector::<3, _, _>::from(($($arg,)*))
    };
}

pub use vec3;

/// Creates a [`Vector`]4 with the given elements. Also accepts [`Vector`]s as
/// arguments, as long as their lengths sum up to 4.
///
/// If only a single element is given, it will be used for all elements of the
/// [`Vector`].
///
/// ## Examples
///
/// ```
/// use ggmath::{f32::Vec4f, vec2, vec4};
///
/// let v: Vec4f = vec4!(1.0, 2.0, 3.0, 4.0);
/// assert_eq!(v, Vec4f::from_array([1.0, 2.0, 3.0, 4.0]));
///
/// let v: Vec4f = vec4!(1.0, vec2!(2.0, 3.0), 4.0);
/// assert_eq!(v, Vec4f::from_array([1.0, 2.0, 3.0, 4.0]));
///
/// let v: Vec4f = vec4!(1.0);
/// assert_eq!(v, Vec4f::from_array([1.0, 1.0, 1.0, 1.0]));
/// ```
#[macro_export]
macro_rules! vec4 {
    ($($arg:expr),*$(,)?) => {
        $crate::Vector::<4, _, _>::from(($($arg,)*))
    };
}

pub use vec4;

////////////////////////////////////////////////////////////////////////////////
// Generic Length Implementations
////////////////////////////////////////////////////////////////////////////////

impl<const N: usize, T: Scalar, A: Alignment> From<(Vector<N, T, A>,)> for Vector<N, T, A>
where
    Length<N>: SupportedLength,
{
    #[inline(always)]
    fn from(value: (Vector<N, T, A>,)) -> Self {
        value.0
    }
}

impl<const N: usize, T: Scalar, A: Alignment> From<(T,)> for Vector<N, T, A>
where
    Length<N>: SupportedLength,
{
    #[inline(always)]
    fn from(value: (T,)) -> Self {
        Vector::splat(value.0)
    }
}

////////////////////////////////////////////////////////////////////////////////
// Vector2 Implementations
////////////////////////////////////////////////////////////////////////////////

impl<T: Scalar, A: Alignment> From<(T, T)> for Vector<2, T, A> {
    #[inline(always)]
    fn from(value: (T, T)) -> Self {
        Vector::from_array([value.0, value.1])
    }
}

////////////////////////////////////////////////////////////////////////////////
// Vector3 Implementations
////////////////////////////////////////////////////////////////////////////////

impl<T: Scalar, A: Alignment> From<(T, T, T)> for Vector<3, T, A> {
    #[inline(always)]
    fn from(value: (T, T, T)) -> Self {
        Vector::from_array([value.0, value.1, value.2])
    }
}

impl<T: Scalar, A: Alignment> From<(T, Vector<2, T, A>)> for Vector<3, T, A> {
    #[inline(always)]
    fn from(value: (T, Vector<2, T, A>)) -> Self {
        Vector::from_array([value.0, value.1[0], value.1[1]])
    }
}

impl<T: Scalar, A: Alignment> From<(Vector<2, T, A>, T)> for Vector<3, T, A> {
    #[inline(always)]
    fn from(value: (Vector<2, T, A>, T)) -> Self {
        Vector::from_array([value.0[0], value.0[1], value.1])
    }
}

////////////////////////////////////////////////////////////////////////////////
// Vector4 Implementations
////////////////////////////////////////////////////////////////////////////////

impl<T: Scalar, A: Alignment> From<(T, T, T, T)> for Vector<4, T, A> {
    #[inline(always)]
    fn from(value: (T, T, T, T)) -> Self {
        Vector::from_array([value.0, value.1, value.2, value.3])
    }
}

impl<T: Scalar, A: Alignment> From<(T, T, Vector<2, T, A>)> for Vector<4, T, A> {
    #[inline(always)]
    fn from(value: (T, T, Vector<2, T, A>)) -> Self {
        Vector::from_array([value.0, value.1, value.2[0], value.2[1]])
    }
}

impl<T: Scalar, A: Alignment> From<(T, Vector<2, T, A>, T)> for Vector<4, T, A> {
    #[inline(always)]
    fn from(value: (T, Vector<2, T, A>, T)) -> Self {
        Vector::from_array([value.0, value.1[0], value.1[1], value.2])
    }
}

impl<T: Scalar, A: Alignment> From<(T, Vector<3, T, A>)> for Vector<4, T, A> {
    #[inline(always)]
    fn from(value: (T, Vector<3, T, A>)) -> Self {
        Vector::from_array([value.0, value.1[0], value.1[1], value.1[2]])
    }
}

impl<T: Scalar, A: Alignment> From<(Vector<2, T, A>, T, T)> for Vector<4, T, A> {
    #[inline(always)]
    fn from(value: (Vector<2, T, A>, T, T)) -> Self {
        Vector::from_array([value.0[0], value.0[1], value.1, value.2])
    }
}

impl<T: Scalar, A: Alignment> From<(Vector<2, T, A>, Vector<2, T, A>)> for Vector<4, T, A> {
    #[inline(always)]
    fn from(value: (Vector<2, T, A>, Vector<2, T, A>)) -> Self {
        Vector::from_array([value.0[0], value.0[1], value.1[0], value.1[1]])
    }
}

impl<T: Scalar, A: Alignment> From<(Vector<3, T, A>, T)> for Vector<4, T, A> {
    #[inline(always)]
    fn from(value: (Vector<3, T, A>, T)) -> Self {
        Vector::from_array([value.0[0], value.0[1], value.0[2], value.1])
    }
}
