//
// Asura_Chunk_StreamingBackgroundSound.cpp
//
// Created by Richard Rice - 13/01/04
// 
// Copyright (C) 2004 Rebellion, All Rights Reserved.
//

#include "Core/Asura.h"
#include "ChunkSystem/Asura_Chunk_StreamingBackgroundSound.h"

#include "ChunkSystem/Asura_SaveBlock.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Asura_AABB_Tree.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Debugging/Asura_Assert.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Network/Asura_Guid.h"
#include "Sound/Asura_StreamingBackgroundSound_System.h"
#include "Volume/Asura_VolumeGroup.h"

#ifdef ASURA_TOOLS
#include "Lighting/Tools_LightSets.h"
#endif

#define ASURA_CHUNK_STREAMINGBACKGROUNDSOUND_VERSION 11
//Version 1 :
//			Removed the stereo flags
//			RAR 10/02/2004
//Version 2 :
//			Added orientation for volumes
//			RAR 22/02/2005
//Version 3 :
//			Flags added (in place of old junk value)
//			RAR 18/07/2005
//Version 4 :
//			Mixer IDs added
//			RAR 22/01/2007
//Version 5 :
//			Light set IDs added
//			RAR 30/01/2007
//Version 6 :
//			Flags , and IDs and Guids added for linking to static messages
//			RAR 03/08/2007
//Version 7 :
//			Option for spherical sound volumes
//			RAR 06/08/2007
//Version 8 :
//			Change to light sets to allow multiple groups of light sets
//			RAR 06/03/2008
//Version 9 :
//			Added priority and clip volume
//			RAR 15/04/2008
//Version 10 :
//			Template guid and version
//			RAR 15/04/2008
//Version 11 :
//			Cylindrical volumes
//			RAR 15/04/2008


//NOTE!
//Changes to this will most likely require equivalent changes to ImportFromChunkBuffer();
bool Asura_Chunk_StreamingBackgroundSound::Process(Asura_Chunk_Stream& xStream) const
{
	Asura_Assert(Version<=ASURA_CHUNK_STREAMINGBACKGROUNDSOUND_VERSION,"Unknown chunk version");

	if(Asura_StreamingBackgroundSound_System::s_iNumberOfSounds)
	{
		Asura_Assert(0,"Background sounds already loaded");
		return false;
	}

	if(Version >= 6)
	{
		xStream >> Asura_StreamingBackgroundSound_System::s_uNextID;
	}



	//Determine the number of default sounds to load. Old chunks always have only one.
	int iNumDefaultSounds = 1;
	if(Version >= 5)
	{
		xStream >> iNumDefaultSounds;

		#ifndef ASURA_TOOLS
		if(iNumDefaultSounds != 1)
		{
			Asura_Assert(0,"Should only be getting one default streaming background sound in exports for retail builds");
			return false;
		}
		#endif
	}


	for(int i=0;i<iNumDefaultSounds;i++)
	{
		u_int uLightSets;
		if(Version>=8)
		{
			xStream >> uLightSets;
		}
		else
		{
			uLightSets = 1<<i;
		}

		#ifdef ASURA_TOOLS
		Asura_DefaultStreamingBackgroundSound* pxDefaultSound = Asura_StreamingBackgroundSound_System::GetDefaultSound(uLightSets);
		#else
		Asura_DefaultStreamingBackgroundSound* pxDefaultSound = &Asura_StreamingBackgroundSound_System::s_xDefaultSound;
		#endif
		
		xStream >> pxDefaultSound->m_fVolume;		
		
		char acFilename[256];
		xStream.ReadString(acFilename, sizeof(acFilename));
		if(acFilename[0])
		{
			pxDefaultSound->m_xFilename = acFilename;
			pxDefaultSound->m_uFilenameHash = Asura_GetHashID(acFilename);
		}
		if(Version >= 4)
		{
			xStream >> pxDefaultSound->m_uMixer;		
		}
	}

	if(Version<3)
	{
		Asura_StreamingBackgroundSound_System::s_uFlags = 0;
	}
	else
	{
		Asura_StreamingBackgroundSound_System::s_uFlags = m_uSBSFlags;
	}
	Asura_StreamingBackgroundSound_System::s_iNumberOfSounds = m_iNumberOfSounds;


	if(m_iNumberOfSounds)
	{
		//allocate space for sounds
		Asura_StreamingBackgroundSound* pxSound;
		Asura_ResourceAllocateArray(pxSound, m_iNumberOfSounds);
		Asura_StreamingBackgroundSound_System::s_pxSounds = pxSound;

		//and read them in
		for(int i=0;i<m_iNumberOfSounds;i++,pxSound++)
		{
			pxSound->Initialise();
			
			if(Version==0)
			{
				//skip the stereo setting
				xStream.Skip(4);
			}
			xStream >> pxSound->m_fVolume;
			xStream >> pxSound->m_xInnerBox;
			xStream >> pxSound->m_xOuterBox;
			if(Version<2)
			{
				pxSound->m_xOrient.Identity();
			}
			else
			{
				xStream >> pxSound->m_xOrient;
			}

			if(Version<4)
			{
				pxSound->m_uMixer = 0;
			}
			else
			{
				xStream >> pxSound->m_uMixer;
			}

			#ifdef ASURA_TOOLS
			if(Version<5)
			{
				pxSound->m_uLightSets = 0xffffffff;
			}
			else
			{
				xStream >> pxSound->m_uLightSets;
			}
			#else
			if(Version>=5)
			{
				xStream.Skip(4);
			}
			#endif

			char acFilename[256];
			xStream.ReadString(acFilename, sizeof(acFilename));

			if(acFilename[0])
			{
				pxSound->m_xFilename = acFilename;
				pxSound->m_uFilenameHash = Asura_GetHashID(acFilename);
			}
			else
			{
				pxSound->m_uFilenameHash = ASURA_HASH_ID_UNSET;
			}

			if(Version<6)
			{
				pxSound->m_uFlags = ASURA_STREAMINGBACKGROUNDSOUNDFLAG_ACTIVE;
				pxSound->m_uID = Asura_StreamingBackgroundSound_System::GenerateID();
				pxSound->m_uGuid = ASURA_GUID_UNREGISTERED;
			}
			else
			{
				xStream >> pxSound->m_uFlags;
				xStream >> pxSound->m_uID;
				xStream >> pxSound->m_uGuid;
			}

			if(Version<7)
			{
				pxSound->m_fInnerRadius = 5.0f;
				pxSound->m_fOuterRadius = 20.0f;
				pxSound->m_xPosition = pxSound->m_xInnerBox.CalculateCentre();
			}
			else
			{
				xStream >> pxSound->m_fInnerRadius;
				xStream >> pxSound->m_fOuterRadius;
				xStream >> pxSound->m_xPosition;
			}
			if(Version<9)
			{
				pxSound->m_iPriority = 0;
			}
			else
			{
				xStream >> pxSound->m_iPriority;
			}
			if(Version<10)
			{
				#ifdef ASURA_TOOLS
				pxSound->m_uTemplateGuid = ASURA_GUID_UNREGISTERED;
				pxSound->m_iTemplateVersion = 0;
				#endif
			}
			else
			{
				#ifdef ASURA_TOOLS
				xStream >> pxSound->m_uTemplateGuid;
				xStream >> pxSound->m_iTemplateVersion;
				#else
				xStream.Skip(8);
				#endif

			}
			if(Version<11)
			{
				pxSound->m_iType = (pxSound->m_uFlags & ASURA_OLDSTREAMINGBACKGROUNDSOUNDFLAG_SPHERE) ? ASURA_STREAMINGBACKGROUNDSOUNDTYPE_SPHERE : ASURA_STREAMINGBACKGROUNDSOUNDTYPE_CUBOID;
				pxSound->m_uFlags &=~ ASURA_OLDSTREAMINGBACKGROUNDSOUNDFLAG_SPHERE;

				pxSound->m_fInnerHeight = 5.0f;
				pxSound->m_fOuterHeight = 5.0f;
				
			}
			else
			{
				xStream >> pxSound->m_iType;
				xStream >> pxSound->m_fInnerHeight;
				xStream >> pxSound->m_fOuterHeight;
			}

			if(pxSound->m_uFlags & ASURA_STREAMINGBACKGROUNDSOUNDFLAG_USECLIPVOLUME)
			{
				pxSound->m_pxClipVolume = new Asura_VolumeGroup;
				pxSound->m_pxClipVolume->ReadFromChunkStream(xStream);

			}
		}
		
		Asura_ResourcePushType(ASURA_RESOURCE_AABB_TREE);
		{
			//set up the volume tree
			Asura_AABB_Tree* pxTree = new Asura_AABB_Tree;
			Asura_StreamingBackgroundSound_System::s_pxVolumeTree = pxTree;
			if(m_iNumberOfSounds>1)
			{
				Asura_ResourceAllocateArray(pxTree->Nodes, m_iNumberOfSounds-1);
			}
			else
			{
				pxTree->Nodes = 0;
			}

			xStream >> pxTree->RootBoundingBox;

			Asura_AABB_Tree_Node* pxNode = pxTree->Nodes;
			for(int i=0;i<m_iNumberOfSounds-1;i++,pxNode++)
			{
				pxNode->ReadFromChunkStream(xStream);
			}
		}
		Asura_ResourcePopType();
	}

	return true;
}

bool Asura_Chunk_StreamingBackgroundSound::Process(Asura_Handle iFileHandle) const
{
	Asura_Chunk_File_Stream	xStream(iFileHandle);
	return Process(xStream);
}

#ifdef ASURA_TOOLS
//NOTE!
//Changes to this will most likely require equivalent changes to Process();
int Asura_Chunk_StreamingBackgroundSound::ImportFromChunkBuffer(const char* pcChunkBuffer, u_int uSavedID, u_int uNewID) const
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);

	//Streaming sounds couldn't exist in templates before version 10 ,so can earlier chunks
	if(Version > ASURA_CHUNK_STREAMINGBACKGROUNDSOUND_VERSION || Version < 10)
	{
		Asura_Assert(Version<=ASURA_CHUNK_STREAMINGBACKGROUNDSOUND_VERSION,"Unknown chunk version");
		return -1;
	}

	if(!m_iNumberOfSounds) return -1;

	//Need to skip some global settings to get to the sounds

	xStream.Skip(4); //Asura_StreamingBackgroundSound_System::s_uNextID;



	//Determine the number of default sounds to load. Old chunks always have only one.
	int iNumDefaultSounds = 1;
	xStream >> iNumDefaultSounds;


	for(int i=0;i<iNumDefaultSounds;i++)
	{
		xStream.Skip(8);// uLightSets and pxDefaultSound->m_fVolume;		
		
		xStream.SkipString();//filename
		xStream.Skip(4);//pxDefaultSound->m_uMixer;		
	}

	if(m_iNumberOfSounds)
	{
		//check each sound
		for(int i=0;i<m_iNumberOfSounds;i++)
		{
			Asura_StreamingBackgroundSound xTempSound;
			Asura_StreamingBackgroundSound* pxSound = &xTempSound;

			pxSound->Initialise();
			
			xStream >> pxSound->m_fVolume;
			xStream >> pxSound->m_xInnerBox;
			xStream >> pxSound->m_xOuterBox;
			xStream >> pxSound->m_xOrient;
			xStream >> pxSound->m_uMixer;

			pxSound->m_uLightSets = 0xffffffff;
			xStream >> pxSound->m_uLightSets;

			char acFilename[256];
			xStream.ReadString(acFilename, sizeof(acFilename));

			if(acFilename[0])
			{
				pxSound->m_xFilename = acFilename;
				pxSound->m_uFilenameHash = Asura_GetHashID(acFilename);
			}
			else
			{
				pxSound->m_uFilenameHash = ASURA_HASH_ID_UNSET;
			}

			xStream >> pxSound->m_uFlags;
			xStream >> pxSound->m_uID;
			xStream >> pxSound->m_uGuid;

			xStream >> pxSound->m_fInnerRadius;
			xStream >> pxSound->m_fOuterRadius;
			xStream >> pxSound->m_xPosition;

			xStream >> pxSound->m_iPriority;

			xStream >> pxSound->m_uTemplateGuid;
			xStream >> pxSound->m_iTemplateVersion;

			if(Version<11)
			{
				pxSound->m_iType = (pxSound->m_uFlags & ASURA_OLDSTREAMINGBACKGROUNDSOUNDFLAG_SPHERE) ? ASURA_STREAMINGBACKGROUNDSOUNDTYPE_SPHERE : ASURA_STREAMINGBACKGROUNDSOUNDTYPE_CUBOID;
				pxSound->m_uFlags &=~ ASURA_OLDSTREAMINGBACKGROUNDSOUNDFLAG_SPHERE;

				pxSound->m_fInnerHeight = 5.0f;
				pxSound->m_fOuterHeight = 5.0f;
				
			}
			else
			{
				xStream >> pxSound->m_iType;
				xStream >> pxSound->m_fInnerHeight;
				xStream >> pxSound->m_fOuterHeight;
			}

			if(pxSound->m_uFlags & ASURA_STREAMINGBACKGROUNDSOUNDFLAG_USECLIPVOLUME)
			{
				pxSound->m_pxClipVolume = new Asura_VolumeGroup;
				pxSound->m_pxClipVolume->ReadFromChunkStream(xStream);
			}
			
			//finished reading in the sound... is it the one we want?
			if(pxSound->m_uID == uSavedID)
			{
				//create the new sound
				int iIndex = Asura_StreamingBackgroundSound_System::CreateSound(pxSound->m_xPosition, uNewID);
				Asura_Assert(iIndex>=0, "");

				//and overwrite with the settings from the temporary sound
				Asura_StreamingBackgroundSound* pxNewSound = Asura_StreamingBackgroundSound_System::GetSound(iIndex);
				xTempSound.m_uID = pxNewSound->m_uID;//copy back the ID first in case uNewID was 0
				*pxNewSound = xTempSound;

				return iIndex;

			}
			else
			{
				//don't want this sound , so get rid of anything allocated for it
				xTempSound.Shutdown();
			}
		}
	}
	//Anything that appears after the list of sounds is of no interest anyway ,so skip checking anything else

	return -1;
}
#endif //ASURA_TOOLS


#ifdef ASURA_TOOLS
void Asura_Chunk_StreamingBackgroundSound::Create(Asura_SaveBlock* pxSaveBlock)
{
	Asura_Chunk_SaveBlock_Stream xStream(pxSaveBlock);
	Create(xStream);
}


void Asura_Chunk_StreamingBackgroundSound::Create(Asura_Chunk_Stream& xStream)
{

	int iNumberOfSounds;
	if(Tools_Level_Export::IsExport())
	{
		//During exports , we just want the sounds for the current light set
		iNumberOfSounds = 0;
		Asura_StreamingBackgroundSound* pxSound = Asura_StreamingBackgroundSound_System::s_pxSounds;	

		for(int i=0;i<Asura_StreamingBackgroundSound_System::s_iNumberOfSounds;i++,pxSound++)
		{
			if(pxSound->IsMemberOfCurrentLightSet())
			{
				iNumberOfSounds++;
			}
		}
	}
	else
	{
		iNumberOfSounds = Asura_StreamingBackgroundSound_System::s_iNumberOfSounds;
	}

	//determine how many default sounds need to be saved (either one , or one for each possible light set)
	int iNumDefaultSoundsToSave = 1;
	if((Asura_StreamingBackgroundSound_System::s_uFlags & ASURA_STREAMINGSOUNDSYSTEMFLAG_DEFAULTPERLIGHTSET) && !Tools_Level_Export::IsExport())
	{
		iNumDefaultSoundsToSave = Tools_LightSets::GetNumUsedCombinations();
	}

	if(!iNumberOfSounds)
	{
		//If there are no sounds we may not need the chunk.
		//Check for any valud default sounds.
		bool bNeedChunk = false;
		if(iNumDefaultSoundsToSave == 1)
		{
			Asura_DefaultStreamingBackgroundSound* pxDefaultSound = Asura_StreamingBackgroundSound_System::GetDefaultSound();
			if(!pxDefaultSound->m_xFilename.IsBlank()) bNeedChunk = true;
		}
		else
		{
			for(Tools_UsedLightSetIterator xIt; !xIt.Done(); xIt.Next())
			{
				Asura_DefaultStreamingBackgroundSound* pxDefaultSound = Asura_StreamingBackgroundSound_System::GetDefaultSound(xIt.GetCurrent());
				if(!pxDefaultSound->m_xFilename.IsBlank())
				{
					bNeedChunk = true;
					break;
				}
			}
		}


		if(!bNeedChunk)
		{
			return;//no need for the chunk
		}
	}
	
	xStream.WriteChunk_Start(ASURA_CHUNK_STREAMINGBACKGROUNDSOUND, ASURA_CHUNK_STREAMINGBACKGROUNDSOUND_VERSION, 0);


	xStream << iNumberOfSounds;
	xStream << Asura_StreamingBackgroundSound_System::s_uFlags;
	xStream << Asura_StreamingBackgroundSound_System::s_uNextID;


	xStream << iNumDefaultSoundsToSave;
	

	if(iNumDefaultSoundsToSave == 1)
	{
		const Asura_DefaultStreamingBackgroundSound* pxDefaultSound = Asura_StreamingBackgroundSound_System::GetDefaultSound();

		xStream << 1u;
		xStream << pxDefaultSound->m_fVolume;
		xStream << pxDefaultSound->m_xFilename;
		xStream << pxDefaultSound->m_uMixer;
	}
	else
	{
		for(Tools_UsedLightSetIterator xIt; !xIt.Done(); xIt.Next())
		{
			u_int uLightSets = xIt.GetCurrent();
			const Asura_DefaultStreamingBackgroundSound* pxDefaultSound = Asura_StreamingBackgroundSound_System::GetDefaultSound(uLightSets);
			
			xStream << uLightSets;
			xStream << pxDefaultSound->m_fVolume;
			xStream << pxDefaultSound->m_xFilename;
			xStream << pxDefaultSound->m_uMixer;
		}
	}

	//RAR 08/03/2006 For recentreing during level exports :
	const Asura_Vector_3& xPositionOffset = Tools_Level_Export::GetExportCentre();

	//output the sound array
	Asura_StreamingBackgroundSound* pxSound = Asura_StreamingBackgroundSound_System::s_pxSounds;	
	for(int i=0;i<Asura_StreamingBackgroundSound_System::s_iNumberOfSounds;i++,pxSound++)
	{
		if(Tools_Level_Export::IsExport())
		{
			//Skip any sounds that aren't for the current light set
			if(!pxSound->IsMemberOfCurrentLightSet()) continue;
		}
		
		xStream << pxSound->m_fVolume;

		Asura_Bounding_Box xBB = pxSound->m_xInnerBox;
		xBB.Displace(-xPositionOffset);
		xStream << xBB;

		xBB = pxSound->m_xOuterBox;
		xBB.Displace(-xPositionOffset);
		xStream << xBB;

		xStream << pxSound->m_xOrient;

		xStream << pxSound->m_uMixer;
		xStream << pxSound->m_uLightSets;

		xStream << pxSound->m_xFilename;

		u_int uFlagsToSave = pxSound->m_uFlags;
		if(!pxSound->m_pxClipVolume) uFlagsToSave &=~ ASURA_STREAMINGBACKGROUNDSOUNDFLAG_USECLIPVOLUME;
		
		xStream << uFlagsToSave;
		xStream << pxSound->m_uID;
		xStream << pxSound->m_uGuid;

		xStream << pxSound->m_fInnerRadius;
		xStream << pxSound->m_fOuterRadius;
		xStream << (pxSound->m_xPosition - xPositionOffset);
		xStream << pxSound->m_iPriority;
		xStream << pxSound->m_uTemplateGuid;
		xStream << pxSound->m_iTemplateVersion;
		xStream << pxSound->m_iType;
		xStream << pxSound->m_fInnerHeight;
		xStream << pxSound->m_fOuterHeight;

		if(uFlagsToSave & ASURA_STREAMINGBACKGROUNDSOUNDFLAG_USECLIPVOLUME)
		{
			if(xPositionOffset != Asura_Maths::ZeroVector3)
			{
				//need to make a displaced copy of the volumes and save that instead
				Asura_VolumeGroup xDisplacedClipVolume;
				xDisplacedClipVolume.Copy(pxSound->m_pxClipVolume);
				xDisplacedClipVolume.DisplaceVolumes(-xPositionOffset);
				xDisplacedClipVolume.WriteToChunkStream(xStream);
			}
			else
			{
				pxSound->m_pxClipVolume->WriteToChunkStream(xStream);
			}
		}

	}
	if(iNumberOfSounds)
	{
		//output volume tree
		if(Tools_Level_Export::IsExport())
		{
			//Need to generate a new tree that just uses the exported lights
			Asura_AABB_Tree* pxTree = Asura_AABB_Tree_Creator::Create(iNumberOfSounds,Asura_StreamingBackgroundSound_System::FillVolumeTreeCallback);

			pxTree->RootBoundingBox.Displace(-xPositionOffset);
			Asura_AABB_Tree::SaveTree(1,pxTree,iNumberOfSounds,xStream);

			delete pxTree;
		}
		else
		{
			if(!Asura_StreamingBackgroundSound_System::s_pxVolumeTree)
			{
				Asura_StreamingBackgroundSound_System::UpdateVolumeTree();
			}

			Asura_AABB_Tree* pxTree = Asura_StreamingBackgroundSound_System::s_pxVolumeTree;
			Asura_Assert(pxTree,"");


			Asura_AABB_Tree::SaveTree(1,pxTree,iNumberOfSounds,xStream);
		}
	}
	

	xStream.WriteChunk_End();
}

#endif // ASURA_TOOLS


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/*---------------------------------------------**
** Asura_Chunk_StreamingBackgroundSound_InGame **
**---------------------------------------------*/

bool Asura_Chunk_StreamingBackgroundSound_InGame::Process(Asura_Chunk_Stream& xStream) const
{
	
	// Is this the correct version?

	if (Version > CHUNK_VERSION)
	{
		Asura_Assertf(false, ("Unknown chunk version %d for streaming background sound in-game state",Version));
		return false;
	}
	
	int iNumSavedSounds;
	xStream >> iNumSavedSounds;

	//make sure the number of saved sounds matches the number that exist
	if(iNumSavedSounds != Asura_StreamingBackgroundSound_System::s_iNumberOfSounds)
	{
		Asura_Assert(iNumSavedSounds == Asura_StreamingBackgroundSound_System::s_iNumberOfSounds,"Incorrect number of streaming background sounds in game state chunk");
		return false;
	}

	Asura_StreamingBackgroundSound* pxSound = Asura_StreamingBackgroundSound_System::s_pxSounds;
	for(int i=0; i < iNumSavedSounds; i++, pxSound++)
	{
		xStream >> pxSound->m_uFlags;
	}

	return true;
}

bool Asura_Chunk_StreamingBackgroundSound_InGame::Process(const char* pcChunkBuffer) const
{
	Asura_Chunk_ChunkBuffer_Stream xStream(pcChunkBuffer);
	return Process(xStream);
}

void Asura_Chunk_StreamingBackgroundSound_InGame::Create(Asura_Chunk_Stream& xStream)
{
	if(!Asura_StreamingBackgroundSound_System::s_iNumberOfSounds) return;
	
	u_int uChunkFlags = 0;
	xStream.WriteChunk_Start(ASURA_CHUNK_STREAMINGBACKGROUNDSOUND_INGAME, CHUNK_VERSION, uChunkFlags);

	int iNumberOfSounds = Asura_StreamingBackgroundSound_System::s_iNumberOfSounds;
	xStream << iNumberOfSounds;
	
	//save the state of the flags for each sound
	Asura_StreamingBackgroundSound* pxSound = Asura_StreamingBackgroundSound_System::s_pxSounds;
	for(int i=0; i<iNumberOfSounds; i++, pxSound++)
	{
		xStream << pxSound->m_uFlags;
	}
	xStream.WriteChunk_End();
}

void Asura_Chunk_StreamingBackgroundSound_InGame::Create(Asura_SaveBlock_Base* pxSaveBlock)
{
	Asura_Chunk_SaveBlock_Stream xStream(pxSaveBlock);
	Create(xStream);
}

