//////////////////////////////////////////////////////////////////////////////
//
// main.cpp
//
// Copyright (c) 2005 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "main.h"

// Engine independent.
#include "Core/Asura_Maths.h"
#include "Core/Asura_Timers.h"
#include "Debugging/Asura_Logfile.h"
//#include "UnitTest/Asura_UnitTest.h"

// Engine platform-specific.
#include "Core/Asura_PS2_VU0.h"

// C O N S T A N T S /////////////////////////////////////////////////////////

static const u_int uNUM_TESTS = 100000;
static const u_int uNUM_DATA  = 16;
static const u_int uDATA_MASK = uNUM_DATA - 1;

// G L O B A L S /////////////////////////////////////////////////////////////

Asura_Vector_3 g_ax3VectorInput[uNUM_DATA];
Asura_Matrix_3x3 g_ax3MatrixInput[uNUM_DATA];
Asura_Quat g_axQuatInput[uNUM_DATA];

Asura_Vector_3 g_ax3VectorOutput[uNUM_DATA];
Asura_Matrix_3x3 g_ax3MatrixOutput[uNUM_DATA];

// P R O T O T Y P E S   A N D   I N L I N E   F U N C T I O N S /////////////

static void M3xM3Test( void );
static void M3xV3Test( void );
static void M3xV3Test_2( void );
static void RandomNonsenseBasedOnSomeDynamicsCode( void );
static void RecurseBones( void );

// F U N C T I O N   I M P L E M E N T A T I O N S ///////////////////////////

int main( int iNumArgs, const char** aszArgs )
{
    Asura_PS2_VU0::Initialise();
	Asura_Random::Initialise();
	Asura_Timers::Initialise();

    {
	    Asura_Vector_3 xTemp;

  	    for( u_int u = 0; u < uNUM_DATA; ++u )
  	    {
		    g_ax3VectorInput[u].RandomDirection();

            xTemp.RandomDirection();
            Asura_Maths::OrientationFromDirection( g_ax3MatrixInput[u], xTemp );	    

            g_axQuatInput[u].FromMatrix( g_ax3MatrixInput[u] );
  	    }
    }

#define RUN_TEST( fn, desc ) \
{ \
    float fStart = Asura_Timers::GetTimeInFrame(); \
    fn(); \
	Asura_OutputToDebugger_Retail(( "test \"" desc "\" took %.03f milliseconds\n", (Asura_Timers::GetTimeInFrame() - fStart) * 1000.0f )); \
} 

    for( u_int u = 0; u < 1; ++u )
    {
        Asura_OutputToDebugger_Retail(( "\nrun %u\n", u ));

        RUN_TEST( M3xM3Test,                             "matrix3 x matrix3"      );
        RUN_TEST( M3xV3Test,                             "matrix3 x vector3"      );
        RUN_TEST( M3xV3Test_2,                           "matrix3 x vector3 2"    );
        RUN_TEST( RandomNonsenseBasedOnSomeDynamicsCode, "random dynamicsy stuff" );
        RUN_TEST( RecurseBones,                          "recurse bones"          );
    }

#undef RUN_TEST

    Asura_Timers::Shutdown();
    Asura_Random::Shutdown();
    Asura_PS2_VU0::Shutdown();

	return EXIT_SUCCESS;
}

static void M3xM3Test( void )
{
	for( u_int u = 0; u < uNUM_TESTS - 1; ++u )
  	{
  		g_ax3MatrixOutput[u & uDATA_MASK] = g_ax3MatrixInput[u & uDATA_MASK] * g_ax3MatrixInput[(u + 1) & uDATA_MASK];
  	}
}

static void M3xV3Test( void )
{
	for( u_int u = 0; u < uNUM_TESTS; ++u )
  	{
  		g_ax3VectorOutput[u & uDATA_MASK] = g_ax3VectorInput[u & uDATA_MASK] * g_ax3MatrixInput[u & uDATA_MASK];
  	}
}

static void M3xV3Test_2( void )
{
    Asura_Vector_3 xTmpVec;
	for( u_int u = 0; u < uNUM_TESTS; ++u )
  	{
        u_int uIdx0 = u & uDATA_MASK;
        u_int uIdx1 = Asura_Random::GetIntU() & uDATA_MASK;
        u_int uIdx2 = Asura_Random::GetIntU() & uDATA_MASK;
        
        xTmpVec = g_ax3VectorInput[uIdx0] + g_ax3VectorInput[uIdx1] * Asura_Random::GetFloat();
  		g_ax3VectorOutput[uIdx0] = g_ax3VectorInput[uIdx0] * g_ax3MatrixInput[uIdx0] + g_ax3VectorInput[uIdx2];

        xTmpVec -= g_ax3VectorInput[uIdx2];
        g_ax3VectorOutput[uIdx1] = xTmpVec * g_ax3MatrixInput[uIdx1] + g_ax3VectorInput[uIdx0] * 4.0f;
    }
}

static void RandomNonsenseBasedOnSomeDynamicsCode( void )
{
    Asura_Vector_3 xResult;

    for( u_int u = 0; u < uNUM_TESTS; ++u )
    {
        xResult.RandomDirection();
        xResult *= Asura_Random::GetFloat( 0.0f, 100.0f );

        u_int uIdx0 = u & uDATA_MASK;
        u_int uIdx1 = Asura_Random::GetIntU() & uDATA_MASK;
        u_int uIdx2 = Asura_Random::GetIntU() & uDATA_MASK;

        // collision - elasticity 
        {
            float magOfPerpImp = (g_ax3VectorInput[uIdx0] * g_ax3VectorInput[uIdx1]) * (1.0f + g_ax3VectorInput[uIdx0].y);
            xResult -= g_ax3VectorInput[uIdx2] * magOfPerpImp;
        }

        // resolve player's movement vector against the collision plane
        {
            Asura_Vector_3 v = g_ax3VectorOutput[uIdx0];

            // KJL 17:37:52 28/02/00 - Exciting new system to make things more slidey
            if (g_ax3VectorInput[uIdx1].x!=0 || g_ax3VectorInput[uIdx1].y!=0 || g_ax3VectorInput[uIdx1].z!=0)
            {
                float perp = g_ax3VectorInput[uIdx2] * g_ax3VectorInput[uIdx1];
        
                if( v != g_ax3VectorInput[uIdx1] * perp )
                {
                    v -= g_ax3VectorInput[uIdx1] * perp;
                    v.Normalise();
                }
            }
    
            // awkward problem here to do with non-exact normals
            float magOfPerpVel = 1.01f*(v * xResult);
            xResult -= v * magOfPerpVel;
        }

        xResult = xResult * g_ax3MatrixInput[uIdx1];

        g_ax3VectorOutput[uIdx0] = xResult;
    }
}

static void RecurseBones( void )
{
    Asura_Matrix_3x3 xOriMat;

    for( u_int u = 0; u < uNUM_TESTS; ++u )
    {
        u_int uIdx0 = u & uDATA_MASK;

		const Asura_Quat&     xOriQuat = g_axQuatInput[uIdx0];
		const Asura_Vector_3& xPos     = g_ax3VectorInput[uIdx0];	

		xOriQuat.ToMatrix( xOriMat );

		if( uIdx0 )
		{
			const int iParentIndex = Asura_Random::GetIntU() & uDATA_MASK;
		
			g_ax3VectorOutput[uIdx0] = xPos * g_ax3MatrixInput[iParentIndex] + g_ax3VectorInput[iParentIndex];
			g_ax3MatrixOutput[uIdx0] = g_ax3MatrixInput[iParentIndex] * xOriMat;
		}
		else
		{
			g_ax3VectorOutput[uIdx0] = xPos;
			g_ax3MatrixOutput[uIdx0] = xOriMat;
		}
	}
}
