/****************************************************************************************

		Vector2.h

  		Copyright: (c) 2001 Eovia Corp. All rights reserved.

		Vector Math Library, Release 2.0,  1/1/96 
		Copyright 1996 Alec Miller, All Rights Reserved 

		Revision History

		when		who			what & why
		==========	==========	======================================================
		01/28/99	Nba			Added Argument call, that returns the angle that the vector describes with the x axis
		5/21/98		Greg		Fixed double to float warning (again?)
		5/18/98		Joshua		Fixed double to float warning.
		1/19/98		link		Removed if-def around pragma once.
		1/2/98		Kevin		merge:
		10/9/97		Alec		added GetDistance()
		8/22/97		Alec		changed IsEqual to take a float value instead
		7/11/97		Ian			Added assignment operators to convert from SVector2 to
								TVector2 and back.
		7/2/97		Ian			Fred Merge
		6/25/97		link		New framework draw context & rectangle classes.
		5/1/97		link		Commented out stream code.
		4/1/97		kevin		inlined lots of functions
		5/15/96		Alec		new today - added vector math library to InfiniD project

*****************************************************************************************/

#ifndef H_VECTOR2
#define H_VECTOR2

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCBasicTypes.h"
#include "Real.h"
#include "MCAssert.h"
#include "InlineOps.h"
#include "Vector2Fwd.h"


/// \addtogroup Math
///
/// \{

/// Defines a two dimensional vector.
///
template<class T>
class TVector2Template : public TMCObject0
{
	// this class cannot have virtual functions for C size compatability
	public:
		// constant definitions
		static const TVector2Template<T> kZero;
		//	( 0.0, 0.0 );
		static const TVector2Template<T> kOnes;
		//	( 1.0, 1.0 );

		static const TVector2Template<T> kUnitX;
		//	( 1.0, 0.0 );
		static const TVector2Template<T> kUnitY;
		//	( 0.0, 1.0 );	
		
		static const TVector2Template<T> kNegativeX;
		//	( -1.0, 0.0 );	
		static const TVector2Template<T> kNegativeY;
		//	( 0.0, -1.0 );
			
	public:
		inline			TVector2Template			(void);
		inline			TVector2Template			(const T& xx, const T& yy);
		
		inline void		SetValues					(const T& xx, const T& yy);
				
		inline uint32	GetDimension				(void) const;
				   
		// element access	 	  		 
		inline const T&		operator()				(const uint32 elemNum) const;
		inline T&			operator()				(const uint32 elemNum);
	   	
	   	inline const T&		operator[]				(const uint32 elemNum) const;
		inline T&			operator[]				(const uint32 elemNum);
	
		inline T&			X						(void);
		inline T&			Y						(void);
		
		inline const T&		X						(void) const;
		inline const T&		Y						(void) const;
		
		// vector properties
		inline T			GetMagnitude			(void) const; 
		inline T			GetMagnitudeSquared		(void) const;

		inline T			GetArgument				() const; // result is ]-pi; pi] (rounding gives ]-pi; pi[)
		
		inline T			GetDistance				(const TVector2Template<T>& rhs) const;
				
			   boolean		SetMagnitude			(const T& newMagnitude);
			   void			SetToCenter				(const TVector2Template<T>& v0, const TVector2Template<T>& v1);
		
		inline boolean		Normalize				(const T& zeroTolerance = 0.0f);
		inline boolean		NormalizeOrReplaceWith	(const TVector2Template<T>& replacement,const T& zeroTolerance = T(0.0));
		
			   boolean		IsEqual					(const TVector2Template<T>& compareVec, const T& zeroTolerance = T(0.0)) const;
				
			   void			ClampValue				(const TVector2Template<T>& min, const TVector2Template<T>& max);
				
		inline void			Negate					(void);
		
		inline void			Interpolate				(const TVector2Template<T>& vstart, const TVector2Template<T>& vend, const T& t);
		
			   void			Swap					(TVector2Template<T>& v);

	    inline T			Cross(const TVector2Template<T>& v) const;
	
		// fast vector operation
		inline TVector2Template<T>&	operator/=		(const T& scalar);
		inline TVector2Template<T>&	operator*=		(const T& scalar);
		inline TVector2Template<T>&	operator+=		(const TVector2Template<T>& v);
		inline TVector2Template<T>&	operator-=		(const TVector2Template<T>& v);

		inline TVector2Template<T>	operator*		(const T& s) const;
		inline T					operator*		(const TVector2Template<T>& b) const;
		inline TVector2Template<T>	operator/		(const T& s) const;
	
		template<class U>
		inline TVector2Template<T>&	operator=		(const TVector2Template<U>& v) { x=v.x; y=v.y; return *this; }

		inline TVector2Template<T>&	operator=		(const int32 i) { MCAssert(i==0); x=i; y=i; return *this; }

	public:
		// functionality brought over from RealVector
		
		// this sets the passed in vector to the normalized version of this, and returns
		// the magnitude for the vector
			   T			Normalize				(TVector2Template<T>& result) const;
		
		// deprecated - please use magnitude ftns instead to avoid confusion with Normalize
		inline T			GetNorm					() const
			{ return GetMagnitude(); }

		inline T			GetSquaredNorm			() const 
			{ return GetMagnitudeSquared(); }

	public:
		T x, y;
};
/// \}

// [operator definitions] --------------------------------------------------------------//
				
// standard vector operators

template<class T>
inline TVector2Template<T> TVector2Template<T>::operator/ (const T& s) const
	{ return TVector2Template<T>(*this) /= s; }
	
template<class T>
inline TVector2Template<T> operator*	(const int	s, const TVector2Template<T>& v) 
	{ return TVector2Template<T>(v) *= T(s); }

template<class T>
inline TVector2Template<T> operator*	(const int32	s, const TVector2Template<T>& v) 
	{ return TVector2Template<T>(v) *= T(s); }

template<class T>
inline TVector2Template<T> operator*	(const real		s, const TVector2Template<T>& v) 
	{ return TVector2Template<T>(v) *= T(s); }

template<class T>
inline TVector2Template<T> operator*	(const real64&	s, const TVector2Template<T>& v) 
	{ return TVector2Template<T>(v) *= T(s); }

template<class T>
inline TVector2Template<T> TVector2Template<T>::operator*	(const T& s) const
	{ return TVector2Template<T>(*this) *= s; }

template<class T>
inline TVector2Template<T> operator+	(const TVector2Template<T>& a, const TVector2Template<T>& b) 
	{ return TVector2Template<T>(a) += b; }

template<class T>
inline TVector2Template<T> operator-	(const TVector2Template<T>& a, const TVector2Template<T>& b) 
	{ return TVector2Template<T>(a) -= b; }

template<class T>
inline TVector2Template<T> operator-	(const TVector2Template<T>& v)
	{ return TVector2Template<T>(-v.x, -v.y); }

template<class T>
inline boolean  operator==	(const TVector2Template<T>& lhs, const TVector2Template<T>& rhs) 
	{ return ((lhs.x==rhs.x) && (lhs.y==rhs.y)); }

template<class T>
inline boolean  operator!=	(const TVector2Template<T>& lhs, const TVector2Template<T>& rhs)
	{ return ((lhs.x!=rhs.x) || (lhs.y!=rhs.y)); }
				
// dot product, cross product, and term by term product (a.x * b.x, a.y * b.y, ...)
template<class T>
inline T		TVector2Template<T>::operator*	(const TVector2Template<T>& b) const
	{ return x * b.x + y * b.y; }

template<class T>
inline T		operator^	(const TVector2Template<T>& a, const TVector2Template<T>& b)
	{ return a.x * b.y - a.y * b.x; }

template<class T>
inline TVector2Template<T> operator%	(const TVector2Template<T>& a, const TVector2Template<T>& b)
	{ return TVector2Template<T>(a.x * b.x, a.y * b.y); }




// [inline implementation] -------------------------------------------------------------//

template<class T>
inline TVector2Template<T>::TVector2Template		(void)
	{ }

template<class T>
inline TVector2Template<T>::TVector2Template		(const T& xx, const T& yy)
	: x( xx ), y( yy )
	{ }
				
template<class T>
inline void TVector2Template<T>::SetValues	(const T& xx, const T& yy)
	{ x = xx; y = yy; }


template<class T>
inline const T& TVector2Template<T>::X		(void) const
	{ return x; }

template<class T>
inline const T& TVector2Template<T>::Y		(void) const
	{ return y; }

template<class T>
inline T& TVector2Template<T>::X	(void)
	{ return x; }

template<class T>
inline T& TVector2Template<T>::Y	(void)
	{ return y; }


template<class T>
inline const T& TVector2Template<T>::operator()		(const uint32 elemNum) const
	{ return *(&x + elemNum); }

template<class T>
inline T&	   TVector2Template<T>::operator()		(const uint32 elemNum) 
	{ return *(&x + elemNum); }

template<class T>
inline const T& TVector2Template<T>::operator[]		(const uint32 elemNum) const
	{ MCAssert(elemNum<2); return *(&x + elemNum); } 

template<class T>
inline T&       TVector2Template<T>::operator[]		(const uint32 elemNum)
	{ MCAssert(elemNum<2); return *(&x + elemNum); } 

	
template<class T>
inline uint32	   TVector2Template<T>::GetDimension	(void) const
	{ return 2; }
	

template<class T>
inline T		 TVector2Template<T>::GetMagnitude			(void) const
	{ return (real)RealSqrt( GetMagnitudeSquared() ); }

template<class T>
inline T		 TVector2Template<T>::GetMagnitudeSquared	(void) const
	{ return x*x + y*y; }

template<class T>
inline T		 TVector2Template<T>::GetArgument			() const
	{ return (real)atan2(y, x); }

template<class T>
inline T		 TVector2Template<T>::GetDistance			(const TVector2Template<T>& rhs) const
	{ return (*this - rhs).GetMagnitude(); }



template<class T>
inline TVector2Template<T>& TVector2Template<T>::operator/=			(const T& scalar)
	{
		const T invScalar = T(1.0)/scalar;
		x *= invScalar; y *= invScalar;
		return *this;
	}

template<class T>
inline TVector2Template<T>& TVector2Template<T>::operator*=			(const T& scalar)
	{ x *= scalar; y *= scalar; return *this; }

template<class T>
inline TVector2Template<T>& TVector2Template<T>::operator+=			(const TVector2Template<T>& v)
	{ x += v.x; y += v.y; return *this; }

template<class T>
inline TVector2Template<T>& TVector2Template<T>::operator-=			(const TVector2Template<T>& v)
	{ x -= v.x; y -= v.y; return *this; }



template<class T>
inline void TVector2Template<T>::Negate		(void)
	{ x = -x; y = -y; }
	
template<class T>
inline void TVector2Template<T>::Interpolate	(const TVector2Template<T>& vstart, const TVector2Template<T>& vend, const T& t)
	{
		x = vstart.x + ( vend.x - vstart.x ) * t;
		y = vstart.y + ( vend.y - vstart.y ) * t;
	}

template<class T>
inline T TVector2Template<T>::Cross(const TVector2Template<T>& v) const
{
	return x*v.y - y*v.x;
}

template<class T>
inline boolean TVector2Template<T>::Normalize	(const T& zeroTolerance)
	{
		boolean normalizationSucceeded( false );
		real	magnitude( GetMagnitude() );
		
		if ( magnitude > zeroTolerance )
		{
			*this *= T(1.0) / magnitude;
			
			normalizationSucceeded = true;
		}
		
		return normalizationSucceeded;
	}

template<class T>
inline boolean TVector2Template<T>::NormalizeOrReplaceWith	(const TVector2Template<T>& replacement, const T& zeroTolerance)
	{
		boolean normalizationSucceeded( Normalize( zeroTolerance ) );
		
		if ( !normalizationSucceeded )
		{
			*this = replacement;
		}
		
		return normalizationSucceeded;
	}

#endif /* H_VECTOR2 */