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

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

#include "Core/Asura.h"

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

#include "FileAccess/Asura_PS3_FileSystem.h"

#include <stdio.h>
#include <sys/process.h>
#include <sys/spu_initialize.h>
#include <sys/time_util.h>


static const u_int uNUM_TESTS = 100000;
static const u_int uNUM_DATA= 4;
static const u_int uDATA_MASK = uNUM_DATA - 1;
Asura_Vector_3 g_axVector3s[uNUM_DATA];
Asura_AVector3 g_axVectors[uNUM_DATA];
Asura_AMatrix3x3 g_axMatrices[uNUM_DATA];
Asura_Matrix_3x3 g_axMatrix3s[uNUM_DATA];

Asura_Vector_3 g_axVector3Output[uNUM_DATA];
Asura_AVector3 g_axVector4Output[uNUM_DATA];
Asura_AMatrix3x3 g_axMatrix4Output[uNUM_DATA];
Asura_Matrix_3x3 g_axMatrix3Output[uNUM_DATA];

Asura_SoA_Vector_3 g_axSoaVectorData[uNUM_DATA];
Asura_SoA_Float g_axSoaDotOutput[uNUM_DATA];
Asura_Vector_3 g_axV3VectorData[4][uNUM_DATA];
float g_afV3DotOutput[4][uNUM_DATA];
Asura_AVector3 g_axV4VectorData[4][uNUM_DATA];
float g_afV4DotOutput[4][uNUM_DATA];
Asura_SoA_Vector_3 g_axSoaVectorOutput[uNUM_DATA];
Asura_AVector3 g_axVector4SoAOutput[4][uNUM_DATA];

void M0Marker();
void M3xM3Test();
void M3xV3Test();
void M4xM4Test();
void M4xV4Test();
void SoADotProductTest();
void V3DotProductTest();
void V4DotProductTest();
void V4ParallelMatrixMultTest();
void SOAParallelMatrixMultTest();

int main (int argc, char** argv)
{
	int	iRet;

	// first arg is num physical SPUs, second is num allowed to be RAW SPUs.
	// We need a RAW SPU for PSGL 
	iRet = sys_spu_initialize(6, 1);

	if (CELL_OK != iRet)
	{
		printf("error in sys_spu_initialize (0x%08x)\n", iRet);
		return iRet;
	}

	const char*	szIPAddress	= (argc > 1) ? argv [1] : NULL;
	const char*	szPortID	= (argc > 2) ? argv [2] : NULL;

	Asura_PS3_FileSystem::Initialise	(szIPAddress, szPortID);	

	Asura_Random::Initialise();
	Asura_Timers::Initialise();
	Asura_UnitTest_RunAllTests();
	Asura_SpeedTest_RunAllTests();

#if 0
  	// speed tests
  	float fStartTime[9];
  	float fEndTime[9];
	Asura_Vector_3 xTempV3;
  	for (u_int u = 0; u < uNUM_DATA; ++u)
  	{
		g_axVector3s[u].RandomDirection();
		g_axVectors[u] = Asura_AVector3(g_axVector3s[u]);
		g_axMatrices[u] = Asura_AMatrix3x3( Asura_AVector3(xTempV3.RandomDirection()),
			Asura_AVector3(xTempV3.RandomDirection()), Asura_AVector3(xTempV3.RandomDirection()) );
		g_axMatrix3s[u] = Asura_Matrix_3x3( g_axMatrices[u].GetElement(0,0), g_axMatrices[u].GetElement(0,1), g_axMatrices[u].GetElement(0,2), 
			g_axMatrices[u].GetElement(1,0), g_axMatrices[u].GetElement(1,1), g_axMatrices[u].GetElement(1,2), 
			g_axMatrices[u].GetElement(2,0), g_axMatrices[u].GetElement(2,1), g_axMatrices[u].GetElement(2,2) );
  	}

	// mark to help calc code size
	M0Marker();

	// matrix3 x matrix3 test
  	fStartTime[0] = Asura_Timers::GetTimeInFrame();
	M3xM3Test();
  	fEndTime[0] = Asura_Timers::GetTimeInFrame();

	// matrix3 x vector3 test
  	fStartTime[1] = Asura_Timers::GetTimeInFrame();
	M3xV3Test();
  	fEndTime[1] = Asura_Timers::GetTimeInFrame();

	// matrix4 x matrix4 test
  	fStartTime[2] = Asura_Timers::GetTimeInFrame();
	M4xM4Test();
  	fEndTime[2] = Asura_Timers::GetTimeInFrame();
	// matrix4 x vector4 test
  	fStartTime[3] = Asura_Timers::GetTimeInFrame();
	M4xV4Test();
  	fEndTime[3] = Asura_Timers::GetTimeInFrame();

	// SOA dot test
  	fStartTime[4] = Asura_Timers::GetTimeInFrame();
	SoADotProductTest();
  	fEndTime[4] = Asura_Timers::GetTimeInFrame();

	// V3 dot test
	fStartTime[5] = Asura_Timers::GetTimeInFrame();
	V3DotProductTest();
	fEndTime[5] = Asura_Timers::GetTimeInFrame();
	
	// V4 dot test
	fStartTime[6] = Asura_Timers::GetTimeInFrame();
	V4DotProductTest();
	fEndTime[6] = Asura_Timers::GetTimeInFrame();

	// V4 parallel matrix multiply
	fStartTime[7] = Asura_Timers::GetTimeInFrame();
	V4ParallelMatrixMultTest();
	fEndTime[7] = Asura_Timers::GetTimeInFrame();

	// SOA parallel matrix multiply
	fStartTime[8] = Asura_Timers::GetTimeInFrame();
	SOAParallelMatrixMultTest();
	fEndTime[8] = Asura_Timers::GetTimeInFrame();

	Asura_OutputToDebugger_Retail(("matrix3 x matrix3 test took %.03f milliseconds\n", (fEndTime[0] - fStartTime[0]) * 1000.0f));
  	Asura_OutputToDebugger_Retail(("matrix3 x vector3 test took %.03f milliseconds\n", (fEndTime[1] - fStartTime[1]) * 1000.0f));
  	Asura_OutputToDebugger_Retail(("matrix4 x matrix4 test took %.03f milliseconds\n", (fEndTime[2] - fStartTime[2]) * 1000.0f));
  	Asura_OutputToDebugger_Retail(("matrix4 x vector4 test took %.03f milliseconds\n", (fEndTime[3] - fStartTime[3]) * 1000.0f));
	Asura_OutputToDebugger_Retail(("SOA dot product   test took %.03f milliseconds\n", (fEndTime[4] - fStartTime[4]) * 1000.0f));
  	Asura_OutputToDebugger_Retail(("vector3 dot prod  test took %.03f milliseconds\n", (fEndTime[5] - fStartTime[5]) * 1000.0f));
  	Asura_OutputToDebugger_Retail(("vector4 dot prod  test took %.03f milliseconds\n", (fEndTime[6] - fStartTime[6]) * 1000.0f));
  	Asura_OutputToDebugger_Retail(("v4 parallel       test took %.03f milliseconds\n", (fEndTime[7] - fStartTime[7]) * 1000.0f));
  	Asura_OutputToDebugger_Retail(("soa parallel      test took %.03f milliseconds\n", (fEndTime[8] - fStartTime[8]) * 1000.0f));
#endif
	ASURA_PS3_DEBUGBREAK();
	return 0;
}

void M0Marker()
{}

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

void M3xV3Test()
{
	for (u_int u = 0; u < uNUM_TESTS; ++u)
  	{
  		g_axVector3Output[u & uDATA_MASK] = g_axVector3s[u & uDATA_MASK] * g_axMatrix3s[u & uDATA_MASK];
  	}
}

void M4xM4Test()
{
  	for (u_int u = 0; u < uNUM_TESTS - 1; ++u)
  	{
  		g_axMatrix4Output[u & uDATA_MASK] = g_axMatrices[u & uDATA_MASK] * g_axMatrices[(u + 1) & uDATA_MASK];
  	}
}

void M4xV4Test()
{
  	for (u_int u = 0; u < uNUM_TESTS; ++u)
  	{
  		g_axVector4Output[u & uDATA_MASK] = g_axVectors[u & uDATA_MASK] * g_axMatrices[u & uDATA_MASK];
  	}
}

void SoADotProductTest()
{
  	for (u_int u = 0; u < uNUM_TESTS; u += 4)
  	{
		Asura_SoA_Float xResult0 = g_axSoaVectorData[u & uDATA_MASK] * g_axSoaVectorData[u & uDATA_MASK];
		Asura_SoA_Float xResult1 = g_axSoaVectorData[(u+1) & uDATA_MASK] * g_axSoaVectorData[(u+1) & uDATA_MASK];
		Asura_SoA_Float xResult2 = g_axSoaVectorData[(u+2) & uDATA_MASK] * g_axSoaVectorData[(u+2) & uDATA_MASK];
		Asura_SoA_Float xResult3 = g_axSoaVectorData[(u+3) & uDATA_MASK] * g_axSoaVectorData[(u+3) & uDATA_MASK];
		
		g_axSoaDotOutput[u & uDATA_MASK] = xResult0;
  		g_axSoaDotOutput[(u+1) & uDATA_MASK] = xResult1;
  		g_axSoaDotOutput[(u+2) & uDATA_MASK] = xResult2;
  		g_axSoaDotOutput[(u+3) & uDATA_MASK] = xResult3;
  	}
}

void V3DotProductTest()
{
  	for (u_int u = 0; u < uNUM_TESTS; u += 4)
  	{
		float f0 = g_axV3VectorData[0][u & uDATA_MASK] * g_axV3VectorData[0][u & uDATA_MASK];
		float f1 = g_axV3VectorData[1][u & uDATA_MASK] * g_axV3VectorData[1][u & uDATA_MASK];
		float f2 = g_axV3VectorData[2][u & uDATA_MASK] * g_axV3VectorData[2][u & uDATA_MASK];
		float f3 = g_axV3VectorData[3][u & uDATA_MASK] * g_axV3VectorData[3][u & uDATA_MASK];

		g_afV3DotOutput[0][u & uDATA_MASK] = f0;
		g_afV3DotOutput[1][u & uDATA_MASK] = f1;
		g_afV3DotOutput[2][u & uDATA_MASK] = f2;
		g_afV3DotOutput[3][u & uDATA_MASK] = f3;

		f0 = g_axV3VectorData[0][(u+1) & uDATA_MASK] * g_axV3VectorData[0][(u+1) & uDATA_MASK];
		f1 = g_axV3VectorData[1][(u+1) & uDATA_MASK] * g_axV3VectorData[1][(u+1) & uDATA_MASK];
		f2 = g_axV3VectorData[2][(u+1) & uDATA_MASK] * g_axV3VectorData[2][(u+1) & uDATA_MASK];
		f3 = g_axV3VectorData[3][(u+1) & uDATA_MASK] * g_axV3VectorData[3][(u+1) & uDATA_MASK];

		g_afV3DotOutput[0][(u+1) & uDATA_MASK] = f0;
		g_afV3DotOutput[1][(u+1) & uDATA_MASK] = f1;
		g_afV3DotOutput[2][(u+1) & uDATA_MASK] = f2;
		g_afV3DotOutput[3][(u+1) & uDATA_MASK] = f3;

		f0 = g_axV3VectorData[0][(u+2) & uDATA_MASK] * g_axV3VectorData[0][(u+2) & uDATA_MASK];
		f1 = g_axV3VectorData[1][(u+2) & uDATA_MASK] * g_axV3VectorData[1][(u+2) & uDATA_MASK];
		f2 = g_axV3VectorData[2][(u+2) & uDATA_MASK] * g_axV3VectorData[2][(u+2) & uDATA_MASK];
		f3 = g_axV3VectorData[3][(u+2) & uDATA_MASK] * g_axV3VectorData[3][(u+2) & uDATA_MASK];

		g_afV3DotOutput[0][(u+2) & uDATA_MASK] = f0;
		g_afV3DotOutput[1][(u+2) & uDATA_MASK] = f1;
		g_afV3DotOutput[2][(u+2) & uDATA_MASK] = f2;
		g_afV3DotOutput[3][(u+2) & uDATA_MASK] = f3;

		f0 = g_axV3VectorData[0][(u+3) & uDATA_MASK] * g_axV3VectorData[0][(u+3) & uDATA_MASK];
		f1 = g_axV3VectorData[1][(u+3) & uDATA_MASK] * g_axV3VectorData[1][(u+3) & uDATA_MASK];
		f2 = g_axV3VectorData[2][(u+3) & uDATA_MASK] * g_axV3VectorData[2][(u+3) & uDATA_MASK];
		f3 = g_axV3VectorData[3][(u+3) & uDATA_MASK] * g_axV3VectorData[3][(u+3) & uDATA_MASK];

		g_afV3DotOutput[0][(u+3) & uDATA_MASK] = f0;
		g_afV3DotOutput[1][(u+3) & uDATA_MASK] = f1;
		g_afV3DotOutput[2][(u+3) & uDATA_MASK] = f2;
		g_afV3DotOutput[3][(u+3) & uDATA_MASK] = f3;	}
}

void V4DotProductTest()
{
  	for (u_int u = 0; u < uNUM_TESTS; ++u)
  	{
		float f0 = g_axV4VectorData[0][u & uDATA_MASK] * g_axV4VectorData[0][u & uDATA_MASK];
		float f1 = g_axV4VectorData[1][u & uDATA_MASK] * g_axV4VectorData[1][u & uDATA_MASK];
		float f2 = g_axV4VectorData[2][u & uDATA_MASK] * g_axV4VectorData[2][u & uDATA_MASK];
		float f3 = g_axV4VectorData[3][u & uDATA_MASK] * g_axV4VectorData[3][u & uDATA_MASK];

		g_afV4DotOutput[0][u & uDATA_MASK] = f0;
		g_afV4DotOutput[1][u & uDATA_MASK] = f1;
		g_afV4DotOutput[2][u & uDATA_MASK] = f2;
		g_afV4DotOutput[3][u & uDATA_MASK] = f3;
  	}
}

void V4ParallelMatrixMultTest()
{
  	for (u_int u = 0; u < uNUM_TESTS; ++u)
  	{
		Asura_AVector3 xVec0 = g_axV4VectorData[0][u & uDATA_MASK] * g_axMatrices[u & uDATA_MASK];
		Asura_AVector3 xVec1 = g_axV4VectorData[1][u & uDATA_MASK] * g_axMatrices[u & uDATA_MASK];
		Asura_AVector3 xVec2 = g_axV4VectorData[2][u & uDATA_MASK] * g_axMatrices[u & uDATA_MASK];
		Asura_AVector3 xVec3 = g_axV4VectorData[3][u & uDATA_MASK] * g_axMatrices[u & uDATA_MASK];

		g_axVector4SoAOutput[0][u & uDATA_MASK] = xVec0;
		g_axVector4SoAOutput[1][u & uDATA_MASK] = xVec1;
		g_axVector4SoAOutput[2][u & uDATA_MASK] = xVec2;
		g_axVector4SoAOutput[3][u & uDATA_MASK] = xVec3;
  	}
}

void SOAParallelMatrixMultTest()
{
  	for (u_int u = 0; u < uNUM_TESTS; ++u)
  	{
		g_axSoaVectorOutput[u & uDATA_MASK] = g_axSoaVectorData[u & uDATA_MASK] * g_axMatrices[u & uDATA_MASK];
  	}
}

// E N D   O F   F I L E /////////////////////////////////////////////////////
