/**
 * @file Vector.h
 * @author Petr Egert (egertpet@fel.cvut.cz)
 * @brief Contains Vector2, Vector3 and Vector4 class declarations and implementations
 */

#ifndef VECTOR_H
#define VECTOR_H
#include <math.h>

namespace RD
{
    template<class T>
    class Matrix;
    typedef Matrix<float> mat44;

    template<class T>
    class Vector3;

    /**
     * @brief Class representing a two component vector
     */
    template<class T>
    class Vector2
    {
        public:
            T x, y;

            inline Vector2(){x = y = 0;}
            inline Vector2(T F){x = y = F;}
            inline Vector2(T X, T Y){x = X; y = Y;}
            inline Vector2(T * F){x = F[0]; y = F[1];}

            inline Vector2 operator = (const Vector2 &V){x = V.x; y = V.y; return *this;}
            inline Vector2 operator = (const Vector3<T> &V);
            inline Vector2 operator - (){return Vector2(-x, -y);}

            inline Vector2 operator + (const Vector2 &V){return Vector2(x + V.x, y + V.y);}
            inline Vector2 operator + (const T &F){return Vector2(x + F, y + F);}
            inline Vector2 operator - (const Vector2 &V){return Vector2(x - V.x, y - V.y);}
            inline Vector2 operator - (const T &F){return Vector2(x - F, y - F);}


            inline void operator += (const Vector2 &V){x += V.x; y += V.y;}
            inline void operator += (const T &F){x += F; y += F;}
            inline void operator -= (const Vector2 &V){x -= V.x; y -= V.y;}
            inline void operator -= (const T &F){x -= F; y -= F;}

            inline Vector2 operator * (const T &F){return Vector2(x * F, y * F);}
            inline Vector2 operator / (const T &F){if (F != 0.0) return Vector2(x / F, y / F); else return Vector2<T>(0.0);}
            inline void operator *= (const T &F){x *= F; y *= F;}
            inline void operator /= (const T &F){if(!F) return; x /= F; y /= F;}

            inline operator T* (){v[0] = x; v[1] = y; return v;}

            inline bool operator == (const Vector2 &V){return(x == V.x && y == V.y);}
            inline bool operator != (const Vector2 &V){return !(*this == V);}

            ///Returns the length of the vector
            inline T length(){return sqrt(x * x + y * y);}

            ///Normalizes the vector
            inline void normalize(){*this /= length();}

            ///Returns a normalized version of the vector
            inline Vector2 normalized(){return *this / length();}

            ///Computes a dot product of two vectors
            inline T dot(const Vector2 &V){return x * V.x + y * V.y;}

            ///Clamps the individual components of the vector between min and max
            inline void clamp(T min, T max)
            {
                if(min > max) return;
                x = (x < min) ? min : ((x > max) ? max : x);
                y = (y < min) ? min : ((y > max) ? max : y);
            }

        private:
            T v[2];
    };

    ///Typedef for a 2D vector consisting of floats
    typedef Vector2<float> vec2;

    /**
     * @brief Class representing a three component vector
     */
    template<class T>
    class Vector3
    {
        public:
            T x, y, z;

            inline Vector3(){x = y = z = 0;}
            inline Vector3(T F){x = y = z = F;}
            inline Vector3(T X, T Y, T Z){x = X; y = Y; z = Z;}
            inline Vector3(T * F){x = F[0]; y = F[1]; z = F[2];}
            inline Vector3(const Vector2<T> &V, T Z){x = V.x; y = V.y; z = Z;}

            inline Vector3 operator = (const Vector3 &V){x = V.x; y = V.y; z = V.z; return *this;}
            inline Vector3 operator = (const T &F){x = y = z = F; return *this;}
            inline Vector3 operator - (){return Vector3(-x, -y, -z);}

            inline Vector3 operator + (const Vector3 &V){return Vector3(x + V.x, y + V.y, z + V.z);}
            inline Vector3 operator + (const T &F){return Vector3(x + F, y + F, z + F);}
            inline Vector3 operator - (const Vector3 &V){return Vector3(x - V.x, y - V.y, z - V.z);}
            inline Vector3 operator - (const T &F){return Vector3(x - F, y - F, z - F);}

            inline void operator += (const Vector3 &V){x += V.x; y += V.y; z += V.z;}
            inline void operator += (const T &F){x += F; y += F; z += F;}
            inline void operator -= (const Vector3 &V){x -= V.x; y -= V.y; z -= V.z;}
            inline void operator -= (const T &F){x -= F; y -= F; z -= F;}

            inline Vector3 operator * (const T &F){return Vector3(x * F, y * F, z * F);}
            inline Vector3 operator / (const T &F){if (F != 0.0) return Vector3(x / F, y / F, z / F); else return Vector3(0.0);}
            inline void operator *= (const T &F){x *= F; y *= F; z *= F;}
            inline void operator /= (const T &F){if(!F) return; x /= F; y /= F; z /= F;}

    /*        Vector3 operator * (const mat44 &M)
            {
                return Vector3(x * M.m[0][0] + y * M.m[1][0] + z * M.m[2][0] + M.m[3][0],
                            x * M.m[0][1] + y * M.m[1][1] + z * M.m[2][1] + M.m[3][1],
                            x * M.m[0][2] + y * M.m[1][2] + z * M.m[2][2] + M.m[3][2]);
            }
            void operator *= (const mat44 &M){*this = *this * M;}
    */
            inline Vector3 operator * (const Vector3 &V){return Vector3(x * V.x, y * V.y, z * V.z);}
            inline void operator *= (const Vector3 &V){*this = *this * V;}

            inline operator T* (){v[0] = x; v[1] = y; v[2] = z; return v;}

            inline bool operator == (const Vector3 &V){return(x == V.x && y == V.y && z == V.z);}
            inline bool operator != (const Vector3 &V){return !(*this == V);}

            ///Returns the length of the vector
            inline T length(){return sqrt(x * x + y * y + z * z);}

            ///Normalizes the vector
            inline void normalize(){*this /= length();}

            ///Returns a normalized version of the vector
            inline Vector3 normalized(){return *this / length();}

            ///Computes a cross product of two vectors
            inline Vector3 cross(const Vector3 &V){return Vector3(y * V.z - z * V.y, z * V.x - x * V.z, x * V.y - y * V.x);}

            ///Computes a dot product of two vectors
            inline T dot(const Vector3 &V){return x * V.x + y * V.y + z * V.z;}

            ///Clamps the individual components of the vector between l and h
            inline void clamp(const Vector3 &l, const Vector3 &h)
            {
                x = (x < l.x) ? l.x : ((x > h.x) ? h.x : x);
                y = (y < l.y) ? l.y : ((y > h.y) ? h.y : y);
                z = (z < l.z) ? l.z : ((z > h.z) ? h.z : z);
            }

        private:
            T v[3];
    };

    template<class T>
    Vector2<T> Vector2<T>::operator = (const Vector3<T> &V){x = V.x; y = V.y; return *this;}

    ///Typedef of a 3D vector consisting of floats
    typedef Vector3<float> vec3;

    /**
     * @brief Class representing a four component vector
     */
    template<class T>
    class Vector4
    {
        public:
            T x, y, z, w;

            inline Vector4(){x = y = z = w = 0;};
            inline Vector4(T F){x = y = z = w = F;}
            inline Vector4(T X, T Y, T Z, T W){x = X; y = Y; z = Z; w = W;}
            inline Vector4(T * F){x = F[0]; y = F[1]; z = F[2]; w = F[3];}
            inline Vector4(const Vector3<T> &V, T W){x = V.x; y = V.y; z = V.z; w = W;}

            inline Vector4 operator = (const Vector4 &V){x = V.x; y = V.y; z = V.z; w = V.w; return *this;}
            inline Vector4 operator - (){return Vector4(-x, -y, -z, -w);}

            inline Vector4 operator + (const Vector4 &V){return Vector4(x + V.x, y + V.y, z + V.z, w + V.w);}
            inline Vector4 operator + (const T &F){return Vector4(x + F, y + F, z + F, w + F);}
            inline Vector4 operator - (const Vector4 &V){return Vector4(x - V.x, y - V.y, z - V.z, w - V.w);}
            inline Vector4 operator - (const T &F){return Vector4(x - F, y - F, z - F, w - F);}

            inline void operator += (const Vector4 &V){x += V.x; y += V.y; z += V.z; w += V.w;}
            inline void operator += (const T &F){x += F; y += F; z += F; w += F;}
            inline void operator -= (const Vector4 &V){x -= V.x; y -= V.y; z -= V.z; w -= V.w;}
            inline void operator -= (const T &F){x -= F; y -= F; z -= F; w -= F;}

            inline Vector4 operator * (const T &F){return Vector4(x * F, y * F, z * F, w * F);}
            inline Vector4 operator / (const T &F){if (F != 0.0) return Vector4(x / F, y / F, z / F, w / F); else return Vector4(0.0);}
            inline void operator *= (const T &F){x *= F; y *= F; z *= F; w *= F;}
            inline void operator /= (const T &F){if(!F) return; x /= F; y /= F; z /= F; w /= F;}

            inline operator T* (){v[0] = x; v[1] = y; v[2] = z; v[3] = w; return v;}

            inline bool operator == (const Vector4 &V){return(x == V.x && y == V.y && z == V.z && w == V.w);}
            inline bool operator != (const Vector4 &V){return !(*this == V);}

            ///Returns the length of the vector
            inline T length(){return sqrt(x * x + y * y + z * z + w * w);}

            ///Normalizes the vector
            inline void normalize(){*this /= length();}

            ///Returns a normalized version of the vector
            inline Vector4 normalized(){return *this / length();}

            ///Computes a dot product of two vectors
            inline T dot(const Vector4 &V){return x * V.x + y * V.y + z * V.z + w * V.w;}

        private:
            T v[4];
    };

    ///Typedef of a 4D vector consisting of floats
    typedef Vector4<float> vec4;

    ///Typedef of a 3D vector consisting of integers
    typedef Vector3<int> int3;
}
#endif  //VECTOR_H
