#pragma once

#include "tuple4.h"


namespace lm
{

template<typename T>
class vector4 : public tuple4<T>
{
public:
	vector4( void )                                               : tuple4<T>( T(0) , T(0) , T(0) , T(0) ) {}
	vector4( const T& _x, const T& _y, const T& _z, const T& _w ) : tuple4<T>( _x   , _y   , _z   , _w   ) {}
	vector4( const lm::vector4<T>& _v )                           : tuple4<T>( _v.x , _v.y , _v.z , _v.w ) {}
	vector4( const T* _v )                                        : tuple4<T>( _v ) {}


	template<typename U>
	operator vector4<U>(void) const
	{
		return vectur4<U>( static_cast<U>(x) , static_cast<U>(y) , static_cast<U>(z) , static_cast<U>(w) );
	}


	void set_zero(void);
	static vector4<T> get_zero(void);

	void add( const vector4<T>& _v );
	void add( const T* _v );
	void add( const T& _x, const T& _y, const T& _z, const T& _w );
	vector4<T>& operator+=( const vector4<T>& _v );

	void sub( const vector4<T>& _v );
	void sub( const T* _v );
	void sub( const T& _x, const T& _y, const T& _z, const T& _w );
	vector4<T>& operator-=( const vector4<T>& _v );

	vector4<T>& operator*=( const T& _v);
	vector4<T>& operator/=( const T& val );

	vector4<T> operator-(void) const;


	bool is_zero(void) const;

	void normalize(void);
	vector4<T> get_normalize(void) const;

	T length(void) const;
	T square_length(void) const;
};


typedef vector4<double> vector4d;
typedef vector4<float>  vector4f;
typedef vector4<double> vec4d;
typedef vector4<float>  vec4f;


// global method

template<typename T> inline
T dot(const vector4<T>& _a, const vector4<T>& _b)
{
	return _a.x * _b.x + _a.y * _b.y + _a.z * _b.z + _a.w * _b.w;
}


// implements

template<typename T> inline
void vector4<T>::set_zero(void)
{
	this->set( T(0) , T(0) , T(0) , T(0) );
}

template<typename T> inline
vector4<T> vector4<T>::get_zero(void)
{
	return vector4<T>( T(0) , T(0) , T(0) , T(0) );
}


template<typename T> inline
void vector4<T>::add(const vector4<T>& _v)
{
	x += _v.x ;  y += _v.y ;  z += _v.z ;  w += _v.w ;
}

template<typename T> inline
void vector4<T>::add(const T* _v)
{
	x += _v[0] ;  y += _v[1] ;  z += _v[2] ; w += _v[3] ;
}

template<typename T> inline
void vector4<T>::add(const T& _x, const T& _y, const T& _z, const T& _w)
{
	x += _x ;  y += _y ;  z += _z ;  w += _w ;
}

template<typename T> inline
vector4<T>& vector4<T>::operator+=(const vector4<T>& _v)
{
	x += _v.x ;  y += _v.y ;  z += _v.z ;  w += _v.w ;
	return *this;
}


template<typename T> inline
void vector4<T>::sub(const vector4<T>& _v)
{
	x -= _v.x ;  y -= _v.y ;  z -= _v.z ;  w -= _v.w ;
}

template<typename T> inline
void vector4<T>::sub(const T* _v)
{
	x -= _v[0] ;  y -= _v[1] ;  z -= _v[2] ;  w -= _v[3] ;
}

template<typename T> inline
void vector4<T>::sub(const T& _x, const T& _y, const T& _z, const T& _w)
{
	x -= _x ;  y -= _y ;  z -= _z ;  w -= _w ;
}

template<typename T> inline
vector4<T>& vector4<T>::operator-=(const vector4<T>& _v)
{
	x -= _v.x ;  y -= _v.y ;  z -= _v.z ;  w -= _v.w ;
	return *this;
}


template<typename T> inline
vector4<T>& vector4<T>::operator*=(const T& val)
{
	x *= val ;  y *= val ;  z *= val ;  w *= val ;
	return *this;
}


template<typename T> inline
vector4<T>& vector4<T>::operator/=(const T& val)
{
	x /= val ;  y /= val ;  z /= val ;  w /= val ;
	return *this;
}


template<typename T> inline
vector4<T> vector4<T>::operator-(void) const
{
	return vector4<T>( -x , -y , -z , -w );
}


template<typename T> inline
bool vector4<T>::is_zero(void) const
{
	T zero = static_cast<T>(0);
	if( x == zero && y == zero && z == zero && w == zero ) return true;
	else                                                   return false;
}

template<typename T> inline
vector4<T> vector4<T>::get_normalize(void) const
{
	vector4<T> tmp = *this;
	tmp.normalize();
	return tmp;
}


template<typename T> inline
void vector4<T>::normalize(void)
{
	if( is_zero() )return;
	(*this) /= length();
}

template<typename T> inline
T vector4<T>::length(void) const
{
	return sqrt( square_length() );
}

template<typename T> inline
T vector4<T>::square_length(void) const
{
	return x * x + y * y + z * z + w * w ;
}


// friend operations

template<typename T> inline
vector4<T> operator+(const vector4<T>& _a, const vector4<T>& _b)
{
	vector4<T> t = _a ;  t += _b ;  return t ;
}

template<typename T> inline
vector4<T> operator-(const vector4<T>& _a, const vector4<T>& _b)
{
	vector4<T> t = _a ;  t -= _b ;  return t ;
}

template<typename T> inline
vector4<T> operator*(const vector4<T>& _v, const T& _t)
{
	vector4<T> t = _v ;  t *= _t ;  return t ;
}

template<typename T> inline
vector4<T> operator*(const T& _t, const vector4<T>& _v)
{
	vector4<T> t = _v ;  t *= _t ;  return t ;
}

template<typename T> inline
vector4<T> operator/(const vector4<T>& _v, const T& _t)
{
	vector4<T> t = _v ;  t /= _t ;  return t ;
}


}
