
/**
*
* \file Asura_Chunk_Botanicals_Definition.cpp
*
* Created by Dave Reed - 11/11/2005
*
* Copyright (C) 2002 Rebellion (Derby), All Rights Reserved.
*
**/

#include "Core/Asura.h"

#ifdef ASURA_USE_BOTANICALS

#include "Botanicals/Asura_Botanicals_InstanceManager.h"
#include "Camera/Asura_Camera.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "ChunkSystem/Asura_Chunk_Loaders.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_SaveBlock.h"
#include "Core/Asura_Handle.h"
#include "Core/Asura_String.h"
#include "Debugging/Asura_Assert.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Rendering/Asura_Viewport.h"

#include "ChunkSystem/Asura_Chunk_Botanicals_Definition.h"

#include "Botanicals/Asura_Botanicals_ShapeManager.h"

#ifdef ASURA_TOOLS
#include "Misc/Tools_Timed_Messages.h"
#include "Animation/Asura_Animation.h"


////////////////////////////////////////////////////////////////////////////////
// 6 - ART - 26/01/07 - Added project-specific flags to state-table versions.
// 8,9 - DMR - 13/11/07 - Adding foliage collision shapes
////////////////////////////////////////////////////////////////////////////////
static const short	g_sCurrentChunkVersion	= 9;


bool Asura_Chunk_Botanicals_Definition::Create( const char* szFileNameWithNoExtension)
{

	Asura_String xFileName=szFileNameWithNoExtension;

	Asura_BotanicalShape *pxShape=Asura_Botanicals_ShapeManager::GetShapeByHash(Asura_GetHashID(szFileNameWithNoExtension));	
	if (!pxShape) return false;

	Tools_BotanicalShape *pxToolsShape=pxShape->GetToolsShape();
	if (!pxToolsShape) return false;

	Asura_SaveBlock* pxSaveBlock = new Asura_SaveBlock();
	Asura_Assert(pxSaveBlock, "Unable to create saveblock");
	char szFileNameWithExtension[256];	

	if( NULL == pxSaveBlock)
	{
		return( false);
	}
	Asura_Chunk_SaveBlock_Stream	xStream (pxSaveBlock);

	// DLH - 17 November 2006 17:06:46 - The chunk version is stored later. Do not change this one.
	xStream.WriteChunk_Start	(ASURA_CHUNK_BOTANICALS_DEFINITION, 1, 0);

	// The definition chunk contains the name of the shape (with relative path), followed by
	// a number of element chunks, followed by a number of LOD chunks.

	// Strip the path (was saving absolute paths before, causing problems)
	Asura_String xTemp = Asura_FileAccess::PlatformRemoveNetworkOrExecutableDir(xFileName.GetString());
	xStream << xTemp;	// This is the name of the definition that we'll be matching it with

	short sTemp = static_cast<short>( pxToolsShape->GetNumElements());
	xStream << sTemp;

	xStream << g_sCurrentChunkVersion;

	const u_int uSectionCount = pxToolsShape->m_xHashTree_xSections.GetNumberOfNodes();
	for( u_int uSectionIndex = 0; uSectionIndex < uSectionCount; uSectionIndex += 1)
	{
		Tools_BotanicalSection* pxToolSection = pxToolsShape->m_xHashTree_xSections.GetDataByIndex( uSectionIndex);
		
		const u_int uVersionCount = pxToolSection->m_xHashTree_xVersions.GetNumberOfNodes();
		for( u_int uVersionIndex = 0; uVersionIndex < uVersionCount; uVersionIndex += 1)
		{
			Tools_BotanicalVersion* pxToolVersion = pxToolSection->m_xHashTree_xVersions.GetDataByIndex( uVersionIndex);
			for(Asura_ListElt<Tools_BotanicalElement*>* pxElt = pxToolVersion->m_xList_pxElements.Front(); pxElt;pxElt=pxElt->Next())
			{
				Tools_BotanicalElement *pxElement=pxElt->Data();
				
				// DLH - 27 November 2006 11:24:32 - Version 4
				xStream << pxToolSection->m_xName;
				xStream << pxToolVersion->m_xName;
				xStream << pxElement->m_xName;

				// DLH - 27 November 2006 11:25:12 - Version 0
				xStream << ((int)pxElement->m_eType);

				// Output any type-specific data now. We'll need this for the imposters
				if (pxElement->m_eType==TOOLS_BOTANICALS_ELEMENTTYPE_IMPOSTER)
				{
					// Output the imposter settings
					Tools_BotanicalElementLOD *pxLOD=pxElement->GetLODByIndex(0);

					int iCount=pxLOD->GetNumImposters();

					xStream<<iCount;

					for(int j=0;j<iCount;j++)
					{
						Tools_Botanicals_Imposter *pxImposter=pxLOD->GetImposterByIndex(j);
						xStream<<pxImposter->m_fAngleRange;
						xStream<<pxImposter->m_fFadeRange;
					}							
				}


				// Now output the LOD settings
				xStream << pxElement->GetNumLODs();			

				for(int j=0;j<pxElement->GetNumLODs();j++)
				{
					Tools_BotanicalElementLOD *pxLOD = pxElement->GetLODByIndex(j);

					// DLH - 27 November 2006 11:24:46 - Version 4
					xStream << pxLOD->m_xName;
					
					// DLH - 27 November 2006 11:24:52 - Version 0
					xStream << ((int)(pxLOD->m_bLinkToNext));
					xStream << ((int)(pxLOD->m_bLinkToPrev));
					xStream << pxLOD->m_fLod0;
					xStream << pxLOD->m_fLod1;
					xStream << pxLOD->m_fFadeLength0;
					xStream << pxLOD->m_fFadeLength1;
				}
			}
		}
	}

	xStream << pxToolsShape->m_fInvMaxDrawDistance;
	xStream << static_cast< float>( pxToolsShape->m_fMaxDrawDistanceFOV);
	xStream << static_cast< uint32>( pxToolsShape->m_uMaxDrawDistanceRes);
	
	// DLH - 17 November 2006 16:49:58 - Save Interactive information
#if defined( ASURA_BOTANICAL_SUPPORT_INTERACTIVE)
	// DLH - 17 November 2006 16:52:30 - Save the fact there is Interactive Data to load
	// DLH - 17 November 2006 16:53:10 - This is not the same as the active flag. If it was you would look the interactive data on save if inactive
	xStream << static_cast< bool>( true);
	
	{
		xStream << static_cast< bool>( pxToolsShape->m_bIsInteractive);
		xStream << static_cast< float>( pxToolsShape->m_xInteractiveBone0.GetX());
		xStream << static_cast< float>( pxToolsShape->m_xInteractiveBone0.GetY());
		xStream << static_cast< float>( pxToolsShape->m_xInteractiveBone0.GetZ());
		
		xStream << static_cast< float>( pxToolsShape->m_xInteractiveBone1.GetX());
		xStream << static_cast< float>( pxToolsShape->m_xInteractiveBone1.GetY());
		xStream << static_cast< float>( pxToolsShape->m_xInteractiveBone1.GetZ());
		
		xStream << static_cast< float>( pxToolsShape->m_fMass);
		xStream << static_cast< float>( pxToolsShape->m_fDrag);
		xStream << static_cast< float>( pxToolsShape->m_fSpringStrength);
		xStream << static_cast< float>( pxToolsShape->m_fSpringLength);
		xStream << static_cast< float>( pxToolsShape->m_fBoneWeightGamma);
		xStream << static_cast< bool>( pxToolsShape->m_bUsePaintedColoursAsWeights);
	}
#else
	xStream << static_cast< bool>( false);
#endif

#if defined( ASURA_BOTANICAL_SUPPORT_STATES) && 1 == ASURA_BOTANICAL_SUPPORT_STATES
	{
		{
			uint32 uStateCount = pxToolsShape->m_xList_xStateTables1D.Size();
			xStream << uStateCount;
		}
		
		for( const Asura_ListElt< Tools_Botanical_StateTable1D>* pxItStateTable = pxToolsShape->m_xList_xStateTables1D.Front(); NULL != pxItStateTable; pxItStateTable = pxItStateTable->Next())
		{
			const Tools_Botanical_StateTable1D& xStateTable = pxItStateTable->Data();
			
			xStream << xStateTable.m_xSourceState;
			
			{
				uint32 uStateSectionCount = xStateTable.m_xList_xSection.Size();
				xStream << uStateSectionCount;
			}
			
			for( Asura_ListElt< Tools_Botanical_StateTable1D_Section>* pxItStateTableSection = xStateTable.m_xList_xSection.Front(); NULL != pxItStateTableSection; pxItStateTableSection = pxItStateTableSection->Next())
			{
				Tools_Botanical_StateTable1D_Section& xStateTableSection = pxItStateTableSection->Data();
				
				xStream << xStateTableSection.m_xTargetSection;
				
				{
					uint32 uStateSectionVersionCount = xStateTableSection.m_xList_xVersionRanges.Size();
					xStream << uStateSectionVersionCount;
				}
				
				for( Asura_ListElt< Tools_Botanical_StateTable1D_Section_VersionRange>* pxItStateTableSectionVersion = xStateTableSection.m_xList_xVersionRanges.Front(); NULL != pxItStateTableSectionVersion; pxItStateTableSectionVersion = pxItStateTableSectionVersion->Next())
				{
					Tools_Botanical_StateTable1D_Section_VersionRange& xStateTableSectionVersion = pxItStateTableSectionVersion->Data();
					
					xStream << xStateTableSectionVersion.m_xTargetVersion;
					xStream << static_cast< float>( xStateTableSectionVersion.m_fRangeStart);
					xStream << static_cast< float>( xStateTableSectionVersion.m_fRangeEnd);
					xStream << xStateTableSectionVersion.m_uProjectFlags;
					xStream << xStateTableSectionVersion.m_xCollisionMeshName;		
					xStream << xStateTableSectionVersion.m_xCollisionMeshHash;		
					xStream << xStateTableSectionVersion.m_fCollisionMeshDensity;
					xStream << xStateTableSectionVersion.m_bCollisionUseBoundingBox;
				}
			}
		}
	}
#else
	xStream << static_cast< uint32>( 0);
#endif

	xStream.WriteChunk_End();
	xStream.WriteAsuraTerminator();

	sprintf(szFileNameWithExtension,"%s.btd",szFileNameWithNoExtension);
	bool bSuccess=pxSaveBlock->SaveFile(szFileNameWithExtension, false );

	if (!bSuccess)
	{	
		Tools_Timed_Message_System::AddMessage(0xff0000, "Failed to save '%s' - File is probably read-only?",szFileNameWithExtension);
	}

	delete pxSaveBlock;
	if (false == bSuccess)
	{
		return false;
	}
	
	return true;
}
#endif // ASURA_TOOLS



bool Asura_Chunk_Botanicals_Definition::Process(Asura_Handle iFileHandle) const
{
#ifdef ASURA_TOOLS

	Asura_Chunk_File_Stream	xStream(iFileHandle);
	Asura_String xName;

	// Read in the shape name
	xStream >> xName;

	// Fix up the name if we've got an old file (with an absolute path)
	Asura_String xTemp = Asura_FileAccess::PlatformRemoveNetworkOrExecutableDir(xName.GetString());
	xName=xTemp; 

	// We can only read this chunk in if the matching BotanicalShape has already been created
	Asura_BotanicalShape *pxShape=Asura_Botanicals_ShapeManager::GetShapeByHash(Asura_GetHashID(xName.GetString()));	
	if (pxShape==NULL) return false;
	
	Tools_BotanicalShape *pxToolsShape=pxShape->GetToolsShape();
	if (pxToolsShape==NULL) return false;

//CSB 25/04/06	Replacing the int(iNumberOfElements) with short(iNumberOfElements) and short(Version)
//				This allows the addition of new data at the end of the chunk but also means that old data files will be loaded correctly.
//				Old data files will give a version number of 0.
	// Read number of elements (LOD chains)

//DeadCode CSB 25/04/2006 	int iNumberOfElements=0;
//DeadCode CSB 25/04/2006 	xStream >> iNumberOfElements;

	short sTemp;
	xStream >> sTemp;
	int iNumberOfElements = sTemp;

	short sVersionNumber;
	xStream >> sVersionNumber;

	if(sVersionNumber > g_sCurrentChunkVersion)
	{
		Asura_Assertf(false, ("Unexpected chunk version (%d) encountered for botanical definitions.", sVersionNumber));
		return(false);
	}

	for(int i=0;i<iNumberOfElements;i++)
	{
		Tools_BotanicalSection *pxSection = NULL;
		Tools_BotanicalVersion *pxVersion = NULL;
		Tools_BotanicalElement *pxElement = NULL;

		Asura_String xSectionName;
		Asura_String xVersionName;
		Asura_String xElementName;
			
		if( 4 <= sVersionNumber)
		{
			xStream >> xSectionName;
			xStream >> xVersionName;
			xStream >> xElementName;
			
			pxSection = pxToolsShape->m_xHashTree_xSections.GetDataByName( xSectionName.GetString());
			if( NULL != pxSection)
			{
				pxVersion = pxSection->m_xHashTree_xVersions.GetDataByName( xVersionName.GetString());
				if( NULL != pxVersion)
				{
					pxElement = pxVersion->FindElement( xElementName.GetString());
				}
			}
			
		}
		else
		{
			// Does this element exist? (the shape may have been changed, and the two might not match!)
			if (i<=pxToolsShape->GetNumElements())
			{
				pxElement=pxToolsShape->GetElementByIndex(i);
			}
		}		

		// Read the element parameters
		int iType;
		xStream >> iType;
		TOOLS_BOTANICALS_ELEMENTTYPE eType=(TOOLS_BOTANICALS_ELEMENTTYPE)iType;

		// Store them in the element if we have one
		if(pxElement)
		{
			pxElement->m_eType = eType;
		}


		// Read any type-specific data now. We'll need this for the imposters
		if (eType==TOOLS_BOTANICALS_ELEMENTTYPE_IMPOSTER)
		{
			// Output the imposter settings			
			int iCount;
			xStream>>iCount;

			for(int j=0;j<iCount;j++)
			{
				float fAngleRange;
				float fFadeRange;
				
				xStream >> fAngleRange;
				xStream >> fFadeRange;

				if (pxElement)
				{
					Tools_BotanicalElementLOD *pxLOD=pxElement->GetLODByIndex(0);
					if (j<=pxLOD->GetNumImposters())
					{
						Tools_Botanicals_Imposter *pxImposter=pxLOD->GetImposterByIndex(j);
						if(pxImposter)	//The artwork may have been changes since the btd file was saved
						{
							pxImposter->m_fAngleRange=fAngleRange;
							pxImposter->m_fFadeRange=fFadeRange;
						}
					}
				}
			}							
		}

		// Read the LODs
		int iNumberOfLODs;
		xStream >> iNumberOfLODs;

		for(int j=0;j<iNumberOfLODs;j++)
		{
			Tools_BotanicalElementLOD *pxLOD=NULL;

			int   iLinkToNext,iLinkToPrev;  // We use ints in the chunk where bools are used...
			float fLod0,fLod1,fFadeLength0,fFadeLength1;

			Asura_String xLODName;
				
			if( 4 <= sVersionNumber)
			{
				xStream >> xLODName;
			}
			
			xStream >> iLinkToNext;
			xStream >> iLinkToPrev;
			xStream >> fLod0;
			xStream >> fLod1;
			xStream >> fFadeLength0;
			xStream >> fFadeLength1;

			if( 4 <= sVersionNumber)
			{
				if( NULL != pxElement)
				{
					pxLOD = pxElement->GetLODByName( xLODName.GetString());
				}
			}
			else
			{
				// Does this element exist? (the shape may have been changed, and the two might not match!)
				if((pxElement) && (j<=pxElement->GetNumLODs()))
				{
					pxLOD=pxElement->GetLODByIndex(j);
				}		
			}

			if(pxLOD)
			{
				pxLOD->m_bLinkToNext = iLinkToNext?true:false;
				pxLOD->m_bLinkToPrev = iLinkToPrev?true:false;
				pxLOD->m_fLod0		 = fLod0;
				pxLOD->m_fLod1		 = fLod1;
				pxLOD->m_fFadeLength0= fFadeLength0;
				pxLOD->m_fFadeLength1= fFadeLength1;
			}
		}
	}

	if(sVersionNumber == 0)
	{
		// JAT 11/07/06 default far clip has been removed so hard code with 100.0f 
		// Doubt there are any Version 0 files out there anymore and if there are 
		// then they would expect defaultFarClip to be 100.0f
		pxToolsShape->m_fInvMaxDrawDistance = 1.0f / 100.0f; 
	}
	else
	{
		float fInvMaxDrawDistance;
		xStream >> fInvMaxDrawDistance;

		if(fInvMaxDrawDistance < 0.001f)
		{
			Asura_Assert(0, "Loaded Invalid value for Inverse Maximum Draw Distance");
			fInvMaxDrawDistance = 0.001f;
		}
		if(fInvMaxDrawDistance > 0.1f)
		{
			Asura_Assert(0, "Loaded Invalid value for Inverse Maximum Draw Distance");
			fInvMaxDrawDistance = 0.1f;
		}
		pxToolsShape->m_fInvMaxDrawDistance = fInvMaxDrawDistance;
	}
//CSB 25/04/06	^^^^^

	if( 1 < sVersionNumber)
	{
		float fMaxDrawDistanceFOV;
		uint32 uMaxDrawDistanceRes;
		
		xStream >> fMaxDrawDistanceFOV;
		xStream >> uMaxDrawDistanceRes;
		
		pxToolsShape->m_fMaxDrawDistanceFOV = fMaxDrawDistanceFOV;
		pxToolsShape->m_uMaxDrawDistanceRes = uMaxDrawDistanceRes;
	}
	else
	{
		pxToolsShape->m_fMaxDrawDistanceFOV = Asura_Camera::GetHorizontalFieldOfView();
		pxToolsShape->m_uMaxDrawDistanceRes = Asura_Viewport::GetWidth();
	}
	
	if( 2 < sVersionNumber)
	{
		// DLH - 17 November 2006 16:52:30 - Load the fact there is Interactive Data to load
		// DLH - 17 November 2006 16:53:10 - This is not the same as the active flag. If it was you would look the interactive data on save if inactive
		bool bInteractiveDataToLoad;
		xStream >>bInteractiveDataToLoad;
		
#if defined( ASURA_BOTANICAL_SUPPORT_INTERACTIVE)
		if( bInteractiveDataToLoad)
		{
			xStream >> pxToolsShape->m_bIsInteractive;
			
			float fLoadTemp = 0;
			xStream >> fLoadTemp;
			pxToolsShape->m_xInteractiveBone0.SetX( fLoadTemp);
			xStream >>  fLoadTemp;
			pxToolsShape->m_xInteractiveBone0.SetY( fLoadTemp);
			xStream >>  fLoadTemp;
			pxToolsShape->m_xInteractiveBone0.SetZ( fLoadTemp);
			
			xStream >>  fLoadTemp;
			pxToolsShape->m_xInteractiveBone1.SetX( fLoadTemp);
			xStream >>  fLoadTemp;
			pxToolsShape->m_xInteractiveBone1.SetY( fLoadTemp);
			xStream >>  fLoadTemp;
			pxToolsShape->m_xInteractiveBone1.SetZ( fLoadTemp);
			
			xStream >> pxToolsShape->m_fMass;
			xStream >> pxToolsShape->m_fDrag;
			xStream >> pxToolsShape->m_fSpringStrength;
			xStream >> pxToolsShape->m_fSpringLength;
			xStream >> pxToolsShape->m_fBoneWeightGamma;
			if( 6 < sVersionNumber)
			{
				xStream >> pxToolsShape->m_bUsePaintedColoursAsWeights;
			}
		}
#else
		Asura_Assert( false == bInteractiveDataToLoad, "This application is not compiled with ASURA_BOTANICAL_SUPPORT_INTERACTIVE but it is trying to load botanicals with interactive data.");
		if( bInteractiveDataToLoad)
		{
			bool bLoadTemp = 0;
			xStream >> bLoadTemp;
			
			float fLoadTemp = 0;
			xStream >> fLoadTemp;
			xStream >>  fLoadTemp;
			xStream >>  fLoadTemp;
			
			xStream >>  fLoadTemp;
			xStream >>  fLoadTemp;
			xStream >>  fLoadTemp;
			
			xStream >> fLoadTemp;
			xStream >> fLoadTemp;
			xStream >> fLoadTemp;
			xStream >> fLoadTemp;
			xStream >> fLoadTemp;
		}
#endif
	}
	else
	{
#if defined( ASURA_BOTANICAL_SUPPORT_INTERACTIVE)
		pxToolsShape->m_bIsInteractive = false;
#endif
	}
	
	if( 5 <= sVersionNumber)
	{
		uint32 uStateCount;
		xStream >> uStateCount;

#if defined( ASURA_BOTANICAL_SUPPORT_STATES) && 1 == ASURA_BOTANICAL_SUPPORT_STATES
		// DLH - 12 December 2006 12:15:41 - Load the fact there is State Data to load
		for( uint32 uStateIndex = 0; uStateIndex < uStateCount; uStateIndex += 1)
		{
			Asura_ListElt< Tools_Botanical_StateTable1D>* pxItStateTable = pxToolsShape->m_xList_xStateTables1D.Push_Back( Tools_Botanical_StateTable1D());
			Tools_Botanical_StateTable1D& xStateTable = pxItStateTable->Data();
			
			xStream >> xStateTable.m_xSourceState;
			
			uint32 uStateSectionCount;
			xStream >> uStateSectionCount;
			
			for( uint32 uStateSectionIndex = 0; uStateSectionIndex < uStateSectionCount; uStateSectionIndex += 1)
			{
				Asura_ListElt< Tools_Botanical_StateTable1D_Section>* pxItStateTableSection = xStateTable.m_xList_xSection.Push_Back( Tools_Botanical_StateTable1D_Section());
				Tools_Botanical_StateTable1D_Section& xStateTableSection = pxItStateTableSection->Data();
				
				xStream >> xStateTableSection.m_xTargetSection;
				
				uint32 uStateSectionVersionCount;
				xStream >> uStateSectionVersionCount;
				
				for( uint32 uStateSectionVersionIndex = 0; uStateSectionVersionIndex < uStateSectionVersionCount; uStateSectionVersionIndex += 1)
				{
					Asura_ListElt< Tools_Botanical_StateTable1D_Section_VersionRange>* pxItStateTableSectionVersion = xStateTableSection.m_xList_xVersionRanges.Push_Back( Tools_Botanical_StateTable1D_Section_VersionRange());
					Tools_Botanical_StateTable1D_Section_VersionRange& xStateTableSectionVersion = pxItStateTableSectionVersion->Data();
					
					xStream >> xStateTableSectionVersion.m_xTargetVersion;
					xStream >> xStateTableSectionVersion.m_fRangeStart;
					xStream >> xStateTableSectionVersion.m_fRangeEnd;

					if(sVersionNumber >= 6)
					{
						xStream >> xStateTableSectionVersion.m_uProjectFlags;
					}
					else
					{
						xStateTableSectionVersion.m_uProjectFlags	= 0;
					}

					if(sVersionNumber >= 8)
					{
						xStream >> xStateTableSectionVersion.m_xCollisionMeshName;		
						xStream >> xStateTableSectionVersion.m_xCollisionMeshHash;		
						xStream >> xStateTableSectionVersion.m_fCollisionMeshDensity;

						if (xStateTableSectionVersion.m_xCollisionMeshHash!=ASURA_HASH_ID_UNSET)
						{
							Asura_Hierarchy_Skin *pxSkin = Asura_Animation_System::GetSkinByHash(xStateTableSectionVersion.m_xCollisionMeshHash);

							if (!pxSkin)
							{							
								LoadAsuraFile(xStateTableSectionVersion.m_xCollisionMeshName.GetString());
							}													
						}						
					}

					if (sVersionNumber >= 9)
					{					
						xStream >> xStateTableSectionVersion.m_bCollisionUseBoundingBox;
					}
				}
			}
		}
		
#else
		Asura_Assert( 0 == uStateCount, "State data is present in this file. But State data is not supported.");
#endif
	}
	

	return true;
#else
	Asura_Assert(false,"Shouldn't be attempting to load a .btd chunk file in retail builds! - that's tools-only");
	return false;
#endif
}

#endif	// ASURA_USE_BOTANICALS
