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

		MtxMath.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
		==========	==========	======================================================
		1/19/98		link		Removed if-def around pragma once.
		11/15/97	Alec		changed float to real, added VecAffineTfm for vec2's
		7/7/97		Alec		moved in PostRotateVectorByAffineRowTfm(),
									FormCrossProduct()
		2/21/97		Dennis		added FormMatrixFromToUp()
		6/6/96		Alec		added Vec3ToVec4 and Vec2ToVec3 conversions and
									another MultiplyVectorByAffineRowTfm for Vec3's
		5/15/96		Alec		new today - added vector math library to InfiniD project

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

#ifndef H_MTXMATH
#define H_MTXMATH

#if CP_PRAGMA_ONCE
#pragma once
#endif

#include "MCBasicTypes.h"
#include "Vector2Fwd.h"
#include "Vector3Fwd.h"
#include "Vector4Fwd.h"
#include "Matrix22Fwd.h"
#include "Matrix33Fwd.h"
#include "Matrix44Fwd.h"
#include "MatrixMN.h"


class TAngle;

enum EAxis
{
	kAxis_X = 'X',
	kAxis_Y = 'Y',
	kAxis_Z = 'Z'
};


/******************************************************************\
*                                                                  *
*      TMtxMath                                                    *
*                                                                  *
\******************************************************************/

						
class TMtxMath
{
	// When there's more ways to do a routine (such as inverse), these routines
	// fall into this area.  Convenience and conversion routines between math
	// types also go here to keep the headers independent.
	
	// we could break out TMtxMath row/col routines into TColMath and TRowMath
	// or matrix class with code optimized for each
	
	public:
		// fast direct concatenation transformations
		// naming the source system "body" and the destination "world" yields
		// row tfm	 	pre multiplication  - body  coordinate system motion
		//				post multiplication - world coordinate system motion
		// Col tfm 	pre multiplication  - world coordinate system motion
		//  			post multiplication - body  coordinate system motion
		static void PreMultiplyRotationColTfm		(TMatrix44&	m, const EAxis axis, const TAngle& angle);
		static void PostMultiplyRotationColTfm		(TMatrix44&	m, const EAxis axis, const TAngle& angle);

		static void PreMultiplyTranslationColTfm	(TMatrix44&	m, const real tx, const real ty, const real	tz);
		static void PostMultiplyTranslationColTfm	(TMatrix44&	m, const real tx, const real ty, const real	tz);
		
		static void PreMultiplyRotationRowTfm		(TMatrix44&	m, const EAxis axis, const TAngle& angle);
		static void PostMultiplyRotationRowTfm		(TMatrix44& m, const EAxis axis, const TAngle& angle);
				
		static void PreMultiplyTranslationRowTfm	(TMatrix44& m, const real tx, const real ty, const real	tz);
		static void PostMultiplyTranslationRowTfm	(TMatrix44& m, const real tx, const real ty, const real	tz);
						
		static void PreMultiplyScaleTfm				(TMatrix44&	m, const real sx, const real sy, const real	sz);
		static void PostMultiplyScaleTfm			(TMatrix44&	m, const real sx, const real sy, const real	sz);

		// form common affine transformations
		static void FormScaleTfm					(TMatrix44&	m, const real sx, const real sy, const real	sz);
		static void FormTranslationRowTfm			(TMatrix44&	m, const real tx, const real ty, const real	tz);
		static void FormRotationRowTfm				(TMatrix44&	m, const EAxis axis, const TAngle& angle);
		static void FormRotationRowTfm				(TMatrix44&	m, const TVector3& axis, const TAngle& angle);
				
		// CR - Cramer's Rule, GJE - Gauss-Jordan Elimination (with pivoting)
		static boolean SolveInverseGeneralCR		(const TMatrix22& m, TMatrix22& mInverse);
		static boolean SolveInverseGeneralCR		(const TMatrix33& m, TMatrix33& mInverse);
		static boolean SolveInverseGeneralGJE		(const TMatrix33& m, TMatrix33& mInverse);
		static boolean SolveInverseGeneralGJE		(const TMatrix44& m, TMatrix44& mInverse);

		template<class T>
		static boolean SolveInverseGeneralGJE		(const TMatrixMN<T>& m, TMatrixMN<T>& mInverse)
		{
			MCAssert(m.GetRowDimension() == m.GetColumnDimension());
			const int32 dim = m.GetColumnDimension();

			TMatrixMN<T>a(m), b(dim, dim);
			b.SetValuesToIdentity();

			uint32	 	i,
    					j,
    					i1;
			boolean 	inversionSucceeded = true;
    
			// As a evolves from original mat into identity - b evolves from identity into inverse(a)
					
			// Loop over cols of a from left to right, eliminating above and below diag
			for (j=0; (int32)j<dim; j++)
			{ 
    			// Find largest pivot in Col j among rows j..2
    			i1 = j;		    // Row with largest pivot candidate
    			for (i=j+1; (int32)i<dim; i++)
				{
					if ( fabs ( a(i,j) ) > fabs ( a(i1,j) ) )
					{
	    				i1 = i;
	    			}
				}
				
				// Swap rows i1 and j in a and b to put pivot on diagonal
				if (i1 != j)
				{
					a.SwapLines(i1, j);
					b.SwapLines(i1, j);
				}
			
				// Scale row j to have a unit diagonal
				if ( a(j,j) == 0.0 )
				{
	    			inversionSucceeded = false;
					break;
				}

				const T invPivot = 1.0 / a(j,j);
				for (i=0; (int32)i<dim; ++i)
				{
					b(j, i) *= invPivot;
					a(j, i) *= invPivot;
				}
			
				// Eliminate off-diagonal elems in col j of a, doing identical ops to b
				for ( i=0; (int32)i<dim; i++ )
				{
					if (i != j)
					{
						const T factor = -a(i,j);

						for (int32 k=0; k<dim; ++k)
						{
							b(i, k) += factor * b(j, k);
							a(i, k) += factor * a(j, k);
						}
					}
				}
			}
    
			if (inversionSucceeded)
			{
    			mInverse = b;
			}
    
			return inversionSucceeded;
		}

		// R - rotation, T - translation, U - uniform scale, S - non-uniform scale present
		static boolean SolveInverseSimpleRowTfmRT	(const TMatrix44& m, TMatrix44& mInverse); 
		static boolean SolveInverseSimpleRowTfmRTU	(const TMatrix44& m, TMatrix44& mInverse);
		static boolean SolveInverseSimpleRowTfmRTS	(const TMatrix44& m, TMatrix44& mInverse);

		// affine transform operations - row vector transforms have last Col 0,0,...,1
		static void MultiplyAffineRowTfms			(const TMatrix44& m1, const TMatrix44& m2, TMatrix44& result);
		static void MultiplyVectorByAffineRowTfm	(const TVector4& v, const TMatrix44& m, TVector4& result);
		static void MultiplyVectorByAffineRowTfm	(const TVector3& v,	const real wValue, const TMatrix44&	m, TVector3& result);
				
		static void MultiplyAffineRowTfms			(const TMatrix33& m1, const TMatrix33& m2, TMatrix33& result);
		static void MultiplyVectorByAffineRowTfm	(const TVector3& v, const TMatrix33& m, TVector3& result);
		static void MultiplyVectorByAffineRowTfm	(const TVector2& v, const real w, const TMatrix33& m, TVector2&	result);
		
		// useful for applying the inverse transpose to a normal vector
		static void PostRotateVectorByAffineRowTfm	(const TMatrix44& tfm, const TVector3& normal, TVector3& rotatedNormal);
				
		// euler angle XYZ row-vector transformation conversions
		static void ConvertRowTfmToEulerXYZ			(const TMatrix44& m, TAngle& angleX, TAngle& angleY, TAngle& angleZ);
		static void ConvertEulerXYZToRowTfm			(TMatrix44& m, const TAngle& angleX, const TAngle& angleY, const TAngle& angleZ);
		
		// pass in any three vectors and it will generate an orthonormal basis
		static void MakeOrthonormalBasisFromZAxis	(const TVector3& zAxis, TVector3& unitXAxis, TVector3& unitYAxis, TVector3& unitZAxis);
		static void MakeOrthonormalBasisFrom3Axes	(TVector3& xVec, TVector3& yVec, TVector3& zVec);
		
		// creating orthonormal rotation tfms - unit valued, no axis skew, 
		static void FormOrthonormalRowTfmAboutZAxis	(const TVector3& zAxis, TMatrix33& tfm);
		static void FormOrthonormalRowTfmAboutZAxis	(const TVector3& zAxis,	TMatrix44& tfm);
		
		// this needs to identify that it's a row tfm in the name
		static void FormMatrixFromToUp				(const TVector3& inFromPoint, const TVector3&inTargetPoint,
													 const TVector3& inUpVector, TMatrix44& outTfm, TMatrix44& outTfmInverse);
		
};

#endif // H_MTXMATH