//
// Asura_PSP_ObjectRenderer.cpp
//
// Created by KJFL - Wednesday, April 07, 2004
//
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"

// This file's header.
#include "Rendering/Asura_PSP_ObjectRenderer.h"
#include "Rendering/Asura_PSP_ObjectRenderer_Internal.h"

// Asura headers
#include "Animation/Asura_TextureAnimation.h"
#include "Core/Asura_HashTree.h"
#include "Core/Asura_Material_System.h"
#include "Core/Asura_Stream.h"
#include "Core/Asura_TextureManagement.h"
#include "Light/Asura_Photon.h"
#include "Rendering/Asura_Render.h"
#include "Rendering/Asura_PSP_Graphics.h"
#include "Shapes/Asura_Shapes.h"

// PSP headers
#include "Rendering/Asura_PSP_RenderStates.h"
#include "Rendering/Asura_PSP_TextureManagement.h"

////////////////////////////////////////////////////////////////////////////////
//
// Asura_PSP_ObjectRender_Handler
//
////////////////////////////////////////////////////////////////////////////////

void Asura_PSP_ObjectRender_Handler::Reset (void)
{
	m_xHashTree.Reset();
}


int Asura_PSP_ObjectRender_Handler::GetMaterialIndex( const class Asura_Shape& xShape )
{
	const Asura_Hash_ID		uHash	= xShape.GetShapeHashID();
	Asura_PSP_ObjectStrip*	pxStrip	= m_xHashTree.GetDataByHash(uHash);

	// Return the material ID...
	return((pxStrip == NULL) ? ASURA_MATERIAL_NONE : pxStrip->m_iMaterial);
}	


Asura_PSP_ObjectStrip* Asura_PSP_ObjectRender_Handler::AddObjectStrip( const Asura_Hash_ID uHash )
{
	bool bDidExist = true;
	Asura_PSP_ObjectStrip* pxObjStrip = m_xHashTree.AddOrFindNode(uHash,&bDidExist,true);

	Asura_Assert(pxObjStrip,"Out of memory creating new hash tree node");
	Asura_Assertf(!(pxObjStrip && bDidExist),("Duplicate item 0x08%x",uHash));

	return (bDidExist) ? NULL : pxObjStrip;
}


////////////////////////////////////////////////////////////////////////////////
//
// Asura_PSP_ObjectRenderer
//
////////////////////////////////////////////////////////////////////////////////

#if defined(ASURA_USE_MULTIPLE_HANDLERS) || defined(ASURA_USE_STREAMING)
Asura_TemplateHashTree<Asura_PSP_ObjectRender_Handler>	Asura_PSP_ObjectRenderer::s_xHandlerTree;
Asura_PSP_ObjectRender_Handler*							Asura_PSP_ObjectRenderer::s_pxCurrentHandler	= NULL;
#else
Asura_PSP_ObjectRender_Handler							Asura_PSP_ObjectRenderer::s_xHandler;
#endif


void Asura_PSP_ObjectRenderer::Initialise( void )
{
	// Nothing to initialise...
}


void Asura_PSP_ObjectRenderer::Reset( void )
{
#if defined(ASURA_USE_STREAMING)
	s_xHandlerTree.Reset();
	s_pxCurrentHandler	= NULL;
#else
	GetCurrentHandler().Reset();
#endif
}

void Asura_PSP_ObjectRenderer::Shutdown( void )
{
#if defined(ASURA_USE_STREAMING) || defined(ASURA_USE_MULTIPLE_HANDLERS)
	s_xHandlerTree.Reset();
	s_pxCurrentHandler	= NULL;
#else
	s_xHandler.Reset();
#endif	// defined(ASURA_USE_STREAMING)
}

#if defined(ASURA_USE_MULTIPLE_HANDLERS) || defined(ASURA_USE_STREAMING)

void Asura_PSP_ObjectRenderer::ReparentHandler(Asura_PSP_ObjectRender_Handler& xSourceHandler, const Asura_Hash_ID uNewParentHash)
{
	Asura_PSP_ObjectRender_Handler*	pxParentHandler	= s_xHandlerTree.GetDataByHash(uNewParentHash);

	if(pxParentHandler != NULL)
	{
		xSourceHandler.m_xHashTree.SetParent(&(pxParentHandler->m_xHashTree));
	}
}

void Asura_PSP_ObjectRenderer::ReparentHandler(const Asura_Hash_ID uSourceHash, const Asura_Hash_ID uNewParentHash)
{
	Asura_PSP_ObjectRender_Handler*	pxHandler	= s_xHandlerTree.GetDataByHash(uSourceHash);

	if(pxHandler != NULL)
	{
		ReparentHandler(*pxHandler, uNewParentHash);
	}
}

Asura_PSP_ObjectRender_Handler* Asura_PSP_ObjectRenderer::CreateHandler (const Asura_Hash_ID uHash, const Asura_Hash_ID uParentHandlerHash)
{
	Asura_PSP_ObjectRender_Handler*	pxHandler	= s_xHandlerTree.GetDataByHash(uHash);

	// Do we already have this zone?
	if(pxHandler == NULL)
	{
		// No - create one...
		pxHandler	= s_xHandlerTree.AddNode(uHash);

		if(pxHandler != NULL)
		{
			ReparentHandler( *pxHandler, uParentHandlerHash );
		}
	}
	else
	{
		// Yes - this shouldn't happen...
		Asura_Assert(false, "Already have an object handler with this hash-ID.");
	}

	// Return the zone...
	return(pxHandler);
}


bool Asura_PSP_ObjectRenderer::DestroyHandler (const Asura_Hash_ID uHash)
{
	// Call the delete function...
	return(s_xHandlerTree.DeleteNodeByHash(uHash));
}

void Asura_PSP_ObjectRenderer::WriteHandlerReport(u_int uHandler, Asura_Stream& xLog)
{
	const u_int uLOGBUFFERSIZE = 64;
	char szLogBuffer[uLOGBUFFERSIZE];

	Asura_PSP_ObjectRender_Handler* pxHandler = s_xHandlerTree.GetDataByHash(uHandler);
	if (pxHandler)
	{
		if (!pxHandler->m_xHashTree.GetNumberOfNodes())
		{
			xLog << "PSP Obj:\t\t\tNone\n";
		}
		else
		{
			Asura_PrintfToBfr( szLogBuffer, "PSP Obj:\t\t\t%d Item(s)\n", uLOGBUFFERSIZE, true, pxHandler->m_xHashTree.GetNumberOfNodes() );
			xLog << szLogBuffer;
		}
	}
	else
	{
		xLog << "PSP Obj:\t\t\tNo Handler\n";
	}
}

#endif	// defined(ASURA_USE_MULTIPLE_HANDLERS) || defined(ASURA_USE_STREAMING)

void Asura_PSP_ObjectRenderer::Render( const class Asura_Shape* pxShape, const class Asura_Shape_Instance* pxShapeInst, const u_int uARGB )
{
	const Asura_PSP_ObjectRender_Handler&	xHandler	= Asura_PSP_ObjectRenderer::GetCurrentHandler();
	const Asura_PSP_ObjectStrip*			pxStrip		= xHandler.GetObjectStrip(pxShape->GetShapeHashID());

	if (!pxStrip || !pxShapeInst) return;

	Asura_PSP_Graphics::SetUseColourDoubling(true);
	
	const float fAlpha = (float)(uARGB>>24)/255.0f;
	const int iTexture = Asura_Material_System::GetTextureID( pxStrip->m_iMaterial );
	const u_int uMaterialFlags = Asura_Material_System::GetMaterialFlags(pxStrip->m_iMaterial);
	const u_int uTextureFlags = Asura_Texture_Management::GetTextureFlags(iTexture);

	Asura_PSP_Graphics::SetupEntityAlphaStates(uMaterialFlags, uTextureFlags, (fAlpha < 0.999f), Asura_Material_System::GetProjectFlags( pxStrip->m_iMaterial ));

	Asura_PSP_TextureManagement::RequestTextureIndex( iTexture );
	Asura_Render::CurrentStates.RequestZBufferEnabled(true);
	Asura_Render::CurrentStates.RequestCullMode(ASURA_CULLMODE_CW);

	Asura_TextureAnimationOutput xTextureAnim;
	if(Asura_Material_System::GetTextureAnimationState(pxStrip->m_iMaterial,0,&xTextureAnim))
	{
		Asura_PSP_RenderStates::RequestTexOffset(xTextureAnim.m_fUOffset, xTextureAnim.m_fVOffset);
	}
	else
	{
		Asura_PSP_RenderStates::RequestTexOffset(0.f, 0.f);
	}

	const Asura_Vector_3& xPosition = pxShapeInst->GetPosition();
	const Asura_Matrix_3x3& xOrientation = pxShapeInst->GetOrientation();

	// TODO - Get correct LOD

	if (!(pxStrip->m_pxVertices && pxStrip->m_pusIndices && pxStrip->m_uNumberOfTriangles))
	{
		return;
	}

	// KOR - 3/3/2006 - entity renderers now premultiply view matrix
	// into world matrix, to get around PSP's 24-bit precision rubbishness.
	Asura_PSP_Matrix4 xViewMatrix;
	GUM_MatrixMode(SCEGU_MATRIX_VIEW);
	GUM_PushMatrix();
	GUM_StoreMatrix(&xViewMatrix);
	GUM_LoadIdentity();

	Asura_PSP_Matrix4 xMatrix;

	xMatrix.x.x = xOrientation.e11;
	xMatrix.x.y = xOrientation.e21;
	xMatrix.x.z = xOrientation.e31;
	xMatrix.y.x = xOrientation.e12;
	xMatrix.y.y = xOrientation.e22;
	xMatrix.y.z = xOrientation.e32;
	xMatrix.z.x = xOrientation.e13;
	xMatrix.z.y = xOrientation.e23;
	xMatrix.z.z = xOrientation.e33;

	xMatrix.w.x = xPosition.x;
	xMatrix.w.y = xPosition.y;
	xMatrix.w.z = xPosition.z;

	xMatrix.w.w = 1.0f;

	xMatrix.x.w = 0;
	xMatrix.y.w = 0;
	xMatrix.z.w = 0;

	GUM_MatrixMode(SCEGU_MATRIX_WORLD);
	GUM_PushMatrix();

	GUM_LoadMatrix(&xViewMatrix);
	GUM_MultMatrix(&xMatrix);

	// Set up lighting
	pxShapeInst->UpdateLighting();
	Asura_PSP_Graphics::SetupEntityLights(fAlpha);

    GUM_DrawArray
	(
		SCEGU_PRIM_TRIANGLE_STRIP,
		SCEGU_COLOR_NONE|
		SCEGU_TEXTURE_FLOAT|
		SCEGU_NORMAL_FLOAT|
		SCEGU_VERTEX_FLOAT|
		SCEGU_INDEX_USHORT,
		pxStrip->m_uNumberOfTriangles+2,
		pxStrip->m_pusIndices,
		pxStrip->m_pxVertices
	);

	if(uMaterialFlags & AsuraMaterialFlag_SelfIlluminate)
	{
		Asura_Render::CurrentStates.RequestTranslucencyMode( ASURA_TRANSLUCENCY_GLOWING );
		// Disable all lights
		Asura_PSP_Graphics::SetNumberOfLights(0);

		GUM_DrawArray
		(
			SCEGU_PRIM_TRIANGLE_STRIP,
			SCEGU_COLOR_NONE|
			SCEGU_TEXTURE_FLOAT|
			SCEGU_NORMAL_FLOAT|
			SCEGU_VERTEX_FLOAT|
			SCEGU_INDEX_USHORT,
			pxStrip->m_uNumberOfTriangles+2,
			pxStrip->m_pusIndices,
			pxStrip->m_pxVertices
		);
	#if defined ASURA_RENDER_TTYPES
		Asura_Render::AddToTrianglesRendered(pxStrip->m_uNumberOfTriangles,1,
			ASURA_RENDER_TTYPE_SELF);
	#else
		Asura_Render::AddToTrianglesRendered(pxStrip->m_uNumberOfTriangles);
	#endif
	}

	GUM_PopMatrix();
	GUM_MatrixMode(SCEGU_MATRIX_VIEW);
	GUM_PopMatrix();

#if defined ASURA_RENDER_TTYPES
	Asura_Render::AddToTrianglesRendered(pxStrip->m_uNumberOfTriangles,1,
		ASURA_RENDER_TTYPE_OBJ);
#else
	Asura_Render::AddToTrianglesRendered(pxStrip->m_uNumberOfTriangles);
#endif

	// JG - 11/09/07: Set UV offsets to zero by default
	Asura_PSP_RenderStates::RequestTexOffset( 0.0f, 0.0f ); 
}


/// End of file. ///
