

/**
*
* \file Asura_Carpet_MeshManager.cpp
*
* Created by Dave Reed - 01/12/2005
*
* Copyright (C) 2005 Rebellion (Derby), All Rights Reserved.
*
**/

#include "Core/Asura.h"

#ifdef ASURA_USE_CARPET

#include "Camera/Asura_Camera.h"
#include "Carpet/Asura_Carpet_MeshManager.h"
#include "Carpet/Asura_Carpet_StyleManager.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Debugging/Asura_CPUProfile.h"
#include "Debugging/Asura_Logfile.h"
#include "Core/Asura_Timers.h"

#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
#include "Interface/Asura_CommandConsole.h"
#endif // #ifdef ASURA_SUPPORT_COMMAND_CONSOLE

#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_RenderStates.h"

#ifdef ASURA_TOOLS
#include "Misc/Tools_Render_Options.h"
#include "Rendering/Asura_Direct3d.h"
#include "Rendering/Asura_Direct3D_Shaders.h"
#include "Carpet/Asura_Direct3D_Carpet_Game_Render.h"
#include "Carpet/Tools_Carpet_Mesh.h"
#endif

#define ASURA_CARPET_MESH_CHUNK_VERSION		(3)

float								  Asura_Carpet_MeshManager::s_fFade = 1.0f;
float								  Asura_Carpet_MeshManager::s_fMovementSpeed=1.0f;
#ifdef ASURA_TOOLS
Asura_LinkList<Asura_Carpet_Triangle> Asura_Carpet_MeshManager::s_xTriangleList;
int								      Asura_Carpet_MeshManager::s_iNumTriangles=0;
#endif // #ifdef ASURA_TOOLS

void Asura_Carpet_MeshManager::Initialise()
{
	// JAT 13/02/06 : ASURA_CARPET_MESH_DATA_FRAME_SIZE_WAVEX must be a power of 2
	ASURA_STATIC_ASSERT( ( ( ASURA_CARPET_MESH_DATA_FRAME_SIZE_WAVEX & ( ASURA_CARPET_MESH_DATA_FRAME_SIZE_WAVEX - 1 ) ) == 0 ) );

	Platform_Initialise();	
}


void Asura_Carpet_MeshManager::Shutdown()
{
	Platform_Shutdown();	
#ifdef ASURA_TOOLS
	s_xTriangleList.Clear();
#endif // #ifdef ASURA_TOOLS
}


void Asura_Carpet_MeshManager::Reset()
{
#ifdef ASURA_TOOLS
	s_xTriangleList.Clear();
#endif // #ifdef ASURA_TOOLS
	Platform_Reset();
}

#ifdef ASURA_SUPPORT_COMMAND_CONSOLE
void Asura_Carpet_MeshManager::AddConsoleCommands( void )
{
	Asura_CommandConsole::AddCmd( L"CarpetFade", ConsoleCmd_SetFade, Asura_CommandConsole::REMOVE_AT_SHUTDOWN );	
}

bool Asura_Carpet_MeshManager::ConsoleCmd_SetFade( float fFade, bool bInterrogate, bool bHelp )
{
	if ( bHelp )
	{
		Asura_CommandConsole::Printf( L"Sets the fade value 0.0->1.0" );
		Asura_CommandConsole::Printf( L"Only 0.0 & 1.0 currently supported" );
	}
	else if ( bInterrogate )
	{
		const float fCurrentFade = GetFade();
		Asura_CommandConsole::Printf( L"Current fade is %f", Asura_Maths::FtoD( fCurrentFade ) );
	}
	else
	{
		if ( ( fFade >= 0.0f ) && ( fFade <= 1.0f ) )
		{
			SetFade( fFade );
			Asura_CommandConsole::Printf( L"Fade set %f", Asura_Maths::FtoD( fFade ) );
			ConsoleCmd_SetFade( 0.0f, true, false );
			return true;
		}
		else
		{
			Asura_CommandConsole::Printf( L"failed to set fade to %f, allowed range is 0.0f -> 1.0f", Asura_Maths::FtoD( fFade ) );
			return false;
		}
	}
	return true;
}
#endif // #if defined(ASURA_SUPPORT_COMMAND_CONSOLE)

#ifdef ASURA_TOOLS

void Asura_Carpet_MeshManager::ConvertFromToolData()
{
	Asura_Carpet_Triangle *pTri=Tools_Carpet_Mesh::GetFirstTriangle();
	
	// Clear our list
	s_xTriangleList.Clear();
	s_iNumTriangles=0;

	while (pTri)
	{
		// Add it to our list
		Asura_Carpet_Triangle xTri=*pTri;
		s_xTriangleList.Push_Back(xTri);
		s_iNumTriangles++;

		pTri=Tools_Carpet_Mesh::GetNextTriangle();
	}

	// Clear the tools copy of the mesh
	Tools_Carpet_Mesh::Reset();
}



void Asura_Carpet_MeshManager::ConvertToToolData(bool bUseNewFindScenePolyBlock)
{
//	Asura_TickCount uStartTicks = Asura_Timers::GetCurTicks();	

	// DLH - 19 January 2006 15:24:17 - Clear the Direct3D optimised data so itdoes not rendering
	Asura_Direct3D_Carpet_Game_Render::ClearDataSet();
	
	if (bUseNewFindScenePolyBlock)
	{
		Tools_Carpet_Mesh::BeginFindScenePoly();
	}
	

	// For each triangle in our list, add it to the tools copy of the mesh
	for (Asura_ListElt<Asura_Carpet_Triangle> *pElt=s_xTriangleList.Front();pElt;pElt=pElt->Next())
	{
		Asura_Carpet_Triangle xTri=pElt->Data();
		Tools_Carpet_Mesh::AddTriangle(&xTri);
	}

	// Clear our list
	s_xTriangleList.Clear();

	// Recompute the lighting
	Tools_Carpet_Mesh::CaptureLighting();

	if (bUseNewFindScenePolyBlock)
	{
		Tools_Carpet_Mesh::EndFindScenePoly();
	}

//	Asura_TickCount uTicks = Asura_Timers::GetCurTicks() - uStartTicks;
//	float fTime = Asura_Timers::TicksToSeconds(uTicks);
//	Asura_OutputToDebugger_Retail(("\nAsura_Carpet_MeshManager::ConvertToToolData() - took %0.02f seconds\n\n",fTime));
}

#endif //ASURA_TOOLS

void Asura_Carpet_MeshManager::Render()
{
	if (!Asura_Render::IsSystemEnabled(ASURA_RENDER_SYSTEM_CARPET))
	{
		return;
	}

#ifdef ASURA_TOOLS
	if (Tools_Render_Options::GetWireframeMode()==TOOLS_WIREFRAME_ON)
	{
		return;
	}
#endif

	ASURA_CPU_PROFILE__START_LABEL( ASURA_CPU_PROFILE__CARPET_RENDER, "Carpet" );

	const float fFade = GetFade();
	if ( fFade > 0.0f )
	{
		Platform_Render( fFade );
	}

	ASURA_CPU_PROFILE__STOP( ASURA_CPU_PROFILE__CARPET_RENDER );
}


#ifdef ASURA_TOOLS

// Remap styles - Find current style indices from their UIDs, and delete the triangle if the
//                style no longer exists
void Asura_Carpet_MeshManager::RemapStyles()
{
	Asura_LinkList<Asura_ListElt<Asura_Carpet_Triangle>*> xTrisToRemove;

	for ( Asura_ListElt<Asura_Carpet_Triangle>* pElt=s_xTriangleList.Front();pElt;pElt=pElt->Next() )
	{		
		Asura_Carpet_Triangle* pxTri=&pElt->Data();		
		pxTri->iStyle = Asura_Carpet_StyleManager::FindStyleFromUID(pxTri->iStyleUID);
		
		// If style doesn't exist, remove the triangle
		if (pxTri->iStyle<0)
		{
			xTrisToRemove.Push_Back(pElt);
		}
	}

	// Delete the elements in the to-delete list
	for ( Asura_ListElt<Asura_ListElt<Asura_Carpet_Triangle>*>* pElt = xTrisToRemove.Front();pElt;pElt=pElt->Next() )
	{
		Asura_ListElt<Asura_Carpet_Triangle>* pEltToRemove = pElt->Data();		
		s_xTriangleList.Remove( pEltToRemove );
		s_iNumTriangles--;	
	}

	xTrisToRemove.Clear();
}

#endif // #ifdef ASURA_TOOLS

#ifdef ASURA_TOOLS

// Serialize/Deserialise. 

int  Asura_Carpet_MeshManager::GetCurrentChunkVersion(void)
{
	return ASURA_CARPET_MESH_CHUNK_VERSION;
}


void Asura_Carpet_MeshManager::Serialize(Asura_Chunk_SaveBlock_Stream &xStream)
{
	xStream << s_iNumTriangles;

	for (Asura_ListElt<Asura_Carpet_Triangle> *pElt=s_xTriangleList.Front();pElt;pElt=pElt->Next())
	{		
		Asura_Carpet_Triangle xTri=pElt->Data();

		for (int i=0; i<3; i++)
		{
			xStream << xTri.xVerts[i].xPos;
			xStream << xTri.xVerts[i].fDensity;

#if defined( ASURA_DIRECT3D_BGR_SUPPORT_SM30)
			// (AAM - 22.01.2009) Replaced packed bytes with four floats for HDR values
			Asura_WriteToStream_Array_Wrapper<float> xWrapper(4,xTri.xVerts[i].afColour);
			xStream << xWrapper;
#else
			// Convert the int into floats in [0,1]
			for(u_int u=0; u<32; u+=8)
			{
				const u_int uByte = (xTri.xVerts[i].uColour>>u)&0xFF;
				const float fFloat = static_cast<float>(uByte)/255.0f;
				xStream << fFloat;
			}
#endif
		}

		xStream << xTri.fArea;
		xStream << xTri.iStyle;
		xStream << xTri.iStyleUID;
		xStream << xTri.uSeed;
		xStream << xTri.xCentre;
		xStream << xTri.fRadius;
	}

	xStream << s_fMovementSpeed;
}


void Asura_Carpet_MeshManager::Deserialize(int iVersion, Asura_Handle iFileHandle)
{
	s_xTriangleList.Clear();
	
	Asura_Chunk_File_Stream	xStream(iFileHandle);
	xStream >> s_iNumTriangles;

	for(int i=0;i<s_iNumTriangles;i++)
	{
		Asura_Carpet_Triangle xTri;		

		if (iVersion>0)
		{
			for (int i=0; i<3; i++)
			{
				xStream >> xTri.xVerts[i].xPos;
				xStream >> xTri.xVerts[i].fDensity;

				if (iVersion>2)
				{
					float afFloats[4];
					Asura_ReadFromStream_Array_Wrapper<float> xWrapper(4,afFloats);
					xStream >> xWrapper;

					#if defined( ASURA_DIRECT3D_BGR_SUPPORT_SM30)
						// Just stream the floats straight in
						for( u_int u=0; u<4; u++)
						{
							xTri.xVerts[i].afColour[u] = afFloats[u];
						}
					#else
						// Pack the floats into an integer
						xTri.xVerts[i].uColour = 0;
						u_int uShift = 0;

						for(u_int u=0; u<4; ++u)
						{
							int iInt = Asura_Maths::FloatToInt(afFloats[u]*255.0f);
							iInt = Asura_Maths::ClampToRange(iInt, 0, 255);

							xTri.xVerts[i].uColour |= (iInt << uShift);
							uShift += 8;
						}
					#endif
				}
				else
				{
					// Older chunks stored packed bytes for colour
					u_int uColour;
					xStream >> uColour;

					#if defined( ASURA_DIRECT3D_BGR_SUPPORT_SM30)
						// Unpack the integer into floats. Funky ordering here
						xTri.xVerts[i].afColour[3] = ((uColour>>24)&0xFF)/255.0f;
						xTri.xVerts[i].afColour[0] = ((uColour>>16)&0xFF)/255.0f;
						xTri.xVerts[i].afColour[1] = ((uColour>>8)&0xFF)/255.0f;
						xTri.xVerts[i].afColour[2] = ((uColour>>0)&0xFF)/255.0f;
					#else
						// Just copy the int straight across
						xTri.xVerts[i].uColour = uColour;
					#endif
				}
			}

			xStream >> xTri.fArea;
			xStream >> xTri.iStyle;
			xStream >> xTri.iStyleUID;
			xStream >> xTri.uSeed;
			xStream >> xTri.xCentre;
			xStream >> xTri.fRadius;
		}
		else
		{
			Asura_Assert(false,"Unknown carpet mesh chunk version!");
			return;
		}		

		// Only add this triangle if it has a valid style ID
		xTri.iStyle = Asura_Carpet_StyleManager::FindStyleFromUID(xTri.iStyleUID);		
		
		// If it is a valid style add it to the list
		if (xTri.iStyle >= 0 )
		{
			s_xTriangleList.Push_Back(xTri);
		}
	}

	if (iVersion>=2)
	{
		xStream >> s_fMovementSpeed;
	}
	else
	{
		s_fMovementSpeed = 1.0f;
	}

	Platform_MakeDataSet( s_xTriangleList );
}

void Asura_Carpet_MeshManager::UpdateLighting()
{
//	Asura_TickCount uStartTicks = Asura_Timers::GetCurTicks();	

	Tools_Carpet_Mesh::BeginFindScenePoly();

	for (Asura_ListElt<Asura_Carpet_Triangle> *pElt=s_xTriangleList.Front();pElt;pElt=pElt->Next())
	{		
		Asura_Carpet_Triangle& xTri = pElt->Data();
		Tools_Carpet_Mesh::CaptureLightingForOneTri( &xTri);			
	}

	Tools_Carpet_Mesh::EndFindScenePoly();

//	Asura_TickCount uTicks = Asura_Timers::GetCurTicks() - uStartTicks;
//	float fTime = Asura_Timers::TicksToSeconds(uTicks);
//	Asura_OutputToDebugger_Retail(("\nAsura_Carpet_MeshManager::UpdateLighting() - took %0.02f seconds\n\n",fTime));
}

#endif //  #ifdef ASURA_TOOLS


#endif //ASURA_USE_CARPET

