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

		Vector3.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
		11/3/97		kevin		removed Vector3's operator =() - it caused wierd compiler
								problems on CW.
		7/11/97		Ian			Added assignment operators to convert from SVector3 to
								TVector3Template 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_VECTOR3
#define H_VECTOR3

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCBasicTypes.h"
#include "Real.h"
#include "InlineOps.h"
#include "Vector2.h"
#include "Vector3Fwd.h"


/// \addtogroup Math
/// \{

/// Defines a three dimensional vector.
///

template<class T>
class TVector3Template : public TMCObject0
{
	// this class cannot have virtual functions for C size compatability.  You may
	// derive off them, but understand that there is no virtual destructor. 
	public:
		// constant definitions
		static const TVector3Template<T> kZero;
		//	( 0.0, 0.0, 0.0 );
		static const TVector3Template<T> kOnes;
		//	( 1.0, 1.0, 1.0 );

		static const TVector3Template<T> kUnitX;
		//	( 1.0, 0.0, 0.0 );
		static const TVector3Template<T> kUnitY;
		//	( 0.0, 1.0, 0.0 );	
		static const TVector3Template<T> kUnitZ;
		//	( 0.0, 0.0, 1.0 );
		
		static const TVector3Template<T> kNegativeX;
		//	( -1.0, 0.0, 0.0 );	
		static const TVector3Template<T> kNegativeY;
		//	( 0.0, -1.0, 0.0 );	
		static const TVector3Template<T> kNegativeZ;
		//	( 0.0, 0.0, -1.0 );
	
	public:
		inline			   TVector3Template					(void);

		template<class U>
		inline			   TVector3Template					(const TVector3Template<U>& inVector) : x(inVector.x), y(inVector.y), z(inVector.z) {}

		inline			   TVector3Template					(const T& xx, const T& yy, const T& zz);
				
		// Bug fix: using reference for the parameters of SetValues was causing problems with the bulge modifier (Charles 2006)
		inline void		   SetValues				(const T xx, const T yy, const T zz);
				
		inline uint32	   GetDimension				(void) const;
		
		// simple conversion
			   void		   SetFromXY				(const TVector2Template<T>& v, const T& z);
		
		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 const T&		X						(void) const;
		inline const T&		Y						(void) const;
		inline const T&		Z						(void) const;
		
		// vector properties
		inline real64	   GetMagnitude				(void) const; 
		inline real64	   GetMagnitudeSquared		(void) const;
		
		inline real64	   GetDistance				(const TVector3Template<T>& rhs) const;
		
			   boolean	   SetMagnitude				(const T& newMagnitude);
			   void		   SetToCenter				(const TVector3Template<T>& v0, const TVector3Template<T>& v1);
		
		inline boolean	   Normalize				(const T& zeroTolerance = 0.0f);
		inline boolean	   NormalizeOrReplaceWith	(const TVector3Template<T>& replacement,const T& zeroTolerance = 0.0f);
		
		// returns true if the vector if already normalized
		inline boolean	   Normalized() const;
			   boolean	   IsEqual					(const TVector3Template<T>& compareVec, const T& zeroTolerance = 0.0f) const;
			   boolean	   IsEqualRelative			(const TVector3Template<T>& compareVec, const T& zeroTolerance = 0.0f) const;

			   void		   ClampValue				(const TVector3Template<T>& min, const TVector3Template<T>& max);
		
		inline void		   Negate					(void);
		
			   void		   Interpolate				(const TVector3Template<T>& vstart, const TVector3Template<T>& vend, const T& t);
		
			   void		   Swap						(TVector3Template<T>& v);

			   // returns I and J so that (I,J,this) is an orthonormal base
			   void		   BuildOrthonormalBase		(TVector3Template<T>& I,TVector3Template<T>& J) const;
				
		// fast vector operation
		inline TVector3Template<T>&   operator=				(const int32 i)		{ x=i;y=i;z=i; return *this; }

		template<class U>
		inline TVector3Template<T>&   operator=				(const TVector3Template<U>& v)
		{ x = (T)v.x; y = (T)v.y; z = (T)v.z; return *this; }

		inline TVector3Template<T>&   operator/=			(const T& scalar);
		inline TVector3Template<T>&   operator*=			(const T& scalar);
		inline TVector3Template<T>&   operator+=			(const TVector3Template<T>& v);
		inline TVector3Template<T>&   operator-=			(const TVector3Template<T>& v);

		inline real64				operator*  (const TVector3Template<T>& b) const;
		inline TVector3Template<T>	operator/  (const T& s) const;
		inline TVector3Template<T>	operator*  (const T& s) const;

	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				(TVector3Template<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(); }

		inline boolean		IsNull					(const T& eps= (T)1e-5) const
			{ return ((-eps < x) && (x < eps) && (-eps < y) && (y < eps) && (-eps < z) && (z < eps) ); }
			
	public:
		T x, y, z;
};
/// \}

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


// standard vector operators
template<class T>
inline TVector3Template<T> TVector3Template<T>::operator/ (const T& s) const
	{ return TVector3Template<T>(*this) /= s; }

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

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

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

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

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

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

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

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

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

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



// dot product, cross product, and term by term product (a.x * b.x, a.y * b.y, ...)
template<class T>
inline real64   TVector3Template<T>::operator*  (const TVector3Template<T>& b) const
	{ return (real64(x) * real64(b.x) + real64(y) * real64(b.y) + real64(z) * real64(b.z)); }

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

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




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

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

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

// Bug fix: using reference for the parameters of SetValues was causing problems with the bulge modifier (Charles 2006)
template<class T>	
inline void TVector3Template<T>::SetValues		 (const T xx, const T yy, const T zz)
	{ x = xx; y = yy; z = zz; }

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

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

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

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

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

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


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

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

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

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

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


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

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

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

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

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

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

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


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


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

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

template<class T>
inline boolean TVector3Template<T>::Normalized() const
{
	return (fabs( GetMagnitudeSquared() - 1.0 ) < 1e-3 );
}


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

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


#endif /* H_VECTOR3 */