//
// Tools_ObjectProcessor.cpp
//
// Created by BM - 02/04/2003 15:19:40
//
// Copyright (C) 2003 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "MeshProcessing/Tools_ObjectProcessor.h"

#include "Mesh/Asura_Mesh.h"
#include "Animation/Asura_Hierarchy.h"
#include "MeshProcessing/Asura_TriStripper.h"
#include "MeshProcessing/Tools_MeshRestartIndex.h"
#include "Core/Asura_Material_System.h"


Tools_ObjectProcessor_Strip*    Tools_ObjectProcessor::s_pxProcessedStrip;
u_int							Tools_ObjectProcessor::s_uTotalNumberOfVertices;
u_int							Tools_ObjectProcessor::s_uTotalNumberOfIndices;
u_int							Tools_ObjectProcessor::s_uTotalNumberOfTriangles;
bool							Tools_ObjectProcessor::s_bBeenInitialised = false;


bool Tools_ObjectProcessor::Initialise(Asura_Mesh* pxMesh,int iMaterialIndex,bool bStripify /*=true*/)
{
	return Tools_ObjectProcessor::Initialise(pxMesh, iMaterialIndex, 0, 0, bStripify);
}

bool Tools_ObjectProcessor::Initialise(Asura_Mesh* pxMesh,Asura_Hash_ID uMatResponse1,Asura_Hash_ID uMatResponse2,bool bStripify /*=true*/)
{
	return Tools_ObjectProcessor::Initialise(pxMesh, ASURA_MATERIAL_NONE, uMatResponse1, uMatResponse2, bStripify);
}

bool Tools_ObjectProcessor::Initialise(Asura_Mesh* pxMesh,int iMaterialIndex,Asura_Hash_ID uMatResponse1,Asura_Hash_ID uMatResponse2,bool bStripify)
{
	if(!pxMesh)
	{
		Asura_Assert(pxMesh,"no mesh to process");
		return false;
	}
	
	//reset totals
	s_uTotalNumberOfTriangles = 0;
	s_uTotalNumberOfVertices  = 0;
	s_uTotalNumberOfIndices   = 0;

	// KJL - 10/06/2003 10:49:20 - worst case scenario is that every poly is double sided (so tris * 3 verts * 2)
	u_int uTotalIndices = pxMesh->NumberOfTriangles*3*2;
	u_int uNumberOfUniqueVertices = 0;
	u_int uIndex = 0;

	s_pxProcessedStrip = new Tools_ObjectProcessor_Strip;

	Tools_ObjectProcessor_Vertex* pxWorkspaceVertices = new Tools_ObjectProcessor_Vertex[uTotalIndices];
	unsigned short* usIndexData = new unsigned short[uTotalIndices];

	u_int uNumberOfTriangles = 0;
	
	if(!pxMesh->AreNormalsValid())
	{
		pxMesh->CreateNormals();
	}	
	
	// fill up the workspace vertex buffer
	for (int iPoly=0; iPoly<pxMesh->NumberOfPolygons; iPoly++)
	{	
		int v=0;
		int iIndexNumber[4];
		
		int iPolyFlags = 0;

		if (pxMesh->PolygonFlagDataPtr)
		{
			iPolyFlags = pxMesh->PolygonFlagDataPtr[iPoly].Flags;
		}

		if (iPolyFlags & AsuraPolygonFlag_DontRender) continue;

		do
		{
			
			
			int vID =  pxMesh->PolygonDataPtr[iPoly].VertexID[v];
			Asura_Vector_3& xPosition = pxMesh->VertexDataPtr[vID].Position;
			Asura_Vector_3& xNormal = pxMesh->NormalsDataPtr[iPoly].VertexNormal[v];

			Tools_ObjectProcessor_Vertex xVertexData;

			xVertexData.m_fX = xPosition.x;
			xVertexData.m_fY = xPosition.y;
			xVertexData.m_fZ = xPosition.z;

			xVertexData.m_fNX = xNormal.x;
			xVertexData.m_fNY = xNormal.y;
			xVertexData.m_fNZ = xNormal.z;

			const Asura_Mesh_TextureInfo* pxTexInfo = pxMesh->GetTextureInfoForExport(iPoly);
			xVertexData.m_fU = pxTexInfo->U[0][v];
			xVertexData.m_fV = pxTexInfo->V[0][v];

			// work out which vertices are duplicates
			int i = uNumberOfUniqueVertices;
			bool bVertexNotUnique = false;
			while(i)
			{
				i--;

				if(pxWorkspaceVertices[i] == xVertexData)
				{
					bVertexNotUnique = true;
					break;
				}
			}
	
			if (bVertexNotUnique)
			{
				iIndexNumber[v] = i;
			}
			else
			{
				iIndexNumber[v] = uNumberOfUniqueVertices;

				pxWorkspaceVertices[uNumberOfUniqueVertices] = xVertexData;
				uNumberOfUniqueVertices++;
			}
			v++;
		}
		
		while(v<4 && pxMesh->PolygonDataPtr[iPoly].VertexID[v]!=0xffff);

		usIndexData[uIndex++] = (unsigned short)(iIndexNumber[0]);
		usIndexData[uIndex++] = (unsigned short)(iIndexNumber[1]);
		usIndexData[uIndex++] = (unsigned short)(iIndexNumber[2]);
		uNumberOfTriangles++;

		//take account of quads
		if (pxMesh->PolygonDataPtr[iPoly].IsQuad())
		{
			usIndexData[uIndex++] = (unsigned short)(iIndexNumber[2]);
			usIndexData[uIndex++] = (unsigned short)(iIndexNumber[3]);
			usIndexData[uIndex++] = (unsigned short)(iIndexNumber[0]);
			uNumberOfTriangles++;
		}

		if(iPolyFlags & AsuraPolygonFlag_DoubleSided)
		{
			v=0;
			
			do
			{
				int vID =  pxMesh->PolygonDataPtr[iPoly].VertexID[v];
				Asura_Vector_3& xPosition = pxMesh->VertexDataPtr[vID].Position;
				Asura_Vector_3& xNormal = pxMesh->NormalsDataPtr[iPoly].VertexNormal[v];

				Tools_ObjectProcessor_Vertex xVertexData;

				xVertexData.m_fX = xPosition.x;
				xVertexData.m_fY = xPosition.y;
				xVertexData.m_fZ = xPosition.z;

				xVertexData.m_fNX = -xNormal.x;
				xVertexData.m_fNY = -xNormal.y;
				xVertexData.m_fNZ = -xNormal.z;

				const Asura_Mesh_TextureInfo* pxTexInfo = pxMesh->GetTextureInfoForExport(iPoly);
				xVertexData.m_fU = pxTexInfo->U[0][v];
				xVertexData.m_fV = pxTexInfo->V[0][v];

				// work out which vertices are duplicates
				int i = uNumberOfUniqueVertices;
				bool bVertexNotUnique = false;
				while(i)
				{
					i--;

					if(pxWorkspaceVertices[i] == xVertexData)
					{
						bVertexNotUnique = true;
						break;
					}
				}
		
				if (bVertexNotUnique)
				{
					iIndexNumber[v] = i;
				}
				else
				{
					iIndexNumber[v] = uNumberOfUniqueVertices;

					pxWorkspaceVertices[uNumberOfUniqueVertices] = xVertexData;
					uNumberOfUniqueVertices++;
				}
				v++;
			}
			
			while(v<4 && pxMesh->PolygonDataPtr[iPoly].VertexID[v]!=0xffff);

			usIndexData[uIndex++] = (unsigned short)(iIndexNumber[2]);
			usIndexData[uIndex++] = (unsigned short)(iIndexNumber[1]);
			usIndexData[uIndex++] = (unsigned short)(iIndexNumber[0]);
			uNumberOfTriangles++;

			//take account of quads
			if (pxMesh->PolygonDataPtr[iPoly].IsQuad())
			{
				usIndexData[uIndex++] = (unsigned short)(iIndexNumber[0]);
				usIndexData[uIndex++] = (unsigned short)(iIndexNumber[3]);
				usIndexData[uIndex++] = (unsigned short)(iIndexNumber[2]);
				uNumberOfTriangles++;
			}
		}
	}
	
	// test
	for(u_int u=0; u<uIndex; u++)
	{
		Asura_Assert(usIndexData[u]>=0,"");
		Asura_Assert(usIndexData[u]<uNumberOfUniqueVertices,"");
	}

	u_int uNumberOfVertices = uNumberOfUniqueVertices;
    u_int uNumberOfStrippedIndices = 0;
	
	if(uNumberOfTriangles)
	{
    	u_short* pusStripIndices = 0;
    	u_short* pusPermutationTable = 0;

        if(bStripify)
		{
        	Tools_TriStripper::Initialise();
	    	Tools_TriStripper::SetOutputType( Tools_TriStripper::OUTPUT_TRISTRIP_WITH_RESET_INDICES );

        	uNumberOfStrippedIndices = Tools_TriStripper::CreateStrips( uNumberOfTriangles, usIndexData, &pusStripIndices );

			Tools_TriStripper::ComputeVertexPermutation( uNumberOfStrippedIndices, pusStripIndices, uNumberOfVertices, &pusPermutationTable );

        	Tools_TriStripper::Shutdown();

			u_short usRestartValue;
			const bool bUseRestartIndex = Tools_MeshRestartIndex::IsValidForCurrentPlatform( usRestartValue );
			if( !bUseRestartIndex )
			{
				uNumberOfTriangles = uNumberOfStrippedIndices - 2;
			}
		}
		else
		{
			//not stripifying , so just transfer the index list since it will be in the correct format. RAR 05/01/2007
			uNumberOfStrippedIndices = uNumberOfTriangles*3;
			pusStripIndices = usIndexData;
			usIndexData = 0;
		}

		// create new strip for putting data in
		s_pxProcessedStrip->m_pxVertices = new Tools_ObjectProcessor_Vertex[uNumberOfUniqueVertices];
		s_pxProcessedStrip->m_pusIndices = pusStripIndices;

		// copy all the indexed vertex data
		for(u_int u=0; u<uNumberOfUniqueVertices; u++)
		{	
			u_short usIndex = pusPermutationTable ? pusPermutationTable[u] : (u_short) u;
			s_pxProcessedStrip->m_pxVertices[u].m_fX = pxWorkspaceVertices[usIndex].m_fX;
			s_pxProcessedStrip->m_pxVertices[u].m_fY = pxWorkspaceVertices[usIndex].m_fY;
			s_pxProcessedStrip->m_pxVertices[u].m_fZ = pxWorkspaceVertices[usIndex].m_fZ;
			
			s_pxProcessedStrip->m_pxVertices[u].m_fNX = pxWorkspaceVertices[usIndex].m_fNX;
			s_pxProcessedStrip->m_pxVertices[u].m_fNY = pxWorkspaceVertices[usIndex].m_fNY;
			s_pxProcessedStrip->m_pxVertices[u].m_fNZ = pxWorkspaceVertices[usIndex].m_fNZ;

			s_pxProcessedStrip->m_pxVertices[u].m_fU = pxWorkspaceVertices[usIndex].m_fU;
			s_pxProcessedStrip->m_pxVertices[u].m_fV = pxWorkspaceVertices[usIndex].m_fV;
		}	

		//s_pxProcessedStrip[uStrip].m_iMaterialID			= pxMesh->TextureDataPtr[0].m_iMaterialID;	
		s_pxProcessedStrip->m_uNumberOfIndices	= uNumberOfStrippedIndices;
		s_pxProcessedStrip->m_uNumberOfTriangles	= uNumberOfTriangles;
		s_pxProcessedStrip->m_uNumberOfVertices	= uNumberOfVertices;

        delete[] pusPermutationTable;
	}
	else
	{
		s_pxProcessedStrip->m_pxVertices = 0;
		s_pxProcessedStrip->m_pusIndices = 0;

		s_pxProcessedStrip->m_uNumberOfIndices	= 0;
		s_pxProcessedStrip->m_uNumberOfTriangles = 0;
		s_pxProcessedStrip->m_uNumberOfVertices	= 0;
	}

	// bounding box data
	s_pxProcessedStrip->m_xBBMin.x = pxMesh->BoundingBox.MinX;
	s_pxProcessedStrip->m_xBBMin.y = pxMesh->BoundingBox.MinY;
	s_pxProcessedStrip->m_xBBMin.z = pxMesh->BoundingBox.MinZ;

	s_pxProcessedStrip->m_xBBMax.x = pxMesh->BoundingBox.MaxX;
	s_pxProcessedStrip->m_xBBMax.y = pxMesh->BoundingBox.MaxY;
	s_pxProcessedStrip->m_xBBMax.z = pxMesh->BoundingBox.MaxZ;

	//s_bBeenInitialised = true;

	// BM - 02/04/2003 15:47:16 -  - We only have one texture per object at 
	// the moment. If we ever have more than one this will need to be changed.
	// The conversion mechanism means that the exported material index will be 0

	// RAR - 07/04/2003 - Material index for the shape now passed to this function , to deal
	// with files with multiple shapes.
	#if defined ASURA_USE_MATERIAL_RESPONSE_SYSTEM
	s_pxProcessedStrip->m_auMaterialResponseHashID[0] = uMatResponse1;
	s_pxProcessedStrip->m_auMaterialResponseHashID[1] = uMatResponse2;
	#endif
	s_pxProcessedStrip->m_iMaterialID = iMaterialIndex;
	
	// add up totals
	s_uTotalNumberOfTriangles += uNumberOfTriangles;
	s_uTotalNumberOfVertices  += uNumberOfVertices;
	s_uTotalNumberOfIndices   += uNumberOfStrippedIndices;

	// delete temp data
	delete [] usIndexData;
	delete [] pxWorkspaceVertices;

	s_bBeenInitialised = true;

	return true;
}	


void Tools_ObjectProcessor::Shutdown(void)
{
	if (s_bBeenInitialised)
	{
		delete s_pxProcessedStrip;
	
		s_bBeenInitialised = false;
	}
	else
	{
		Asura_Assert( s_bBeenInitialised, "Not yet Initialised!" );
	}	
}

const Tools_ObjectProcessor_Strip* Tools_ObjectProcessor::GetProcessedStrip()
{
	if (s_bBeenInitialised)
	{
			return s_pxProcessedStrip;
	}
	else
	{
		Asura_Assert( s_bBeenInitialised, "Not yet Initialised!" );
		return 0;
	}
}

u_int Tools_ObjectProcessor::GetTotalNumberOfVertices()
{
	if (s_bBeenInitialised)
	{
			return s_uTotalNumberOfVertices;
	}
	else
	{
		Asura_Assert( s_bBeenInitialised, "Not yet Initialised!" );
		return 0;
	}
}

u_int Tools_ObjectProcessor::GetTotalNumberOfIndices()
{
	if (s_bBeenInitialised)
	{
			return s_uTotalNumberOfIndices;
	}
	else
	{
		Asura_Assert( s_bBeenInitialised, "Not yet Initialised!" );
		return 0;
	}
}

