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

#include "Core/Asura.h"

#if !defined(ASURA_SUPPORT_DYNAMIC_MUSIC_SYSTEM)

#include "ChunkSystem/Asura_Chunk_MetaMusic.h"

#include "ChunkSystem/Asura_SaveBlock.h"
#include "ChunkSystem/Asura_Chunk_Macros.h"
#include "ChunkSystem/Asura_Chunk_Stream.h"
#include "Core/Memory/Asura_ResourceAllocator.h"
#include "Debugging/Asura_Assert.h"
#include "FileAccess/Asura_FileAccess.h"
#include "Sound/Asura_MetaMusic_System.h"
#ifdef ASURA_USE_METAMUSICZONES
#include "Zone/Asura_Zone_Controller.h"
#endif

#define ASURA_CHUNK_METAMUSIC_VERSION 6
//Added in Version 1 :
//				Volume settings per meta music piece and per file
//				RAR 05/02/2004
//Version 2:
//				Additional outro states added
//				RAR 15/10/2004
//Version 3:
//				Added some flags
//				RAR 08/11/2004
//Version 4:
//				Added beat times for tension/action
//				RAR 03/06/2004
//Version 5:
//				Added intro loop state
//				RAR 24/10/2004
//Version 6:
//				Meta music zones changed
//				RAR 11/01/2005


bool Asura_Chunk_MetaMusic::Process(Asura_Handle iFileHandle) const
{
	Asura_Chunk_File_Stream xStream(iFileHandle);

	Asura_Assert(Version<=ASURA_CHUNK_METAMUSIC_VERSION,"Unknown chunk version");

	if(Asura_MetaMusic_System::GetNumberOfTracks())
	{
		Asura_Assert(0,"meta music tracks already loaded");
		return false;
	}

	Asura_ResourcePushType(ASURA_RESOURCE_META_MUSIC);
	{
		Asura_MetaMusic_Handler&	xHandler	= Asura_MetaMusic_System::GetCurrentHandler();
		Asura_MetaMusic*			pxMusic		= xHandler.Initialise(m_iNumberOfMusicTracks);

		Asura_MetaMusic_System::ClearBestGuessFile();

		for(int i=0;i<m_iNumberOfMusicTracks;i++,pxMusic++)
		{
			xStream >> pxMusic->m_xName;
			xStream >> pxMusic->m_uNameHash;
			xStream >> pxMusic->m_fDuration;
			xStream >> pxMusic->m_fAmbientBeatsPerMinute;
			xStream >> pxMusic->m_fAmbientLoopBeats;
			if(Version>=4)
			{
				xStream >> pxMusic->m_fTensionBeatsPerMinute;
				xStream >> pxMusic->m_fTensionLoopBeats;
				xStream >> pxMusic->m_fActionBeatsPerMinute;
				xStream >> pxMusic->m_fActionLoopBeats;
			}
			else
			{
				pxMusic->m_fTensionBeatsPerMinute = pxMusic->m_fActionBeatsPerMinute = pxMusic->m_fAmbientBeatsPerMinute;
				pxMusic->m_fTensionLoopBeats = pxMusic->m_fActionLoopBeats = pxMusic->m_fAmbientLoopBeats;
			}
			if(Version>=1)
			{
				xStream >> pxMusic->m_fVolume;
			}
			else
			{
				pxMusic->m_fVolume = 1.0f;			
			}
			if(Version>=3)
			{
				xStream >> pxMusic->m_uFlags;
			}
			else
			{
				pxMusic->m_uFlags = 0;
			}

			if(Version>=5)
			{
				int iIntroLoopState;
				xStream >> iIntroLoopState;
				pxMusic->m_eIntroLoopState = (Asura_MetaMusicState)iIntroLoopState;
			}
			else
			{
				pxMusic->m_eIntroLoopState = ASURA_METAMUSIC_AMBIENT;
			}


			for(int j=0;j<ASURA_METAMUSIC_NUMSTATES;j++)
			{
				xStream >> pxMusic->m_axStates[j].m_iFirstIndex;
				xStream >> pxMusic->m_axStates[j].m_iNumberOfFiles;
				xStream >> pxMusic->m_axStates[j].m_fFadeInTime;
				xStream >> pxMusic->m_axStates[j].m_fFadeOutTime;

				if(Version<2 && j== ASURA_METAMUSIC_AMBIENTOUTRO)
				{
					//copy ambient outro to tension and action outros
					pxMusic->m_axStates[ASURA_METAMUSIC_TENSIONOUTRO] = pxMusic->m_axStates[ASURA_METAMUSIC_AMBIENTOUTRO];
					pxMusic->m_axStates[ASURA_METAMUSIC_ACTIONOUTRO] = pxMusic->m_axStates[ASURA_METAMUSIC_AMBIENTOUTRO];
					j+=2;

				}
			}
			xStream >> pxMusic->m_iNumberOfFiles;

			if(pxMusic->m_iNumberOfFiles)
			{
				Asura_ResourceAllocateArray(pxMusic->m_pxFiles, pxMusic->m_iNumberOfFiles);
				for(int j=0;j<pxMusic->m_iNumberOfFiles;j++)
				{
					xStream >> pxMusic->m_pxFiles[j].m_xFilename;
					if(Version>=1)
					{
						xStream >> pxMusic->m_pxFiles[j].m_fVolume;
					}
					else
					{
						pxMusic->m_pxFiles[j].m_fVolume = 1.0f;
					}
				}
			}
			else
			{
				pxMusic->m_pxFiles = 0;
			}

#ifdef ASURA_TOOLS
			if(Version<2)
			{
				//If in tools ,then we need duplicate any file entries for these states , or
				//things will go wrong as soon as any editing takes place
				int iFirstIndex = pxMusic->m_axStates[ASURA_METAMUSIC_AMBIENTOUTRO].m_iFirstIndex;
				int iNumFilesInState = pxMusic->m_axStates[ASURA_METAMUSIC_AMBIENTOUTRO].m_iNumberOfFiles;

				pxMusic->m_axStates[ASURA_METAMUSIC_TENSIONOUTRO].m_iFirstIndex += iNumFilesInState;
				for(int j = ASURA_METAMUSIC_ACTIONOUTRO;j<ASURA_METAMUSIC_NUMSTATES;j++)
				{
					pxMusic->m_axStates[j].m_iFirstIndex += iNumFilesInState*2;
				}

				if(iNumFilesInState)
				{
					//allocate space for extra filenames
					Asura_MetaMusic_File* pxFiles;
					Asura_ResourceAllocateArray(pxFiles, pxMusic->m_iNumberOfFiles+iNumFilesInState*2);

					//copy from old array
					int iToIndex = 0;
					for(int j=0;j<pxMusic->m_iNumberOfFiles;j++,iToIndex++)
					{
						if(j==iFirstIndex+iNumFilesInState)
						{
							iToIndex += iNumFilesInState*2;
						}
						pxFiles[j] = pxMusic->m_pxFiles[j];

					}
					//insert duplicate of outro files
					int iIndexAmb = pxMusic->m_axStates[ASURA_METAMUSIC_AMBIENTOUTRO].m_iFirstIndex;
					int iIndexTen = pxMusic->m_axStates[ASURA_METAMUSIC_TENSIONOUTRO].m_iFirstIndex;
					int iIndexAct = pxMusic->m_axStates[ASURA_METAMUSIC_ACTIONOUTRO].m_iFirstIndex;

					for(int j=0;j<iNumFilesInState;j++,iIndexAmb++,iIndexTen++,iIndexAct++)
					{
						pxFiles[iIndexTen] = pxFiles[iIndexAmb];
						pxFiles[iIndexAct] = pxFiles[iIndexAmb];
					}

					//replace old array
					Asura_ResourceDeallocateArray(pxMusic->m_pxFiles);
					pxMusic->m_pxFiles = pxFiles;
					pxMusic->m_iNumberOfFiles += iNumFilesInState*2;
				}

			}
#endif //ASURA_TOOLS
		}
	}
	Asura_ResourcePopType();

	if(Flags & ChunkFlag_HasZoneController)
	{
		#ifdef ASURA_USE_METAMUSICZONES
		Asura_Zone_Controller*	pxZoneController	= Asura_MetaMusic_System::GetZoneController();

		if(!pxZoneController)
		{
			Asura_Assert(0,"Meta music zone controller not allocated");
			return false;
		}
		if(Version<6) return false;//ditching old versions of the music zones.

		pxZoneController->ReadFromChunkStream(xStream);

		#else
		Asura_Assert(0,"Unexpected meta music zone data");
		return false;
		#endif
	}

	return true;
}


#ifdef ASURA_TOOLS
void Asura_Chunk_MetaMusic::Create(Asura_SaveBlock* pxSaveBlock,Asura_MetaMusic_Handler* pxHandler)
{
	//save from the current handler if none has been specified
	if(!pxHandler) pxHandler = &Asura_MetaMusic_System::GetCurrentHandler();

	const int	iNumberOfMusicTracks	= pxHandler->GetNumberOfTracks();

	if(!iNumberOfMusicTracks)
	{
		return;
	}

	u_int uChunkFlags = 0;
	#ifdef ASURA_USE_METAMUSICZONES
	uChunkFlags |= ChunkFlag_HasZoneController;
	#endif


	// Write the data...
	Asura_Chunk_SaveBlock_Stream xStream(pxSaveBlock);
	xStream.WriteChunk_Start(ASURA_CHUNK_METAMUSIC, ASURA_CHUNK_METAMUSIC_VERSION, uChunkFlags);


	xStream << iNumberOfMusicTracks;

	for(int i=0;i<iNumberOfMusicTracks;i++)
	{
		const Asura_MetaMusic*	pxMusic	= pxHandler->GetTrackData(i);

		//prepare bitfield wrapper
		Asura_WriteToStream_Bitfield_Wrapper<u_int>	xFlagsBitfield (pxMusic->m_uFlags);
		xFlagsBitfield.Add(pxMusic->m_bPlayAmbientToEndOfLoop, 1);
		xFlagsBitfield.Add(pxMusic->m_bPlayTensionToEndOfLoop, 1);
		xFlagsBitfield.Add(pxMusic->m_bPlayActionToEndOfLoop, 1);
		xFlagsBitfield.Add(pxMusic->m_bChooseNewFileAfterEachLoop, 1);
		xFlagsBitfield.Add(0, 28);


		xStream << pxMusic->m_xName;
		xStream << pxMusic->m_uNameHash;
		xStream << pxMusic->m_fDuration;
		xStream << pxMusic->m_fAmbientBeatsPerMinute;
		xStream << pxMusic->m_fAmbientLoopBeats;
		xStream << pxMusic->m_fTensionBeatsPerMinute;
		xStream << pxMusic->m_fTensionLoopBeats;
		xStream << pxMusic->m_fActionBeatsPerMinute;
		xStream << pxMusic->m_fActionLoopBeats;
		xStream << pxMusic->m_fVolume;
		xStream << xFlagsBitfield;
		xStream << pxMusic->m_eIntroLoopState;

		

		for(int j=0;j<ASURA_METAMUSIC_NUMSTATES;j++)
		{
			xStream << pxMusic->m_axStates[j].m_iFirstIndex;
			xStream << pxMusic->m_axStates[j].m_iNumberOfFiles;
			xStream << pxMusic->m_axStates[j].m_fFadeInTime;
			xStream << pxMusic->m_axStates[j].m_fFadeOutTime;
		}
		xStream << pxMusic->m_iNumberOfFiles;

		for(int j=0;j<pxMusic->m_iNumberOfFiles;j++)
		{
			xStream << pxMusic->m_pxFiles[j].m_xFilename;
			xStream << pxMusic->m_pxFiles[j].m_fVolume;
		}
		
	}

	#ifdef ASURA_USE_METAMUSICZONES
	//and the zone data itself
	pxHandler->GetZoneController()->WriteToChunkStream(xStream);
	#endif

	xStream.WriteChunk_End	();

}

#endif // ASURA_TOOLS


bool Asura_Chunk_MetaMusic_Ingame_State::Process(const char* pcChunkBuffer) const
{
	Asura_Chunk_Memory_Stream	xStream(Size-sizeof(this),pcChunkBuffer);
	return Process(xStream);
}

bool Asura_Chunk_MetaMusic_Ingame_State::Process(Asura_Chunk_Stream& xStream) const
{
	Asura_MetaMusic_Handler&	xHandler	= Asura_MetaMusic_System::GetCurrentHandler();

	xHandler.ReadFromChunkStream(xStream);
	return true;
}

void Asura_Chunk_MetaMusic_Ingame_State::Create(Asura_SaveBlock_Base& xSaveBlock)
{
	Asura_Chunk_SaveBlock_Stream	xStream(&xSaveBlock);
	const Asura_MetaMusic_Handler&	xHandler	= Asura_MetaMusic_System::GetCurrentHandler();

	xStream.WriteChunk_Start(ASURA_CHUNK_METAMUSIC_INGAME, 0, 0);
	xHandler.WriteToChunkStream(xStream);
	xStream.WriteChunk_End	();
}

#endif	//!defined(ASURA_SUPPORT_DYNAMIC_MUSIC_SYSTEM)


/// End of file. ///
