use core::ops::{AddAssign, DivAssign, MulAssign, SubAssign};

pub trait Coordinate:
    Copy + Clone + PartialEq + AddAssign + MulAssign + SubAssign + DivAssign
{
}

impl Coordinate for usize {}
impl Coordinate for isize {}
impl Coordinate for u8 {}
impl Coordinate for i8 {}
impl Coordinate for u16 {}
impl Coordinate for i16 {}
impl Coordinate for u32 {}
impl Coordinate for i32 {}
impl Coordinate for u64 {}
impl Coordinate for i64 {}
impl Coordinate for u128 {}
impl Coordinate for i128 {}
impl Coordinate for f32 {}
impl Coordinate for f64 {}

#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Debug)]
pub struct Vec2<T: Coordinate> {
    x: T,
    y: T,
}

impl<T: Coordinate> Vec2<T> {
    pub const fn new(x: T, y: T) -> Self {
        Self { x, y }
    }

    pub fn into<S: Coordinate + From<T>>(&self) -> Vec2<S> {
        Vec2::new(self.x.into(), self.y.into())
    }

    pub const fn x(&self) -> T {
        self.x
    }

    pub const fn y(&self) -> T {
        self.y
    }

    pub const fn set_x(&mut self, x: T) {
        self.x = x;
    }

    pub const fn set_y(&mut self, y: T) {
        self.y = y;
    }
}

impl<T: Coordinate> AddAssign for Vec2<T> {
    fn add_assign(&mut self, rhs: Self) {
        self.x += rhs.x;
        self.y += rhs.y;
    }
}

impl<T: Coordinate> SubAssign for Vec2<T> {
    fn sub_assign(&mut self, rhs: Self) {
        self.x -= rhs.x;
        self.y -= rhs.y;
    }
}

impl<T: Coordinate> MulAssign<T> for Vec2<T> {
    fn mul_assign(&mut self, rhs: T) {
        self.x *= rhs;
        self.y *= rhs;
    }
}

impl<T: Coordinate> DivAssign<T> for Vec2<T> {
    fn div_assign(&mut self, rhs: T) {
        self.x /= rhs;
        self.y /= rhs;
    }
}

impl<T: Coordinate> From<Vec2<T>> for (T, T) {
    fn from(value: Vec2<T>) -> Self {
        (value.x, value.y)
    }
}
