#pragma once

#include "tuple2.h"


namespace lm
{

template<typename T>
class vector2 : public tuple2<T>
{
public:
	vector2(void)                       : tuple2<T>( T(0) , T(0) ) {}
	vector2( const T& _x, const T& _y ) : tuple2<T>( _x   , _y   ) {}
	vector2( const vector2<T>& _v )     : tuple2<T>( _v.x , _v.y ) {}
	vector2( const T* _v )              : tuple2<T>( _v ) {}


	template<typename U>
	operator vector2<U>(void) const
	{
		return vector2<U>( static_cast<U>(x) , static_cast<U>(y) );
	}


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

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

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

	void mult( const T& val );
	vector2<T>& operator*=( const T& _v );

	void div( const T& val );
	vector2<T>& operator/=( const T& _v );

	vector2<T> operator-(void) const;


	bool is_zero(void) const;

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

	// 正方向に90度回転する.
	void perpendicular(void);
	vector2<T> get_perpendicular(void) const;

	// 負方向に90度回転する.
	void reverse_perpendicular(void);
	vector2<T> get_reverse_perpendicular(void) const;

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


	void rotate( const T& angle );
};


typedef vector2<double> vector2d;
typedef vector2<float>  vector2f;
typedef vector2<double> vec2d;
typedef vector2<float>  vec2f;


// global method 

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


// vector implements

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

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


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

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

template<typename T> inline
void vector2<T>::add(const T& _x, const T& _y)
{
	x += _x ;  y += _y ;
}

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


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

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

template<typename T> inline
void vector2<T>::sub(const T& _x, const T& _y)
{
	x -= _x ;  y -= _y ;
}

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


template<typename T> inline
void vector2<T>::mult(const T& val)
{
	x *= val ;  y *= val ;
}

template<typename T> inline
vector2<T>& vector2<T>::operator*=(const T& _v)
{
	x *= _v ;  y *= _v ;
	return *this;
}


template<typename T> inline
void vector2<T>::div(const T& val)
{
	x /= val ;  y /= val ;
}

template<typename T> inline
vector2<T>& vector2<T>::operator/=(const T& _v)
{
	x /= _v ;  y /= _v ;
	return *this;
}


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


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

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

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

template<typename T> inline
void vector2<T>::perpendicular(void)
{
	T tx = x;
	T ty = y;
	set( -ty , tx );
}

template<typename T> inline
vector2<T> vector2<T>::get_perpendicular(void) const
{
	vector2<T> perp = (*this);
	perp.perpendicular();
	return perp;
}

template<typename T> inline
void vector2<T>::reverse_perpendicular(void)
{
	T tx = x;
	T ty = y;
	set( ty , -tx );
}

template<typename T> inline
vector2<T> vector2<T>::get_reverse_perpendicular(void) const
{
	vector2<T> rev_perp = (*this);
	rev_perp.reverse_perpendicular();
	return rev_perp;
}

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

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


template<typename T> inline
void vector2<T>::rotate(const T& angle)
{
	T c = cos(angle)  ,  s = sin(angle);
	T tx = x * c - y * s ;
	T ty = x * s + y * c ;
	x = tx ;   y = ty ;
}


// friend operations

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

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

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

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

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


}
