#ifndef VMATH_H_
#define VMATH_H_

//#include <GL/glew.h>
//#include <GL/gl.h>
#include <cmath>

#ifndef M_PI
#define M_PI 3.1415926535897932384626433832795
#endif


#define DEG2RAD(x) ((x * M_PI) / 180.0)

#ifdef VMATH_NAMESPACE
namespace VMATH_NAMESPACE
{
#endif


template < class T >
class Vector2 {
public:
	union {T x; T s; };
	union {T y; T t; };
    //T x, y;

    //----------------[ constructors ]--------------------------
    Vector2 < T > () {
        x = y = 0;
    };

    Vector2 < T > (T x, T y) {
        this->x = x; this->y = y;
    };


    //----------------[ access operators ]-------------------
    Vector2 < T > & operator = (Vector2 < T > v) {
        x = v.x;
        y = v.y;
        return * this;
    };

    T & operator[] (int n) {
        if (0 == n) return x;
        else
            return y;
    };


    //---------------[ vector aritmetic operator ]--------------
    Vector2 < T > operator + (Vector2 < T > v) {
        return Vector2 < T > (x + v.x, y + v.y);
    };

    Vector2 < T > operator - (Vector2 < T > v) {
        return Vector2 < T > (x - v.x, y - v.y);
    };

    Vector2 < T > operator * (Vector2 < T > v) {
        return Vector2 < T > (x * v.x, y * v.y);
    };

    Vector2 < T > operator / (Vector2 < T > v) {
        return Vector2 < T > (x / v.x, y / v.y);
    };

    Vector2 < T > & operator += (Vector2 < T > v) {
        x += v.x;
        y += v.y;
        return * this;
    };

    Vector2 < T > & operator -= (Vector2 < T > v) {
        x -= v.x;
        y -= v.y;
        return * this;
    };

    Vector2 < T > & operator *= (Vector2 < T > v) {
        x *= v.x;
        y *= v.y;
        return * this;
    };

    Vector2 < T > & operator /= (Vector2 < T > v) {
        x /= v.x;
        y /= v.y;
        return * this;
    };

    //--------------[ scalar vector operator ]--------------------
    Vector2 < T > operator + (T n) {
        return Vector2 < T > (x + n, y + n);
    };

    Vector2 < T > operator - (T n) {
        return Vector2 < T > (x - n, y - n);
    };

    Vector2 < T > operator * (T n) {
        return Vector2 < T > (x * n, y * n);
    };

    Vector2 < T > operator / (T n) {
        return Vector2 < T > (x / n, y / n);
    };

    Vector2 < T > & operator += (T n) {
        x += n;
        y += n;
        return * this;
    };

    Vector2 < T > & operator -= (T n) {
        x -= n;
        y -= n;
        return * this;
    };

    Vector2 < T > & operator *= (T n) {
        x *= n;
        y *= n;
        return * this;
    };

    Vector2 < T > & operator /= (T n) {
        x /= n;
        y /= n;
        return * this;
    };

    //-------------[ size operations ]---------------------------
    float size() {
        return std::sqrt(x * x + y * y);
    };

    void normalize() {
        T s = size();
        x /= s;
        y /= s;
    };

	T sizeSq(){
		return x * x + y * y;
	}

	//-------------[ voodoo xexexe ]-----------------------------

	T crossProduct(const Vector2<T>& v)
	{
		return x * v.y - y * v.x;
	}

    //-------------[ GL operations ]-----------------------------

    /*void glTexCoord() {
        glTexCoord2f(x, y);
    };*/
    
    //-------------[ conversion ]-----------------------------
    operator T*(){ return (T*) this; }
    operator const T*(){ return (const T*) this; }


};



typedef class Vector2 < float > Vector2f;
typedef class Vector2 < double > Vector2d;



template < class T >
class Vector3 {
public:
    //T x, y, z;
	union{T x; T s; T r; };
	union{T y; T t; T g; };
	union{T z; T u; T b; };

    //----------------[ constructors ]--------------------------
    Vector3() {
        x = y = z = 0;
    };

    Vector3(T x, T y, T z) {
        this->x = x; this->y = y; this->z = z;
    };


    //----------------[ access operators ]-------------------
    Vector3 < T > operator = (Vector3 < T > v) {
        x = v.x;
        y = v.y;
        z = v.z;
        return * this;
    };

    T & operator[] (int n) {
        if (0 == n) return x;
        else if (1 == n) return y;
        else
            return z;
    };


    //---------------[ vector aritmetic operator ]--------------
    Vector3 < T > operator + (Vector3 < T > v) {
        return Vector3 < T > (x + v.x, y + v.y, z + v.z);
    };

    Vector3 < T > operator - (Vector3 < T > v) {
        return Vector3 < T > (x - v.x, y - v.y, z - v.z);
    };

    Vector3 < T > operator * (Vector3 < T > v) {
        return Vector3 < T > (x * v.x, y * v.y, z * v.z);
    };

    Vector3 < T > operator / (Vector3 < T > v) {
        return Vector3 < T > (x / v.x, y / v.y, z / v.z);
    };

    Vector3 < T > & operator += (Vector3 < T > v) {
        x += v.x;
        y += v.y;
        z += v.z;
        return * this;
    };

    Vector3 < T > & operator -= (Vector3 < T > v) {
        x -= v.x;
        y -= v.y;
        z -= v.z;
        return * this;
    };

    Vector3 < T > & operator *= (Vector3 < T > v) {
        x *= v.x;
        y *= v.y;
        z *= v.z;
        return * this;
    };

    Vector3 < T > & operator /= (Vector3 < T > v) {
        x /= v.x;
        y /= v.y;
        z /= v.z;
        return * this;
    };

    float dotProduct(Vector3 < T > v) {
        return x * v.x + y * v.y + z * v.z;
    };

    Vector3 < T > crossProduct(Vector3 < T > v) {
        return Vector3 < T > (y * v.z - v.y * z, z * v.x - v.z * x, x * v.y - v.x * y);
    };


    //--------------[ scalar vector operator ]--------------------
    Vector3 < T > operator + (T n) {
        return Vector3 < T > (x + n, y + n, z + n);
    };

    Vector3 < T > operator - (T n) {
        return Vector3 < T > (x - n, y - n, z - n);
    };

    Vector3 < T > operator * (T n) {
        return Vector3 < T > (x * n, y * n, z * n);
    };

    Vector3 < T > operator / (T n) {
        return Vector3 < T > (x / n, y / n, z / n);
    };

    Vector3 < T > & operator += (T n) {
        x += n;
        y += n;
        z += n;
        return * this;
    };

    Vector3 < T > & operator -= (T n) {
        x -= n;
        y -= n;
        z -= n;
        return * this;
    };

    Vector3 < T > & operator *= (T n) {
        x *= n;
        y *= n;
        z *= n;
        return * this;
    };

    Vector3 < T > & operator /= (T n) {
        x /= n;
        y /= n;
        z /= n;
        return * this;
    };

    //-------------[ size operations ]---------------------------
    T size() {
        return std::sqrt(x * x + y * y + z * z);
    };

	T sizeSq(){
		return x * x + y * y + z * z;
	}

    void normalize() {
        T s = size();
        x /= s;
        y /= s;
		z /= s;
    };

    //------------[ other operations ]---------------------------

    void rotate(T ax, T ay, T az) {
        T a = cos(DEG2RAD(ax));
        T b = sin(DEG2RAD(ax));
        T c = cos(DEG2RAD(ay));
        T d = sin(DEG2RAD(ay));
        T e = cos(DEG2RAD(az));
        T f = sin(DEG2RAD(az));
        T nx = c * e * x - c * f * y + d * z;
        T ny = (a * f + b * d * e) * x + (a * e - b * d * f) * y - b * c * z;
        T nz = (b * f - a * d * e) * x + (a * d * f + b * e) * y + a * c * z;
        x = nx; y = ny; z = nz;


    };

    //-----------[ GL operations ]-------------------------------
    /*void glVertex() {
        glVertex3f(x, y, z);
    };

    void glNormal() {
        glNormal3f(x, y, z);
    };*/

    //-------------[ conversion ]-----------------------------
    operator T*(){ return (T*) this; }
    operator const T*(){ return (const T*) this; }

};



typedef Vector3 < float > Vector3f;
typedef Vector3 < double > Vector3d;

template < class T >
class Vector4{
public:
	union { T r; T x; };
	union { T g; T y; };
	union { T b; T z; };
	union { T a; T w; };

    //----------------[ constructors ]--------------------------
	Vector4() {
		x = y = z = w = 0;
	};

	Vector4(T x, T y, T z, T w) {
		this->x = x; this->y = y; this->z = z; this->w = w;
	};


    //----------------[ access operators ]-------------------
	Vector4 < T > operator = (Vector4 < T > v) {
		x = v.x;
		y = v.y;
		z = v.z;
		w = v.w;
		return * this;
	};

	T & operator[] (int n) {
		if (0 == n) return x;
		else if (1 == n) return y;
		else if (2 == n) return z;
		else return w;
	};


    //---------------[ vector aritmetic operator ]--------------
	Vector4 < T > operator + (Vector4 < T > v) {
		return Vector4 < T > (x + v.x, y + v.y, z + v.z, w + v.w);
	};

	Vector4 < T > operator - (Vector4 < T > v) {
		return Vector4 < T > (x - v.x, y - v.y, z - v.z, w - v.w);
	};

	Vector4 < T > operator * (Vector4 < T > v) {
		return Vector4 < T > (x * v.x, y * v.y, z * v.z, w * v.w);
	};

	Vector4 < T > operator / (Vector4 < T > v) {
		return Vector4 < T > (x / v.x, y / v.y, z / v.z, w / v.w);
	};

	Vector4 < T > & operator += (Vector4 < T > v) {
		x += v.x;
		y += v.y;
		z += v.z;
		w += v.w;
		return * this;
	};

	Vector4 < T > & operator -= (Vector4 < T > v) {
		x -= v.x;
		y -= v.y;
		z -= v.z;
		w -= v.w;
		return * this;
	};

	Vector4 < T > & operator *= (Vector4 < T > v) {
		x *= v.x;
		y *= v.y;
		z *= v.z;
		w *= v.w;
		return * this;
	};

	Vector4 < T > & operator /= (Vector4 < T > v) {
		x /= v.x;
		y /= v.y;
		z /= v.z;
		w /= v.w;
		return * this;
	};



    //--------------[ scalar vector operator ]--------------------
	Vector4 < T > operator + (T n) {
		return Vector4 < T > (x + n, y + n, z + n, w + n);
	};

	Vector4 < T > operator - (T n) {
		return Vector4 < T > (x - n, y - n, z - n, w - n);
	};

	Vector4 < T > operator * (T n) {
		return Vector4 < T > (x * n, y * n, z * n, w * n);
	};

	Vector4 < T > operator / (T n) {
		return Vector4 < T > (x / n, y / n, z / n, w / n);
	};

	Vector4 < T > & operator += (T n) {
		x += n;
		y += n;
		z += n;
		w += n;
		return * this;
	};

	Vector4 < T > & operator -= (T n) {
		x -= n;
		y -= n;
		z -= n;
		w -= n;
		return * this;
	};

	Vector4 < T > & operator *= (T n) {
		x *= n;
		y *= n;
		z *= n;
		w *= n;
		return * this;
	};

	Vector4 < T > & operator /= (T n) {
		x /= n;
		y /= n;
		z /= n;
		w /= n;
		return * this;
	};

    //-------------[ size operations ]---------------------------
	T size() {
		return std::sqrt(x * x + y * y + z * z + w * w);
	};

	void normalize() {
		T s = size();
		x /= s;
		y /= s;
		z /= s;
		w /= s;
	};

	T sizeSq(){
		return x * x + y * y + z * z + w * w;
	}


    //-------------[ conversion ]-----------------------------
    operator T*(){ return (T*) this; }
    operator const T*(){ return (const T*) this; }



};

typedef Vector4<float> Vector4f;
typedef Vector4<double> Vector4d;


template < class T >
class Matrix4 {
public:
    T data[16];

    //--------------------------[ constructors ]-------------------------------
    Matrix4() {
        for (int i = 0; i < 16; i++)
            data[i] = (i % 5) ? 0 : 1;
    };

    Matrix4(T * dt) {
        for (int i = 0; i < 16; i++) data[i] = dt[i];
    };

	/// Creates rotation matrix
	Matrix4(T a, T b, T c)
	{
		Matrix4<T> ma, mb, mc;
		float ac = cos(a);
		float as = sin(a);
		float bc = cos(b);
		float bs = sin(b);
		float cc = cos(c);
		float cs = sin(c);

		ma.at(1,1) = ac;
		ma.at(2,1) = -as;
		ma.at(1,2) = as;
		ma.at(2,2) = ac;

		mb.at(0,0) = bc;
		mb.at(3,0) = bs;
		mb.at(0,3) = -bs;
		mb.at(3,3) = bc;

		mc.at(0,0) = cc;
		mc.at(1,0) = -cs;
		mc.at(0,1) = cs;
		mc.at(1,1) = cc;

		(*this) = ma * mb * mc;
	};

	/// Creates translation matrix
	Matrix4(T x, T y, T z, T w)
	{
		this->at(3,0) = x;
		this->at(3,1) = x;
		this->at(3,2) = z;
		this->at(3,3) = w;
	};

	/// Creates inversion matrix
	Matrix4(T a, T b, T c,
			T x, T y, T z, T w)
	{
		Matrix4<T> m1(-a,-b,-c),m2(-x,-y,-z);
		(*this) = m1 * m2;
	}

    //---------------------[ access operators ]---------------------------------
    T & at(int x, int y) {
        return data[y * 4 + x];
    };

    Matrix4 < T > & operator = (Matrix4 < T > m) {
        for (int i = 0; i < 16; i++) data[i] = m.data[i];
        return * this;
    };

    Matrix4 < T > & operator = (T * m) {
        for (int i = 0; i < 16; i++) data[i] = m[i];
        return * this;
    };

    Matrix4 < T > & operator = (double * m) {
        for (int i = 0; i < 16; i++) data[i] = (T)m[i];
        return * this;
    };

    //--------------------[ multiply operators ]--------------------------------
    Vector3 < T > operator * (Vector3 < T > v) {
        return Vector3 < T > (data[0] * v.x + data[1] * v.y + data[2] * v.z + data[3],
             data[4] * v.x + data[5] * v.y + data[6] * v.z + data[7],
             data[8] * v.x + data[9] * v.y + data[10] * v.z + data[11]);

    };

    Matrix4 < T > & operator * (Matrix4 < T > m) {
        static Matrix4 < T > w;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                float n = 0;
                for (int k = 0; k < 4; k++) n += m.at(i, k) * at(k, j);
                w.at(i, j) = n;
            }
        }
        return w;

    };

    //---------------------[ GL operations ]------------------------------------
    /*void glMultMatrix() {
        glMultMatrixf(data);
    };*/
    
    //-------------[ conversion ]-----------------------------
    operator T*(){ return (T*) data; }
    operator const T*(){ return (const T*) data; }
    
};



typedef Matrix4 < float > Matrix4f;

#ifdef VMATH_NAMESPACE
} // end of namespace
#endif	

#endif /*VMATH_H_*/
