//*******************************************************************
//
// File: Asura_Botanical_Game_Render_Manager.h
//
// Created: 30 Nov 2006
//
// Description: 
//
//*******************************************************************

#if defined( ASURA_USE_BOTANICALS )

#ifndef _ASURA_BOTANICAL_GAME_RENDER_MANAGER_H_
#define _ASURA_BOTANICAL_GAME_RENDER_MANAGER_H_

// other libraries
#include "Botanicals/Asura_Botanical_Game_Render_Constants.h"
#include "Botanicals/Asura_Botanical_Game_Render_TypeID.h"
#include "Botanicals/Asura_Botanical_Game_Render_Type.h"
#include "Botanicals/Asura_Botanical_Game_Render_Instance.h"
#include "Botanicals/Asura_Botanical_Game_Render_Instance_Header.h"
#include "Core/Asura_Maths.h"
#include "Shapes/Asura_Shape_System.h"
#include "Animation/Asura_Animation.h"

// internal dependencies

// forward declarations
class Asura_BGR_Type;
class Asura_BGR_Instances_Header;

//*******************************************************************
// Constants
//*******************************************************************

#if 0
	#define ASURA_BGR_MANAGER_DEBUG_LINES	(1)
#endif

#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
	#include "Debugging/Asura_DebugLineRender.h"
#endif


//*******************************************************************
// Class Definitions
//*******************************************************************

class Asura_BGR_Manager
{
public:
	virtual ~Asura_BGR_Manager( void ) {}
//	static void FinishAllJobs();
	static void Initialise();
	static void Reset();
	static void Shutdown();
	
	static Asura_BGR_Manager& GetBGRManager( void);	

	virtual bool Update( const float fTime);

	virtual void Platform_Reset() {}
	
	virtual u_int GetInstanceHeaderCount( void) const = 0;
	virtual const Asura_BGR_Instances_Header* GetInstanceHeader( const u_int uInstanceHeaderIndex) const = 0;
	virtual Asura_BGR_Instances_Header* GetInstanceHeader( const u_int uInstanceHeaderIndex) = 0;
	virtual Asura_BGR_Type* GetType( const Asura_BGR_TypeID xTypeID) = 0;
	
	virtual u_int GetStaticCheckSum( void) const { return( 0);}
	
#if defined( ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA) && 1 == ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA
	void ResetDynamicData();
	template< class TcallbackData> u_int ProcessArea( TcallbackData& tCallbackData, const Asura_Bounding_Box& xBoundingBox);
	// (MH - 16.12.2008) ProcessRay() seems to be completely unused and doesn't even compile if 
	// ASURA_BOTANICAL_SUPPORT_STATES is disabled. Therefore I've commented it out.
	//template< class TcallbackData> u_int ProcessRay( TcallbackData& tCallbackData, const Asura_Vector_3& xAlpha, const Asura_Vector_3& xBeta, float fMaxRange = 100000.0f);	
#endif

	static void Update();

	static const u_int s_uChunkVersion;

	static float s_fDualSidedLightingLerp;

#if defined( ASURA_SUPPORT_COMMAND_CONSOLE)
	static bool s_bDebugVarShow;
	static bool s_bDebugVarShowPixels;
#endif


#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
	virtual void RenderCollisionMesh( const Asura_CollisionMesh *pxMesh, Asura_Vector_3 &xInstancePosition, Asura_Matrix_3x3 &xOri, uint32 uCol)
	{
		uint32 uPolys = pxMesh->GetNumberOfPolys();

		for(uint32 uPoly=0; uPoly<uPolys; uPoly++)
		{
			Asura_Vector_3 axVerts[4];
			Asura_Vector_3 axWorldVerts[4];
			uint32 uVertCount = pxMesh->GetNumVerticesInPoly(uPoly);
			pxMesh->GetVertices(axVerts,uPoly);

			for(uint32 uVert=0; uVert<uVertCount; uVert++)
			{
				axWorldVerts[uVert] = (axVerts[uVert] * xOri)+xInstancePosition;
			}

			for(uint32 uVert=0; uVert<uVertCount; uVert++)
			{
				Asura_DebugLineRender::AddLine(axWorldVerts[uVert], axWorldVerts[(uVert+1)%uVertCount], uCol);
			}
		}
	}
#endif

};





//*******************************************************************
// Inline Function Definitions
//*******************************************************************

#if defined( ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA) && 1 == ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA
template< class TcallbackData> 
inline u_int Asura_BGR_Manager::ProcessArea( TcallbackData& tCallbackData, const Asura_Bounding_Box& xBoundingBox)
{
	u_int uInstanceHeaderCount = this->GetInstanceHeaderCount();
	u_int uNumProcessed = 0;

	for( u_int uInstanceHeaderIndex = 0; uInstanceHeaderIndex < uInstanceHeaderCount; uInstanceHeaderIndex += 1)
	{
		Asura_BGR_Instances_Header* pxInstanceHeader = this->GetInstanceHeader( uInstanceHeaderIndex);
		if( NULL == pxInstanceHeader)
		{
			continue;
		}
		
		const u_int uInstanceCount = pxInstanceHeader->GetInstanceCount();
		if( 0 == uInstanceCount)
		{
			continue;
		}		

		if( false == pxInstanceHeader->HasDynamicSectionVersionData())
		{
			continue;
		}

		const Asura_Bounding_Box& xInstanceHeaderBoundingBox = pxInstanceHeader->GetBoundingBox();

		if( false == xInstanceHeaderBoundingBox.Intersects( xBoundingBox))
		{
			continue;
		}

		const Asura_BGR_Type* pxType = this->GetType( pxInstanceHeader->GetType());
		if( NULL == pxType)
		{
			continue;
		}

		const bool bProcessInstanceHeader = tCallbackData.ProcessInstanceHeader( pxInstanceHeader, pxType);
		if( false == bProcessInstanceHeader)
		{
			continue;
		}
		
		const float fTypeRadius = pxType->GetBoundingRadius();

		for( u_int uInstanceIndex = 0; uInstanceIndex < uInstanceCount; uInstanceIndex += 1)
		{
			const Asura_BGR_Instance* pxInstance = pxInstanceHeader->GetInstance( uInstanceIndex);

#if defined( ASURA_TOOLS)
			if( false == pxInstance->IsActive())
			{
				continue;
			}
#endif
			Asura_Vector_3 xInstancePosition;

			pxInstance->GetInstancePosition( xInstancePosition);

			if( false == xBoundingBox.IntersectsSphere( xInstancePosition, fTypeRadius))
			{
				continue;
			}

			Asura_AVector3 xInstancePositionA( xInstancePosition);

			Asura_BGR_DynamicInstance* const pxDynamicInstance = pxInstanceHeader->GetDynamicInstanceData( uInstanceIndex);

			if(tCallbackData.ProcessInstance( pxInstanceHeader, pxInstance, pxType, xInstancePositionA, pxDynamicInstance))
			{
				uNumProcessed++;
			}
		}
	}

	return(uNumProcessed);
}
#endif

// (MH - 16.12.2008) ProcessRay() seems to be completely unused and doesn't even compile if 
// ASURA_BOTANICAL_SUPPORT_STATES is disabled. Therefore I've commented it out.
//#if defined( ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA) && 1 == ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA
//template< class TcallbackData> 
//inline u_int Asura_BGR_Manager::ProcessRay( TcallbackData& tCallbackData, const Asura_Vector_3& xRayOrigin, const Asura_Vector_3& xRayDirection, float fMaxRange)
//{
//	u_int uInstanceHeaderCount = this->GetInstanceHeaderCount();
//	u_int uNumProcessed = 0;
//	
//	const Asura_Vector_3 xRayTarget = xRayOrigin + xRayDirection;
//	
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//	Asura_Vector_3 xDebugLinePosition( 0, 0, 0);
//	Asura_Matrix_3x3 xDebugLineOrientation( 1, 0, 0,  0, 1, 0,  0, 0, 1);
//	Asura_DebugLineRender::AddLine( xRayOrigin, xRayTarget, 0xFF0000FF);
//#endif
//	
//
//	for( u_int uInstanceHeaderIndex = 0; uInstanceHeaderIndex < uInstanceHeaderCount; uInstanceHeaderIndex += 1)
//	{
//		Asura_BGR_Instances_Header* pxInstanceHeader = this->GetInstanceHeader( uInstanceHeaderIndex);
//		if( NULL == pxInstanceHeader)
//		{
//			continue;
//		}
//		
//		const u_int uInstanceCount = pxInstanceHeader->GetInstanceCount();
//		if( 0 == uInstanceCount)
//		{
//			continue;
//		}		
//
//		if( false == pxInstanceHeader->HasDynamicSectionVersionData())
//		{
//			continue;
//		}
//
//		const Asura_Bounding_Box& xInstanceHeaderBoundingBox = pxInstanceHeader->GetBoundingBox();
//
//		Asura_Vector_3 xHit;
//		
//		{
//			float fDistance = 0.0f;
//			const bool bRayWithInstanceHeaderBoundingBox = Asura_Maths::RayIntersectsBoundingBox( xRayOrigin, xRayDirection, xInstanceHeaderBoundingBox, &fDistance, &xHit);
//			if( false == bRayWithInstanceHeaderBoundingBox)
//			{
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//		Asura_DebugLineRender::AddBoundingBox( xInstanceHeaderBoundingBox, xDebugLinePosition, xDebugLineOrientation, 0xFF00FF00);
//#endif
//				continue;
//			}
//			
//			if( fDistance < 0.0f)
//			{
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//		Asura_DebugLineRender::AddBoundingBox( xInstanceHeaderBoundingBox, xDebugLinePosition, xDebugLineOrientation, 0xFF00FF00);
//#endif
//				continue;
//			}
//		}
//
//		const Asura_BGR_Type* pxType = this->GetType( pxInstanceHeader->GetType());
//		if( NULL == pxType)
//		{
//			continue;
//		}
//
//		const bool bProcessInstanceHeader = tCallbackData.ProcessInstanceHeader( pxInstanceHeader, pxType);
//		if( false == bProcessInstanceHeader)
//		{
//			continue;
//		}
//		
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//		Asura_DebugLineRender::AddBoundingBox( xInstanceHeaderBoundingBox, xDebugLinePosition, xDebugLineOrientation, 0xFFFF0000);
//#endif
//		const float fTypeRadius = pxType->GetBoundingRadius();
//
//		for( u_int uInstanceIndex = 0; uInstanceIndex < uInstanceCount; uInstanceIndex += 1)
//		{
//			const Asura_BGR_Instance* pxInstance = pxInstanceHeader->GetInstance( uInstanceIndex);
//
//#if defined( ASURA_TOOLS)
//			if( false == pxInstance->IsActive())
//			{
//				continue;
//			}
//#endif
//			Asura_Vector_3 xInstancePosition;
//
//			pxInstance->GetInstancePosition( xInstancePosition);
//
//			{				
//				float fDistance;
//				float fInstanceScale = pxInstance->GetScale();
//
//				const bool bRayWithInstanceSphere = Asura_Maths::RayIntersectsSphere( xRayOrigin, xRayDirection, xInstancePosition, fTypeRadius * fInstanceScale, &fDistance);
//				if( false == bRayWithInstanceSphere)
//				{
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//					Asura_DebugLineRender::AddSphere( xInstancePosition, fTypeRadius * fInstanceScale, 0xFF00FF00);
//#endif
//					continue;
//				}
//				
//				if( fDistance < 0.0f)
//				{
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//					Asura_DebugLineRender::AddSphere( xInstancePosition, fTypeRadius * fInstanceScale, 0xFF00FF00);
//#endif
//					continue;
//				}
//
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//				Asura_DebugLineRender::AddSphere( xInstancePosition, fTypeRadius * fInstanceScale, 0xFFFF0000);
//#endif
//			}
//
//#if 1
//			
//			float fDist;
//			Asura_Vector_3 xHitPos;
//
//			const Asura_Bounding_Box& xTypeBoundingBox = pxType->GetBoundingBox();
//			
//			Asura_Vector_3 xRayLocalOrigin = xRayOrigin - xInstancePosition;
//			Asura_Vector_3 xRayLocalTarget = xRayTarget - xInstancePosition;
//			
//			//Get orientation of the instance
//			Asura_Matrix_3x3 xInvOri;
//			pxInstance->GetOrientationInversed(xInvOri);
//			
//			xRayLocalOrigin = xRayLocalOrigin * xInvOri;
//			xRayLocalTarget = xRayLocalTarget * xInvOri;
//			
//			Asura_Vector_3 xRayLocalDirection = xRayLocalTarget - xRayLocalOrigin;
//				
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//			//Get orientation of the instance
//			Asura_Matrix_3x3 xOri;
//			pxInstance->GetOrientation(xOri);
//#endif
//
//			const bool bRayIntersectsWithBB = Asura_Maths::RayIntersectsBoundingBox( xRayLocalOrigin, xRayLocalDirection, xTypeBoundingBox, &fDist, &xHitPos );
//
//			if ( false == bRayIntersectsWithBB )
//			{
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//				Asura_DebugLineRender::AddBoundingBox( xTypeBoundingBox, xInstancePosition, xOri, 0xFF606060);
//#endif
//				continue;
//			}
//			if ( fDist < 0.0f )
//			{
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//				Asura_DebugLineRender::AddBoundingBox( xTypeBoundingBox, xInstancePosition, xOri, 0xFF606060);
//#endif
//				continue;
//			}
//			
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//			Asura_DebugLineRender::AddBoundingBox( xTypeBoundingBox, xInstancePosition, xOri, 0xFFFF00FF);
//#endif
//			Asura_AVector3 xHitPosA = Asura_AVector3(xHitPos);
//#endif
//
//
//			//don't need this anymore as we've the actual position from the BB intersect test
//			Asura_AVector3 xInstancePositionA( xInstancePosition);
//
//			Asura_BGR_DynamicInstance* const pxDynamicInstance = pxInstanceHeader->GetDynamicInstanceData( uInstanceIndex);
//
//
//
//			// Foliage collision meshes are stored in the state tables
//			// Test against any collision mesh(es) that are valid for the current state
//			const Asura_CollisionMesh *pxHitMesh = NULL;
//			int   iNumStates = pxType->GetStateCount();						
//			float fDensity = 0;
//			bool  bUseBBox = false;
//
//			for(int iState=0; iState<iNumStates; iState++)
//			{
//				Asura_BGR_State_Value xVal = pxInstanceHeader->GetState(pxDynamicInstance , 0);
//				
//				const Asura_BGR_StateTable& xStateTable = pxType->GetStateTable(iState);
//
//				for(uint32 uSection = 0; uSection < xStateTable.m_xArray_xSections.Size(); uSection++)
//				{
//					const Asura_BGR_StateTable_Section &xSection = xStateTable.m_xArray_xSections[uSection];
//
//					for(uint32 uVersion = 0; uVersion < xSection.m_xArray_xVersions.Size(); uVersion++)
//					{
//						const Asura_BGR_StateTable_Version &xVersion = xSection.m_xArray_xVersions[uVersion];
//
//						if (xVal>=xVersion.m_xRangeStart && xVal<=xVersion.m_xRangeStop)
//						{
//							
//
//							if (xVersion.m_bCollisionUseBoundingBox)
//							{
//								bUseBBox = true;
//								fDensity = xVersion.m_fCollisionMeshDensity;											
//							}
//
//							// Do we have a collision mesh?
//							if (xVersion.m_xCollisionMeshHash!=ASURA_HASH_ID_UNSET && (!bUseBBox))
//							{								
//#ifndef ASURA_TOOLS
//								// In non-tools builds it should be safe to cache this pointer. In tools builds it isn't
//								if (!xVersion.m_pxCollisionMesh)
//#endif
//								{
//									Asura_Hierarchy_Skin *pxCollSkin = Asura_Animation_System::GetSkinByHash(xVersion.m_xCollisionMeshHash);
//									if ( pxCollSkin == NULL )
//									{
//										Asura_Assert(false, "failed to find skin for collision skin on botanical");
//										continue;
//									}
//									xVersion.m_pxCollisionMesh = pxCollSkin->GetCollisionMesh();
//								}
//								
//
//								if (xVersion.m_pxCollisionMesh)
//								{								
//									const Asura_CollisionMesh *pxCollMesh = xVersion.m_pxCollisionMesh;
//
//									if (pxCollMesh)
//									{				
//										// Use the instance-local ray. We can't just pass the instance pos/orient into LOSTestPolygons, as our orientation matrix
//										// can also contain a scale - and that function does a matrix transpose where we'd need an inverse to handle scaling
//
//										Asura_Vector_3 xRayLocalDirectionNormalised = xRayLocalDirection;
//										float fOneOverScaleFactor = xRayLocalDirectionNormalised.Normalise();
//
//										Asura_LOS_InputData xInputData( xRayLocalOrigin, xRayLocalDirectionNormalised, ASURA_GUID_UNREGISTERED, fMaxRange*fOneOverScaleFactor );
//										Asura_LOS_OutputData xOutputData;		
//										xOutputData.m_fDistanceToIntersection = fMaxRange*fOneOverScaleFactor;
//
//										Asura_Matrix_3x3 xIdentity;
//										xIdentity.Identity();
//
//										if (pxCollMesh->LOSTestPolygons(Asura_Vector_3(0,0,0), xIdentity, &xInputData, &xOutputData))
//										{
//											// It's a hit
//											pxHitMesh = pxCollMesh;
//
//											// Update the intersection position
//											xHitPosA = xRayOrigin + (xRayDirection * (xOutputData.m_fDistanceToIntersection/fOneOverScaleFactor));
//
//											// Store the collision mesh density (0 = transparent, 1= opaque)
//											fDensity = xVersion.m_fCollisionMeshDensity;											
//										}													
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//										RenderCollisionMesh( pxCollMesh, xInstancePosition, xOri, 0xFF606060);
//#endif
//
//									}
//								}
//							}							
//						}						
//					}
//				}								
//			}
//
//#if defined( ASURA_BGR_MANAGER_DEBUG_LINES)
//			if (bUseBBox)
//			{
//				// Accepting a hit to the BB
//				Asura_DebugLineRender::AddBoundingBox( xTypeBoundingBox, xInstancePosition, xOri, 0xFFFF00FF);
//			}
//			else
//			{
//				// Acceoting a hit to the coll mesh
//				if (pxHitMesh)
//				{
//					RenderCollisionMesh( pxHitMesh, xInstancePosition, xOri, 0xFFFF00FF);
//				}
//			}			
//#endif
//
//			if ((!pxHitMesh) && (!bUseBBox))
//			{				
//				continue;
//			}
//
//
//			if(tCallbackData.ProcessInstance( pxInstanceHeader, pxInstance, pxType, xInstancePositionA, xHitPosA, fDensity, pxDynamicInstance))
//			{
//				uNumProcessed++;
//			}
//		}
//	}
//
//	return(uNumProcessed);
//}
//#endif

//*******************************************************************
//
// (c) 2006 Rebellion
//
// Warning: This computer program is protected by copyright law and 
// international treaties. Unauthorized reproduction or distribution
// of this program, or any portion of it, may result in severe civil and
// criminal penalties, and will be prosecuted to the maximum extent 
// possible under the law.
//
//*******************************************************************

#endif // _ASURA_BOTANICAL_GAME_RENDER_MANAGER_H_

#endif // #if defined( ASURA_USE_BOTANICALS )
