use bevy::reflect::Reflect;
use core::{fmt, ops};

use super::VectorComponent;

#[derive(PartialEq, Eq, Hash, Clone, Copy, Default, Debug, Reflect)]
pub struct Vector2<T: VectorComponent> {
    pub x: T,
    pub y: T,
}

impl<T: VectorComponent> Vector2<T> {
    pub fn x() -> Self {
        Self {
            x: T::one(),
            y: T::zero(),
        }
    }
    pub fn y() -> Self {
        Self {
            x: T::zero(),
            y: T::one(),
        }
    }
    pub fn zero() -> Self {
        Self {
            x: T::zero(),
            y: T::zero(),
        }
    }
    pub fn one() -> Self {
        Self {
            x: T::one(),
            y: T::one(),
        }
    }
    pub fn up() -> Self {
        Self {
            x: T::zero(),
            y: T::one(),
        }
    }
    pub fn down() -> Self {
        Self {
            x: T::zero(),
            y: -T::one(),
        }
    }
    pub fn left() -> Self {
        Self {
            x: -T::one(),
            y: T::zero(),
        }
    }
    pub fn right() -> Self {
        Self {
            x: T::one(),
            y: T::zero(),
        }
    }

    pub fn new(x: T, y: T) -> Vector2<T> {
        Vector2 { x, y }
    }

    pub fn min(&self, other: &Vector2<T>) -> Vector2<T> {
        Vector2 {
            x: VectorComponent::min(self.x, other.x),
            y: VectorComponent::min(self.y, other.y),
        }
    }

    pub fn max(&self, other: &Vector2<T>) -> Vector2<T> {
        Vector2 {
            x: VectorComponent::max(self.x, other.x),
            y: VectorComponent::max(self.y, other.y),
        }
    }
}

impl<T: VectorComponent> fmt::Display for Vector2<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}

impl<T: VectorComponent> ops::Add<Vector2<T>> for Vector2<T> {
    type Output = Vector2<T>;
    fn add(self, rhs: Vector2<T>) -> Self::Output {
        Vector2 {
            x: self.x + rhs.x,
            y: self.y + rhs.y,
        }
    }
}

impl<T: VectorComponent> ops::Neg for Vector2<T> {
    type Output = Vector2<T>;
    fn neg(self) -> Self::Output {
        Vector2 {
            x: -self.x,
            y: -self.y,
        }
    }
}

impl<T: VectorComponent> ops::Sub<Vector2<T>> for Vector2<T> {
    type Output = Vector2<T>;
    fn sub(self, rhs: Vector2<T>) -> Self::Output {
        self + (-rhs)
    }
}

impl<T: VectorComponent> ops::Mul<Vector2<T>> for Vector2<T> {
    type Output = Vector2<T>;
    fn mul(self, rhs: Vector2<T>) -> Self::Output {
        Vector2 {
            x: self.x * rhs.x,
            y: self.y * rhs.y,
        }
    }
}

impl<T: VectorComponent> ops::Mul<T> for Vector2<T> {
    type Output = Vector2<T>;
    fn mul(self, rhs: T) -> Self::Output {
        Vector2 {
            x: self.x * rhs,
            y: self.y * rhs,
        }
    }
}

impl<T: VectorComponent> ops::Div<Vector2<T>> for Vector2<T> {
    type Output = Vector2<T>;
    fn div(self, rhs: Vector2<T>) -> Self::Output {
        Vector2 {
            x: self.x / rhs.x,
            y: self.y / rhs.y,
        }
    }
}

impl<T: VectorComponent> ops::Div<T> for Vector2<T> {
    type Output = Vector2<T>;
    fn div(self, rhs: T) -> Self::Output {
        Vector2 {
            x: self.x / rhs,
            y: self.y / rhs,
        }
    }
}
