//////////////////////////////////////////////////////////////////////////////
//
// Asura_PSP_Sound.cpp
//
// Copyright (c) 2007 Rebellion. All rights reserved.
//
// Description:
//
//     class to manage PSP sound data, for both streamed and non-streamed
//     sounds
//
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

// This file's header.
#include "Sound/Asura_PSP_Sound.h"

// asura includes
#include "Core/Memory/Asura_Mem.h"
#include "Core/Asura_String.h"
#include "Debugging/Asura_Logfile.h"

// Asura PSP
#include "Sound/Asura_PSP_Atrac.h"
#include "Sound/Asura_PSP_SAS.h"
#include "Sound/Asura_PSP_SimpleAudio.h"

// Useful debugging defines...
#if 1
#define LOG_OUTPUT(s)
#else
#define LOG_OUTPUT(s)	Asura_OutputToDebugger_Retail( s )
#endif


// S T A T I C   C L A S S   D A T A /////////////////////////////////////////
Asura_PSP_ActiveSound Asura_PSP_Sound::s_axActiveSounds[uASURA_PSP_MAX_VOICES];
Asura_PSP_LoadedSound Asura_PSP_Sound::s_axLoadedSounds[ASURA_MAX_NUMBER_OF_LOADED_SOUNDS];
char Asura_PSP_Sound::s_aszFilenames[uASURA_PSP_STREAMING_MAX_FILES][uASURA_PSP_STREAMING_MAX_FILENAME_LENGTH];
bool Asura_PSP_Sound::s_abFileIsPermanent[uASURA_PSP_STREAMING_MAX_FILES];
const char* Asura_PSP_Sound::s_szCurFilename;
Asura_Handle Asura_PSP_Sound::s_xMusicSoundHandle = ASURA_INVALID_HANDLE;

// C L A S S   I M P L E M E N T A T I O N ///////////////////////////////////
void Asura_PSP_Sound::Initialise()
{
	for(u_int u = 0; u < uASURA_PSP_MAX_VOICES; ++u)
	{
		s_axActiveSounds[u].m_iLoadedSoundIndex = -1;
	}

	Asura_PSP_SAS::Initialise();
	Asura_PSP_Atrac::Initialise();
	Asura_PSP_SimpleAudio::Initialise();

	// A single music track
	Asura_PSP_Sound::s_xMusicSoundHandle = uASURA_PSP_MAX_VOICES;
}

void Asura_PSP_Sound::Reset()
{
	Asura_PSP_Atrac::Reset();
	Asura_PSP_SAS::Reset();

	// flag all non-permanent streamed sounds as unloaded.
	for (u_int u = 0; u < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS; ++u)
	{
		if (s_axLoadedSounds[u].m_eLoadedType == ASURA_PSP_LT_STREAMED)
		{
			bool bPermanent = false;
			u_int uFilename;
			for (uFilename = 0; uFilename < uASURA_PSP_STREAMING_MAX_FILES; ++uFilename)
			{
				if (s_axLoadedSounds[u].m_xStreamedLoadedSound.m_szFilename == s_aszFilenames[uFilename])
				{
					bPermanent = s_abFileIsPermanent[uFilename];
					break;
				}
			}
			Asura_Assert( uFilename != uASURA_PSP_STREAMING_MAX_FILES, "failed to find filename for streamed sound");
			if (!bPermanent)
			{
				s_axLoadedSounds[u].m_eLoadedType = ASURA_PSP_LT_NOT_LOADED;
				Asura_Mem::Set(&s_axLoadedSounds[u].m_xStreamedLoadedSound, 0, sizeof(Asura_PSP_Streamed_LoadedSound));
			}
		}
	}
	for (u_int u = 0; u < uASURA_PSP_STREAMING_MAX_FILES; ++u)
	{
		if (!s_abFileIsPermanent[u])
		{
			*s_aszFilenames[u] = 0;
		}
	}
	s_szCurFilename = 0;
}

void Asura_PSP_Sound::Shutdown()
{
	Asura_PSP_SimpleAudio::Shutdown();
	Asura_PSP_Atrac::Shutdown();
	Asura_PSP_SAS::Shutdown();	
}

void Asura_PSP_Sound::SetResetPoint()
{
	for (u_int u = 0; u < uASURA_PSP_STREAMING_MAX_FILES; ++u)
	{
		if (*s_aszFilenames[u])
		{
			s_abFileIsPermanent[u] = true;
		}
	}
}


// Standard playing methods
bool Asura_PSP_Sound::CreateSound(int iLoadedSoundID, Asura_Handle iSoundHandle)
{
	Asura_Assert(s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex == -1, "sound already created");

	bool bRet;
	if (IsResident(iLoadedSoundID))
	{
		bRet = Asura_PSP_SAS::CreateSound(iLoadedSoundID, iSoundHandle);
	}
	else if (IsStreamed(iLoadedSoundID))
	{
		const char* const szFileName = s_axLoadedSounds[iLoadedSoundID].m_xStreamedLoadedSound.m_szFilename;
		const u_int uStartOffset = s_axLoadedSounds[iLoadedSoundID].m_xStreamedLoadedSound.m_uOffset;
		const u_int uSize = s_axLoadedSounds[iLoadedSoundID].m_xStreamedLoadedSound.m_uSize;
		LOG_OUTPUT( ( "CreateSound 0x%X '%s' Offset 0x%X uSize 0x%X\n", iSoundHandle, szFileName, uStartOffset, uSize ) );
		bRet = Asura_PSP_Atrac::Create( iSoundHandle, szFileName, uStartOffset, uSize);
	}
	else
	{
		bRet = false;
	}

	if (bRet)
	{
		s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex = iLoadedSoundID;
	}

	return bRet;
}

bool Asura_PSP_Sound::PlaySound(Asura_Handle iSoundHandle, bool bLooping)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::PlaySound(iSoundHandle, bLooping);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		// TODO: Play() needs to return true or false
		LOG_OUTPUT( ( "PlaySound 0x%X\n", iSoundHandle ) );
		Asura_PSP_Atrac::Play( iSoundHandle, bLooping );
		const bool bRet = true;
		return bRet;
	}
	else
	{
		return false;
	}
}

bool Asura_PSP_Sound::IsSoundPlaying(Asura_Handle iSoundHandle)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::IsSoundPlaying(iSoundHandle);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		const bool bRet = Asura_PSP_Atrac::IsPlaying( iSoundHandle );
		return bRet;
	}
	else
	{
		return false;
	}
}

bool Asura_PSP_Sound::IsSoundPaused(Asura_Handle iSoundHandle)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::IsSoundPaused(iSoundHandle);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		const bool bRet = Asura_PSP_Atrac::IsPaused( iSoundHandle );
		return bRet;
	}
	else
	{
		return false;
	}
}

bool Asura_PSP_Sound::StopSound(Asura_Handle iSoundHandle)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::StopSound(iSoundHandle);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		// TODO: Stop needs to return true or false
		LOG_OUTPUT( ( "StopSound 0x%X\n", iSoundHandle ) );
		Asura_PSP_Atrac::Stop( iSoundHandle );
		const bool bRet = true;
		return bRet;
	}
	else
	{
		return false;
	}
}

bool Asura_PSP_Sound::DestroySound(Asura_Handle iSoundHandle)
{
	bool bRet;
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		bRet = Asura_PSP_SAS::DestroySound(iSoundHandle);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		// TODO: Release() needs to return true or false
		LOG_OUTPUT( ( "DestroySound 0x%X\n", iSoundHandle ) );
		Asura_PSP_Atrac::Release( iSoundHandle );
		bRet = true;
	}
	else
	{
		bRet = false;
	}

	s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex = -1;
	return bRet;
}

bool Asura_PSP_Sound::PauseSound(Asura_Handle iSoundHandle)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::PauseSound(iSoundHandle);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		// TODO: PauseTrack needs to return true or false
		Asura_PSP_Atrac::PauseTrack( iSoundHandle, true );
		const bool bRet = true;
		return bRet;
	}
	else
	{
		return false;
	}
}

bool Asura_PSP_Sound::UnpauseSound(Asura_Handle iSoundHandle)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::UnpauseSound(iSoundHandle);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		// TODO: PauseTrack needs to return true or false
		Asura_PSP_Atrac::PauseTrack( iSoundHandle, false );
		const bool bRet = true;
		return bRet;
	}
	else
	{
		return false;
	}
}

// Property setting
bool Asura_PSP_Sound::SetSoundVolumePanAndFade(Asura_Handle iSoundHandle, float fVolume, float fPan, float fFade)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::SetSoundVolumePanAndFade(iSoundHandle, fVolume, fPan, fFade);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		// TODO: SetVolume needs to return true or false
		// TODO: set the pan
		Asura_PSP_Atrac::SetVolume( iSoundHandle, fVolume );
		const bool bRet = true;
		return bRet;
	}
	else
	{
		return false;
	}
}

bool Asura_PSP_Sound::SetSoundPitch(Asura_Handle iSoundHandle, float fPitch)
{
	Asura_Assert(iSoundHandle >= 0 && iSoundHandle < uASURA_PSP_MAX_VOICES, "invalid handle");
	if (IsResident( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		return Asura_PSP_SAS::SetSoundPitch(iSoundHandle, fPitch);
	}
	else if (IsStreamed( s_axActiveSounds[iSoundHandle].m_iLoadedSoundIndex ))
	{
		//Asura_Assert( false, "SetSoundPitch not allowed on streamed sounds on the PSP" );
		// If pitch is 0 then pause the sound
		Asura_PSP_Atrac::PauseTrack( iSoundHandle, ( fPitch == 0.0f ) );
		return true;
	}
	else
	{
		return false;
	}
}



ASURA_PSP_LOADED_TYPE Asura_PSP_Sound::GetLoadedType(int iLoadedIndex)
{
	Asura_Assert( iLoadedIndex >= 0 && iLoadedIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "invalid index");
	return s_axLoadedSounds[iLoadedIndex].m_eLoadedType;
}

int Asura_PSP_Sound::GetLoadedIndex(u_int uVoice)
{
	Asura_Assert(uVoice < uASURA_PSP_MAX_VOICES, "invalid voice");
	return s_axActiveSounds[uVoice].m_iLoadedSoundIndex;
}

// GetXXXSound() functions Assert and return NULL if sound is not loaded or is wrong type.
const Asura_PSP_SAS_LoadedSound* Asura_PSP_Sound::GetResidentSound(int iLoadedIndex)
{
	Asura_Assert( iLoadedIndex >= 0 && iLoadedIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "invalid index");
	Asura_Assert( s_axLoadedSounds[iLoadedIndex].m_eLoadedType == ASURA_PSP_LT_RESIDENT, "sound not resident" );
	if (s_axLoadedSounds[iLoadedIndex].m_eLoadedType == ASURA_PSP_LT_RESIDENT)
	{
		return &s_axLoadedSounds[iLoadedIndex].m_xSASLoadedSound;
	}
	return NULL;
}

const Asura_PSP_Streamed_LoadedSound* Asura_PSP_Sound::GetStreamedSound(int iLoadedIndex)
{
	Asura_Assert( iLoadedIndex >= 0 && iLoadedIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "invalid index");
	Asura_Assert( s_axLoadedSounds[iLoadedIndex].m_eLoadedType == ASURA_PSP_LT_STREAMED, "sound not streamed" );
	if (s_axLoadedSounds[iLoadedIndex].m_eLoadedType == ASURA_PSP_LT_STREAMED)
	{
		return &s_axLoadedSounds[iLoadedIndex].m_xStreamedLoadedSound;
	}
	return NULL;
}


void Asura_PSP_Sound::SoundLoaded( int iLoadedIndex, u_char* pucData, bool bAllocatedOwnData, float fPitchModifier, u_int uSize )
{
	Asura_Assert( iLoadedIndex >= 0 && iLoadedIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "invalid index");
	Asura_Assert( s_axLoadedSounds[iLoadedIndex].m_eLoadedType == ASURA_PSP_LT_NOT_LOADED, "sound already loaded" );
	s_axLoadedSounds[iLoadedIndex].m_eLoadedType = ASURA_PSP_LT_RESIDENT;

	s_axLoadedSounds[iLoadedIndex].m_xSASLoadedSound.m_pucData = pucData;
	s_axLoadedSounds[iLoadedIndex].m_xSASLoadedSound.m_bAllocatedOwnData = bAllocatedOwnData;
	s_axLoadedSounds[iLoadedIndex].m_xSASLoadedSound.m_fPitchModifier = fPitchModifier;
	s_axLoadedSounds[iLoadedIndex].m_xSASLoadedSound.m_uSize = uSize;
}

void Asura_PSP_Sound::SoundUnloaded( int iLoadedIndex )
{
	Asura_Assert( iLoadedIndex >= 0 && iLoadedIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "invalid index");
	Asura_Assert( s_axLoadedSounds[iLoadedIndex].m_eLoadedType == ASURA_PSP_LT_RESIDENT, "sound not resident" );

	s_axLoadedSounds[iLoadedIndex].m_eLoadedType = ASURA_PSP_LT_NOT_LOADED;
	Asura_Mem::Set( &s_axLoadedSounds[iLoadedIndex].m_xSASLoadedSound, 0, sizeof(Asura_PSP_SAS_LoadedSound) );
}

void Asura_PSP_Sound::InitStreamedSounds( const char* szFilename )
{
	// find free filename slot, store filename and set cur filename pointer.

	s_szCurFilename = 0;

	for (u_int u = 0; u < uASURA_PSP_STREAMING_MAX_FILES; ++u)
	{
		if (!*s_aszFilenames[u])
		{
			Asura_PrintfToBfr( s_aszFilenames[u], "%s", uASURA_PSP_STREAMING_MAX_FILENAME_LENGTH, true, szFilename );
			s_szCurFilename = s_aszFilenames[u];
			break;
		}
	}

	Asura_Assert(s_szCurFilename, "too many sets of streaming sounds?");
}

void Asura_PSP_Sound::AddStreamingSoundData( int iSoundID, int iOffset, int iSize, Asura_Hash_ID uSoundHash )
{
	Asura_Assert(s_szCurFilename, "no filename for streamed sound");

	// flag sound as loaded in Asura_Sound_System and get loaded index.
    int iLoadedIndex = Asura_Sound_System::SoundLoadedByPlatformCode(iSoundID, uSoundHash);
    if (iLoadedIndex < 0)
    {
		Asura_Assert(false, "failed to add streamed sound to list of loaded sounds");
        return;
    }

	Asura_Assert( iLoadedIndex >= 0 && iLoadedIndex < ASURA_MAX_NUMBER_OF_LOADED_SOUNDS, "invalid index");
	Asura_Assert( s_axLoadedSounds[iLoadedIndex].m_eLoadedType == ASURA_PSP_LT_NOT_LOADED, "sound already loaded" );
	s_axLoadedSounds[iLoadedIndex].m_eLoadedType = ASURA_PSP_LT_STREAMED;

	s_axLoadedSounds[iLoadedIndex].m_xStreamedLoadedSound.m_szFilename = s_szCurFilename;
	s_axLoadedSounds[iLoadedIndex].m_xStreamedLoadedSound.m_uOffset = iOffset;
	s_axLoadedSounds[iLoadedIndex].m_xStreamedLoadedSound.m_uSize = iSize;
}

