//////////////////////////////////////////////////////////////////////////////
//
// Asura_Chunk_ResourceFile_Xbox360_Botanicals.cpp
//
// Copyright (c) 2006 Rebellion. All rights reserved.
//
//////////////////////////////////////////////////////////////////////////////

/******************************************************************************/
/* Included Files                                                             */
/******************************************************************************/

#include "Core/Asura.h"

#include "ChunkSystem/Asura_Chunk_ResourceFile_Xbox360_Botanicals.h"

// Asura Platform-independent
#include "Botanicals/Asura_Botanical_Game_Render_Type_BasicInteractive.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_HalfFloat.h"
#include "Core/Asura_Hash.h"
#include "Core/Asura_Material_System.h"

// Asura Xbox 360
#include "Botanicals/Asura_Xbox360_Botanicals.h"

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

// These are the sizes of only the stream 0 components (the per-vertex mesh data)
const u_int uGEOMETRY_VERTEX_SIZE	= 32;
const u_int uBILLBOARDS_VERTEX_SIZE	= 8;

const u_int uBILLBOARD_SIZE			= 24;	// Size of per-billboard data

/******************************************************************************/
/* Asura_Chunk_ResourceFile_Xbox360_Botanicals Class Functions                */
/******************************************************************************/

////////////////////////////////////////////////////////////////////////////////
//
// Asura_Chunk_ResourceFile_Xbox360_Botanicals::Process()
//
// Processes an Xbox 360 botanicals chunk
//
// Parameters:		szFilename - Filename of incoming chunk (not used)
//					xStream - Chunk stream from which to read
//
// Return value:	None
//
////////////////////////////////////////////////////////////////////////////////


bool Asura_Chunk_ResourceFile_Xbox360_Botanicals::Process( const char *const szFilename, Asura_Chunk_Stream &xStream )
{
	Asura_Assert( !Asura_Xbox360_Botanicals_System::s_pxShapes, "Botanicals shape list already exists." );


	// DMR - 24/11/06 - Added a version check
	uint32 uVersion;
	xStream >> uVersion;
	if (Asura_Chunk_ResourceFile_Xbox360_Botanicals::CURRENT_VERSION == 6 && uVersion == 5)
	{
		//BW I only made a minor change (Asura_Platform_BGR_Instance::m_uModuleIndex changed to a short), so it should be compatible
		Asura_Assert(false,"Botanicals data is not current version. Re-export the level! Will try to load anyway.");
	}
	else if (uVersion!= Asura_Chunk_ResourceFile_Xbox360_Botanicals::CURRENT_VERSION)
	{
		Asura_Assert(false,"Botanicals data is not current version. Re-export the level!");
		return(false);
	}
	
	xStream >> uVersion;
	if( Asura_BGR_Manager::s_uChunkVersion != uVersion)
	{
		Asura_Assert(false,"Botanical Features or Version does not match. Re-export the level!");
		return(false);
	}

	xStream >> Asura_Xbox360_Botanicals_System::s_uNumShapes;

	Asura_Xbox360_Botanicals_System::s_pxShapes = new Asura_Xbox360_Botanicals_Shape[ Asura_Xbox360_Botanicals_System::s_uNumShapes ];
	Asura_Assert( Asura_Xbox360_Botanicals_System::s_pxShapes, "Failed to create botanicals shape array." );
	
	for( u_int u = 0; u < Asura_Xbox360_Botanicals_System::s_uNumShapes; ++u )
	{
		Asura_Xbox360_Botanicals_Shape& xCurrShape = Asura_Xbox360_Botanicals_System::s_pxShapes[u];
		
#if defined( ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS) && 1 == ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS
		xCurrShape.m_uSectionCount = 0;
#endif

		xStream >> xCurrShape.m_uHashID;
		xCurrShape.SetType( Asura_BGR_TypeID( u));
		
		xStream >> xCurrShape.m_iNumElements;

		xCurrShape.m_pxElements = new Asura_Xbox360_Botanicals_Element[ xCurrShape.m_iNumElements ];
		Asura_Assert( xCurrShape.m_pxElements, "Failed to create botanicals shape element array." );

		for( int i = 0; i < xCurrShape.m_iNumElements; ++i )
		{
			Asura_Xbox360_Botanicals_Element& xCurrElement = xCurrShape.m_pxElements[i];

			u_int uElementType;
			xStream >> uElementType;

			xCurrElement.m_eType = static_cast< Asura_Xbox360_Botanicals_Element::Type >( uElementType );

			xStream >> xCurrElement.m_iNumLODs;
			
#if defined( ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS) && 1 == ASURA_BOTANICAL_SUPPORT_SECTION_VERSIONS
			xStream >> xCurrElement.m_xSectionID;
			xStream >> xCurrElement.m_xVersionID;
			
			xCurrShape.m_uSectionCount = Asura_Maths::Max( xCurrShape.m_uSectionCount, (xCurrElement.m_xSectionID + 1));
#endif

			switch( xCurrElement.m_eType )
			{
				case Asura_Xbox360_Botanicals_Element::TYPE_GEOMETRY_1SIDED:
				case Asura_Xbox360_Botanicals_Element::TYPE_GEOMETRY_2SIDED:
				{
					xCurrElement.m_pxGeometryLODs = new Asura_Xbox360_Botanicals_LOD_Geometry[ xCurrElement.m_iNumLODs ];
					Asura_Assert( xCurrElement.m_pxGeometryLODs, "Failed to create botanicals element LOD array." );

					break;
				}

				case Asura_Xbox360_Botanicals_Element::TYPE_BILLBOARDS:
				{
					xCurrElement.m_pxBillboardLODs = new Asura_Xbox360_Botanicals_LOD_Billboards[ xCurrElement.m_iNumLODs ];
					Asura_Assert( xCurrElement.m_pxBillboardLODs, "Failed to create botanicals element LOD array." );

					break;
				}

				case Asura_Xbox360_Botanicals_Element::TYPE_IMPOSTER:
				{
					xCurrElement.m_pxImposterLODs = new Asura_Xbox360_Botanicals_LOD_Imposter[ xCurrElement.m_iNumLODs ];
					Asura_Assert( xCurrElement.m_pxImposterLODs, "Failed to create botanicals element LOD array." );

					break;
				}

				default:
				{
					Asura_Assert( false, "Unknown botanicals element type." );
					break;
				}
			};

			for( int j = 0; j < xCurrElement.m_iNumLODs; ++j )
			{
				switch( xCurrElement.m_eType )
				{
					case Asura_Xbox360_Botanicals_Element::TYPE_GEOMETRY_1SIDED:
					case Asura_Xbox360_Botanicals_Element::TYPE_GEOMETRY_2SIDED:
					{
						Asura_Xbox360_Botanicals_LOD_Geometry& xCurrLOD = xCurrElement.m_pxGeometryLODs[j];
						xStream >> xCurrLOD.m_uMaterialResponseHashID;						

						xStream >> xCurrLOD.m_uNumIndices >> xCurrLOD.m_uNumTriangles >> xCurrLOD.m_uNumVertices;

						// Read per-vertex data

						xCurrLOD.m_xVB.CreateStatic( uGEOMETRY_VERTEX_SIZE, xCurrLOD.m_uNumVertices );

						void* const pVertexData = xCurrLOD.m_xVB.Lock();
						Asura_Assert( pVertexData, "Failed to lock botanicals geometry vertex buffer." );

						if( NULL != pVertexData)
						{
							xStream.Read( uGEOMETRY_VERTEX_SIZE * xCurrLOD.m_uNumVertices, pVertexData );
						}
						else
						{
							xStream.Skip( uGEOMETRY_VERTEX_SIZE * xCurrLOD.m_uNumVertices);
						}
						xCurrLOD.m_xVB.Unlock();

						// Read index list

						xCurrLOD.m_xIB.CreateStatic( sizeof( u_int ), xCurrLOD.m_uNumIndices );

						void* const pIndexData = xCurrLOD.m_xIB.Lock();
						Asura_Assert( pIndexData, "Failed to lock botanicals geometry index buffer." );

						if( NULL != pIndexData)
						{
							xStream.Read( sizeof( u_int ) * xCurrLOD.m_uNumIndices, pIndexData );
						}
						else
						{
							xStream.Skip( sizeof( u_int ) * xCurrLOD.m_uNumIndices);
						}
						xCurrLOD.m_xIB.Unlock();

						break;
					}

					case Asura_Xbox360_Botanicals_Element::TYPE_BILLBOARDS:
					{
						Asura_Xbox360_Botanicals_LOD_Billboards& xCurrLOD = xCurrElement.m_pxBillboardLODs[j];

						xStream >> xCurrLOD.m_uMaterialResponseHashID;					

						// Read per-vertex data

						xStream >> xCurrLOD.m_uNumVertices;
						xCurrLOD.m_xVertexVB.CreateStatic( uBILLBOARDS_VERTEX_SIZE, xCurrLOD.m_uNumVertices );

						void* const pVertexData = xCurrLOD.m_xVertexVB.Lock();
						Asura_Assert( pVertexData, "Failed to lock botanicals billboard vertex buffer." );

						if( NULL != pVertexData)
						{
							xStream.Read( uBILLBOARDS_VERTEX_SIZE * xCurrLOD.m_uNumVertices, pVertexData );
						}
						else
						{
							xStream.Skip( uBILLBOARDS_VERTEX_SIZE * xCurrLOD.m_uNumVertices);
						}
						xCurrLOD.m_xVertexVB.Unlock();

						// Read per-billboard data

						Asura_Assert( xCurrLOD.m_uNumVertices % 4 == 0, "Number of verts in botanicals billboard cluster isn't a multiple of 4." );
						const u_int uNumBillboards = xCurrLOD.m_uNumVertices / 4;

						xCurrLOD.m_xBillboardVB.CreateStatic( uBILLBOARD_SIZE, uNumBillboards );

						void* const pBillboardData = xCurrLOD.m_xBillboardVB.Lock();
						Asura_Assert( pBillboardData, "Failed to lock botanicals billboard vertex buffer." );

						if( NULL != pBillboardData)
						{
							xStream.Read( uBILLBOARD_SIZE * uNumBillboards, pBillboardData );
						}
						else
						{
							xStream.Skip( uBILLBOARD_SIZE * uNumBillboards);
						}
						xCurrLOD.m_xBillboardVB.Unlock();

						break;
					}

					case Asura_Xbox360_Botanicals_Element::TYPE_IMPOSTER:
					{						
	   					Asura_Xbox360_Botanicals_LOD_Imposter& xCurrLOD = xCurrElement.m_pxImposterLODs[j];
						
						// Read number of billboards (different orientations)
						xStream >> xCurrLOD.m_iNumImposterAngles;
						xStream >> xCurrLOD.m_uMaterialResponseHashID;							
						
						
						// Create the VB
						xCurrLOD.m_apxAngles = new Asura_Xbox360_Botanicals_ImposterAngle[xCurrLOD.m_iNumImposterAngles];		 						

						// Alloc them						
						for (int i=0;i<xCurrLOD.m_iNumImposterAngles; i++)
						{						
							Asura_Xbox360_Botanicals_ImposterAngleData *pxData = &xCurrLOD.m_apxAngles[i].m_xData;

							// Fill them in. Todo: Move these float->halffloat conversions to the export step?
							float fTmp;
							Asura_Vector_3 xTmp;

							// Width/Height
							xStream >> fTmp;	pxData->afWidthHeight[0]=fTmp;
							xStream >> fTmp;	pxData->afWidthHeight[1]=fTmp;

							// UV set
							xStream >> fTmp;	pxData->afUVSet[0]=fTmp;
							xStream >> fTmp;	pxData->afUVSet[1]=fTmp;
							xStream >> fTmp;	pxData->afUVSet[2]=fTmp;
							xStream >> fTmp;	pxData->afUVSet[3]=fTmp;
								
							// Packed normal
							xStream >> pxData->uNormal;
							xStream >> pxData->uTangent;
							xStream >> pxData->uBinormal;

							// position of origin
							xStream >> xTmp;	
							pxData->afPos[0] = xTmp.x;
							pxData->afPos[1] = xTmp.y;
							pxData->afPos[2] = xTmp.z;
							pxData->afPos[3] = 1.0f;
							
							// Angle fade params
							xStream >> fTmp;	pxData->afAngleFade[0] = fTmp * (Asura_Maths::Pi/180.0f);
							xStream >> fTmp;	pxData->afAngleFade[1] = fTmp * (Asura_Maths::Pi/180.0f);					
						}


						// Create the vertex buffer
						xCurrLOD.m_xImposterAngleDataVB.CreateStatic(sizeof(Asura_Xbox360_Botanicals_ImposterAngleData), xCurrLOD.m_iNumImposterAngles);

						Asura_Xbox360_Botanicals_ImposterAngleData *pxVB = static_cast<Asura_Xbox360_Botanicals_ImposterAngleData *>(xCurrLOD.m_xImposterAngleDataVB.Lock());
						
						Asura_Assert(pxVB, "Failed to create VB (botanical imposters)");

						// Fill it in
						if (pxVB)
						{						
							for (int i=0;i<xCurrLOD.m_iNumImposterAngles; i++)
							{
								memcpy(pxVB,&xCurrLOD.m_apxAngles[i].m_xData,sizeof(Asura_Xbox360_Botanicals_ImposterAngleData));
								pxVB++;
							}

							xCurrLOD.m_xImposterAngleDataVB.Unlock();
						}

						break;
					}

					default:
					{
						Asura_Assert( false, "Unknown botanicals element type." );
						break;
					}
				}
			}

			xStream >> xCurrElement.m_fBSCentreHeight;
			xStream >> xCurrElement.m_fBSRadius;

			xCurrElement.m_pxLODData = new Asura_Xbox360_Botanicals_LODData[ xCurrElement.m_iNumLODs ];
			Asura_Assert( xCurrElement.m_pxLODData, "Failed to allocate botanicals LOD data array." );

			xStream.Read( sizeof( Asura_Xbox360_Botanicals_LODData ) * xCurrElement.m_iNumLODs, xCurrElement.m_pxLODData );
		}
		
		xCurrShape.Process( xStream);
	}

	xStream >> Asura_Xbox360_Botanicals_System::s_uNumTemplates;

	Asura_Xbox360_Botanicals_System::s_pxTemplates = new Asura_Xbox360_Botanicals_Template[ Asura_Xbox360_Botanicals_System::s_uNumTemplates ];
	Asura_Assert( Asura_Xbox360_Botanicals_System::s_pxTemplates, "Failed to create botanicals template array." );

	for( u_int u = 0; u < Asura_Xbox360_Botanicals_System::s_uNumTemplates; ++u )
	{
		Asura_Xbox360_Botanicals_Template& xCurrTemplate = Asura_Xbox360_Botanicals_System::s_pxTemplates[u];

		xStream >> xCurrTemplate.m_xName;

		u_int uBotanicalsShapeIndex;
		xStream >> uBotanicalsShapeIndex;

		xCurrTemplate.m_pxShape = &Asura_Xbox360_Botanicals_System::s_pxShapes[ uBotanicalsShapeIndex ];
		xCurrTemplate.SetType( Asura_BGR_TypeID( uBotanicalsShapeIndex));
		
		Asura_Bounding_Box xTemplateBoundingBox;
		xStream >> xTemplateBoundingBox;
		xTemplateBoundingBox.Grow( xCurrTemplate.m_pxShape->GetBoundingRadius());
		xCurrTemplate.SetBoundingBox( xTemplateBoundingBox);
		
//		xCurrTemplate.Process( xStream);
		
		xStream >> xCurrTemplate.m_iNumInstances;
		xCurrTemplate.m_iOffsetToNextBase = 1;

		xCurrTemplate.m_pxInstances = new Asura_Xbox360_Botanicals_Instance[ xCurrTemplate.m_iNumInstances ];

		if( !xCurrTemplate.m_pxInstances )
		{
			Asura_Assert( false, "Unable to allocate botanicals instance array." );
			xStream.Skip( sizeof( Asura_Xbox360_Botanicals_Instance ) * xCurrTemplate.m_iNumInstances );

			continue;
		}

		xStream.Read( sizeof( Asura_Xbox360_Botanicals_Instance ) * xCurrTemplate.m_iNumInstances, xCurrTemplate.m_pxInstances );
		
		// Allocate enough visibility queries so we can store one per instance
		xCurrTemplate.m_pxVisibilityQueries = new Asura_Static_Visibility_Query[ xCurrTemplate.m_iNumInstances ];

#if defined( ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA) && 1 == ASURA_BOTANICAL_SUPPORT_DYNAMIC_DATA
		xCurrTemplate.SetDynamicDataLayout( xCurrTemplate.m_pxShape->GetDynamicDataLayout());
		xCurrTemplate.AllocateDynamicData( xCurrTemplate.m_iNumInstances);
#endif
	}

	for(int i=0; i< Asura_Xbox360_Botanicals_System::NUM_INSTANCE_BUFFERS; i++)
	{		
		Asura_Xbox360_Botanicals_System::s_axInstanceVBs[i].CreateStatic( sizeof(Asura_Xbox360_Botanicals_Instance::RenderData), ASURA_BOTANICALS_MAX_ELEMENT_INSTANCES_PER_FRAME );

		#if defined ASURA_ASSERT
			const void* const pData = Asura_Xbox360_Botanicals_System::s_axInstanceVBs[i].Lock();
			Asura_Assert( pData, "Failed to create botanicals instance vertex buffer." );
			Asura_Xbox360_Botanicals_System::s_axInstanceVBs[i].Unlock();
		#endif
	}

	{	
		Asura_Xbox360_Botanicals_Template* pxCurrTemplateBase = Asura_Xbox360_Botanicals_System::s_pxTemplates;
		for( u_int u = 1; u < Asura_Xbox360_Botanicals_System::s_uNumTemplates; ++u)
		{
			Asura_Xbox360_Botanicals_Template* pxCurrTemplate = Asura_Xbox360_Botanicals_System::s_pxTemplates + u;
			
			if( pxCurrTemplate->m_pxShape != pxCurrTemplateBase->m_pxShape)
			{
				pxCurrTemplateBase = pxCurrTemplate;
				continue;
			}
			
			pxCurrTemplateBase->m_iOffsetToNextBase += 1;
		}
	}

	return(true);
}
