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

		Vector4.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
		==========	==========	======================================================
		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		IsEqual takes tolerance value instead of vector
		7/21/97		Ian			Moved asignment operator after an inline function it calls.
		7/11/97		Ian			Added assignment operators to convert from SVector4 to
								TVector4 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_VECTOR4
#define H_VECTOR4

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCBasicTypes.h"
#include "Real.h"
#include "Vector3.h"
#include "Vector4Fwd.h"


/// \addtogroup Math
/// \{

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

	static const TVector4Template<T> kUnitX;		///<	( 1.0, 0.0, 0.0, 0.0 );
	static const TVector4Template<T> kUnitY;		///<	( 0.0, 1.0, 0.0, 0.0 );	
	static const TVector4Template<T> kUnitZ;		///<	( 0.0, 0.0, 1.0, 0.0 );
	static const TVector4Template<T> kUnitW;		///<	( 0.0, 0.0, 0.0, 1.0 );

	static const TVector4Template<T> kNegativeX;	///<	( -1.0, 0.0, 0.0, 0.0 );	
	static const TVector4Template<T> kNegativeY;	///<	( 0.0, -1.0, 0.0, 0.0 );	
	static const TVector4Template<T> kNegativeZ;	///<	( 0.0, 0.0, -1.0, 0.0 );
	static const TVector4Template<T> kNegativeW;	///<	( 0.0, 0.0, 0.0, -1.0 );
	 	
public:
		 TVector4Template			(void);
		 TVector4Template			(const T& xx, const T& yy, const T& zz, const T& ww);

	template<class U>
	TVector4Template				(const TVector4Template<U>& v)
	: x(v.x), y(v.y), z(v.z), w(v.w) {}
						
	void SetValues					(const T& xx, const T& yy, const T& zz, const T& ww);
			
	inline uint32 GetDimension		(void) const;
	
	// simple conversion
	void SetFromXYZ					(const TVector3Template<T>& v,const T& w);
	
	inline TVector3Template<T>&			CastToXYZ		(void);
	inline const TVector3Template<T>&	CastToXYZ(void) const;
	inline TVector2Template<T>&			CastToXY		(void);
	inline const TVector2Template<T>&	CastToXY(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 T&			Z	(void);
	inline T&			W	(void);
	
	inline const T&		X	(void) const;
	inline const T&		Y	(void) const;
	inline const T&		Z	(void) const;
	inline const T&		W	(void) const;
	
	// vector properties
	inline T	GetMagnitude		(void) const; 
	inline T	GetMagnitudeSquared	(void) const;
	
	inline T	GetDistance			(const TVector4Template<T>& rhs) const;
			
	boolean		SetMagnitude		(const T& newMagnitude);
	void		SetToCenter			(const TVector4Template<T>& v0, const TVector4Template<T>& v1);

	inline boolean Normalize		(const T& zeroTolerance = 0.0f);
	inline boolean NormalizeOrReplaceWith(const TVector4Template<T>& replacement,const T& zeroTolerance = 0.0f);
	
	boolean		IsEqual				(const TVector4Template<T>& compareVec, const T& zeroTolerance = 0.0f) const;

	void		ClampValue			(const TVector4Template<T>& min, const TVector4Template<T>& max);
	
	inline void Negate				(void);
	
	void		Interpolate			(const TVector4Template<T>& vstart, const TVector4Template<T>& vend, const T& t);
	
	void		Swap				(TVector4Template<T>& v);
					
	// fast vector operation
	inline TVector4Template<T>& operator/=		(const T& scalar);
	inline TVector4Template<T>& operator*=		(const T& scalar);
	inline TVector4Template<T>& operator+=		(const TVector4Template<T>& v);
	inline TVector4Template<T>& operator-=		(const TVector4Template<T>& v);	

	inline TVector4Template<T> operator/	(const T& s) const
	{ return TVector4Template<T>(*this) /= s; }

	inline TVector4Template<T> operator*	(const T& s) const
	{ return TVector4Template<T>(*this) *= s; }

	inline TVector4Template<T> operator+	(const TVector4Template<T>& b) const
	{ return TVector4Template<T>(*this) += b; }

	inline TVector4Template<T> operator-	(const TVector4Template<T>& b) const
	{ return TVector4Template<T>(*this) -= b; }

	inline TVector4Template<T> operator-	() const
	{ return TVector4Template<T>(-x, -y, -z, -w); }

	inline boolean operator==	(const TVector4& rhs) const
	{ return ((x==rhs.x)&&(y==rhs.y)&&(z==rhs.z)&&(w==rhs.w)); }

	inline boolean operator!=	(const TVector4& rhs) const
	{ return ((x!=rhs.x)||(y!=rhs.y)||(z!=rhs.z)||(w!=rhs.w)); }

	// dot product and term by term product (a.x * b.x, a.y * b.y, ...)
	inline T operator*		(const TVector4& b) const
	{ return (x * b.x + y * b.y + z * b.z + w * b.w); }

	TVector4Template<T> operator%	(const TVector4Template<T>& b) const;
	
public:	
	/// This sets the passed in vector to the normalized version of this, 
	/// and returns the magnitude for the vector.
	T			Normalize			(TVector4Template<T>& result) const;
				
public: 
	T x, y, z, w;
};

/// \}

// [operator definitions] --------------------------------------------------------------//

// standard vector operators
template<class T>
inline TVector4Template<T> operator*	(const int s, const TVector4Template<T>& v) 
	{ return TVector4Template<T>(v) *= (T)s; }

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

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


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

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

template<class T>
inline TVector4Template<T>::TVector4Template(const T& xx, const T& yy, const T& zz, const T& ww)
	: x( xx ), y( yy ), z(zz), w(ww)
	{ }

template<class T>
inline void TVector4Template<T>::SetValues(const T& xx, const T& yy, const T& zz, const T& ww )
	{	x = xx; y = yy; z = zz; w = ww;	}

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

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

template<class T>
inline const T& TVector4Template<T>::Z	(void) const
	{ return z; }

template<class T>
inline const T& TVector4Template<T>::W	(void) const
	{ return w; }


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

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

template<class T>
inline T& TVector4Template<T>::Z (void)
	{ return z; }

template<class T>
inline T& TVector4Template<T>::W (void)
	{ return w; }	


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

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


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


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

template<class T>
inline T TVector4Template<T>::GetMagnitude			(void) const
	{ return sqrt( GetMagnitudeSquared() ); }

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


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

template<class T>
inline TVector4Template<T>& TVector4Template<T>::operator*=		(const T& s)
	{ x *= s; y *= s; z *= s; w *= s; return *this; }

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

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

	
template<class T>
inline void TVector4Template<T>::Negate		(void)
	{ x = -x; y = -y; z = -z; w = -w; }


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

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


template<class T>
inline TVector3Template<T>& TVector4Template<T>::CastToXYZ		(void)
	{ return *(TVector3Template<T>*)this; }
	
template<class T>
inline const TVector3Template<T>& TVector4Template<T>::CastToXYZ	(void) const
	{ return *(const TVector3Template<T>*)this; }

template<class T>
inline TVector2Template<T>& TVector4Template<T>::CastToXY		(void)
	{ return *(TVector2Template<T>*)this; }
	
template<class T>
inline const TVector2Template<T>& TVector4Template<T>::CastToXY	(void) const
	{ return *(const TVector2Template<T>*)this; }


#endif // H_VECTOR4