#pragma once

#include "vec3.h"

template<typename T>
struct vec4
{
    union
    {
        struct{T x, y, z, w;};
        struct{
            union{
            struct{T r, g, b;};
            struct{T h, s, v;};};
            T a;
        };
        struct{T left, top, right, bottom;};

        T data[4];
    };

    constexpr vec4():x(0),y(0),z(0),w(0){}

    template<typename S>
    constexpr vec4(const S &a):x(a),y(a),z(a),w(a){}

    template<typename A, typename B, typename C, typename D>
    constexpr vec4(const A &_1, const B &_2, const C &_3 = 0, const D &_4 = 0):x(_1),y(_2),z(_3),w(_4){}



    template<typename S>
    constexpr vec4(const vec2<S> &_1, const S &_2 = 0, const S &_3 = 0):x(_1.x),y(_1.y),z(_2),w(_3){}

    template<typename S>
    constexpr vec4(S _1, const vec2<S> &_2, const S &_3 = 0):x(_1),y(_2.x),z(_2.y),w(_3){}

    template<typename S>
    constexpr vec4(S _1, S _2, const vec2<S> &_3 = 0):x(_1),y(_2),z(_3.x),w(_3.y){}

    template<typename S>
    constexpr vec4(const vec2<S> &_1, const vec2<S> &_2):x(_1.x),y(_1.y),z(_2.x),w(_2.y){}

    template<typename S>
    constexpr vec4(const vec3<S> &_1, S _2 = 1):x(_1.x),y(_1.y),z(_1.z),w(_2){}

    template<typename S>
    constexpr vec4(S _1, const vec3<S> &_2):x(_1),y(_1.x),z(_1.y),w(_1.z){}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    constexpr vec3<T> xyz() const noexcept
    {
        return vec3<T>(x, y, z);
    }

};

#define IM_VEC4_CLASS_EXTRA ImVec4(const vec4<float> &a){x=a.x;y=a.y;z=a.z;w=a.w;}

typedef vec4<int8_t> vec4i8;
typedef vec4<int16_t> vec4i16;
typedef vec4<int32_t> vec4i32;

typedef vec4<uint8_t> vec4u8;
typedef vec4<uint16_t> vec4u16;
typedef vec4<uint32_t> vec4u32;

typedef vec4<float> vec4f;
typedef vec4<double> vec4d;
