#pragma once

#define _HAS_VEC2

#include <cmath>
#include <cstdint>

template<typename T>
struct vec2
{
    union
    {
        struct{T x, y;};
        struct{T s, t;};
        struct{T u, v;};
        struct{T left, top;};
        T data[2];
    };

    constexpr vec2():x(0),y(0){}

    #ifdef B2_MATH_H
    constexpr vec2(const b2Vec2 &a):x(a.x),y(a.y){}
    constexpr operator b2Vec2() const{return b2Vec2(x,y);}
    operator b2Vec2() {return b2Vec2(x,y);}
    #endif // B2_MATH_H

    template<typename S>
    constexpr vec2(S a):x(a),y(a){}

    template<typename S, typename Q>
    constexpr vec2(S a, Q b):x(std::move(a)),y(std::move(b)){}

    template<typename S>
    constexpr vec2(vec2<S> a):x(std::move(a.x)),y(std::move(a.y)){}

    template<typename S>
    constexpr vec2<T>& operator+=(const vec2<S> &a) noexcept
    {
        x+=a.x;
        y+=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator-=(const vec2<S> &a) noexcept
    {
        x-=a.x;
        y-=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator*=(const vec2<S> &a) noexcept
    {
        x*=a.x;
        y*=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator*=(const S &a) noexcept
    {
        x*=a;
        y*=a;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator/=(const vec2<S> &a) noexcept
    {
        x/=a.x;
        y/=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator/=(const S &a) noexcept
    {
        x/=a;
        y/=a;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> &operator=(const vec2<S> &a) noexcept
    {
        x=a.x;
        y=a.y;
        return *this;
    }

    template <typename S>
    constexpr vec2<T> operator+(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x+a.x, y+a.y);
    }

    template <typename S>
    constexpr vec2<T> operator-(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x-a.x, y-a.y);
    }

    constexpr vec2<T> operator-() const noexcept
    {
        return vec2<T>(-x, -y);
    }

    template <typename S>
    constexpr vec2<T> operator*(const S &a) const noexcept
    {
        return vec2<T>(x*a, y*a);
    }

    template <typename S>
    constexpr vec2<T> operator*(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x*a.x, y*a.y);
    }

    template <typename S>
    constexpr vec2<T> operator/(const S &a) const noexcept
    {
        return vec2<T>(x/a, y/a);
    }

    template <typename S>
    constexpr vec2<T> operator/(const vec2<S> &a) const noexcept
    {
        return vec2<T>(x/a.x, y/a.y);
    }

    template <typename S>
    constexpr bool operator!=(const vec2<S> &a) const noexcept
    {
        return x != a.x || y != a.y;
    }

    template <typename S>
    constexpr bool operator==(const vec2<S> &a) const noexcept
    {
        return x == a.x && y == a.y;
    }

    template <typename S>
    constexpr bool operator>(const vec2<S> &a) const noexcept
    {
        return x*x+y*y > a.x*a.x+a.y*a.y;
    }

    template <typename S>
    constexpr bool operator>=(const vec2<S> &a) const noexcept
    {
        return x*x+y*y >= a.x*a.x+a.y*a.y;
    }

    template <typename S>
    constexpr bool operator<=(const vec2<S> &a) const noexcept
    {
        return !(operator>(a));
    }

    template <typename S>
    constexpr bool operator<(const vec2<S> &a) const noexcept
    {
        return !(operator>=(a));
    }

    inline T length() const noexcept{return sqrt(x*x+y*y);}
    inline vec2<T> normalize() const noexcept{return (*this/length());}

    constexpr T &max(){return max(x, y);}
    constexpr T &min(){return min(x, y);}

    constexpr T &operator[](uint32_t a){return *((&x)+a);}

    template <typename S>
    constexpr T dot(const vec2<S> &a) const noexcept
    {
        return T(x*a.x+y*a.y);
    }

    template <typename S>
    constexpr T cross(const vec2<S> &a) const noexcept
    {
        return T(x*a.y-y*a.x);
    }

    inline constexpr vec2<T> yx() const noexcept
    {
        return vec2<T>{y, x};
    }
};

typedef vec2<int8_t> vec2i8;
typedef vec2<int16_t> vec2i16;
typedef vec2<int32_t> vec2i32;

typedef vec2<uint8_t> vec2u8;
typedef vec2<uint16_t> vec2u16;
typedef vec2<uint32_t> vec2u32;

typedef vec2<float> vec2f;
typedef vec2<double> vec2d;
