///////////////////////////////////////////////////////////////////////////////
//
// File      : Asura_PS3_MultiStream_Streaming.h
// Author    : Philip Hall
// Created   : 11/09/2007
//
// Copyright (C) 2007 Rebellion. All Rights Reserved
//
///////////////////////////////////////////////////////////////////////////////

#ifndef __ASURA_PS3_MULTISTREAM_STREAMING_H__
#define __ASURA_PS3_MULTISTREAM_STREAMING_H__

#ifndef ASURA_USE_AUDIO_SYSTEM

#if defined(ASURA_PS3_USE_MULTISTREAM) && (ASURA_PS3_USE_MULTISTREAM != 0)

// Asura headers
#include "Core/Threads/Asura_ProducerConsumerQueue.h"
#include "Sound/Asura_Sound.h"

// Asura PS3 Headers
#include "FileAccess/Asura_PS3_FileSystem.h"
#include "Sound/Asura_PS3_Sound_Loader.h"


#define ASURA_PS3_SOUND_USE_MP3		(1)	
//#define ASURA_PS3_SOUND_USE_ATRAC	(1)

static const u_int ASURA_PS3_STREAMING_BUFFER_SIZE    = 128 * 1024; // 128k gives about 8 seconds of data at 128kbps
static const u_int ASURA_PS3_MUSIC_STREAM_BUFFER_SIZE = 128 * 1024; // for 8 streams that means each read gets 1 second.

enum ASURA_MSSTREAM_LOADING_STATUS
{
	ASURA_MSSTREAM_LOADING_STATUS_INACTIVE,
	ASURA_MSSTREAM_LOADING_STATUS_PRELOAD,
	ASURA_MSSTREAM_LOADING_STATUS_LOADING,
	ASURA_MSSTREAM_LOADING_STATUS_ERROR,
	ASURA_MSSTREAM_LOADING_STATUS_SUCCESS
};

// Async streamed sound initialisation - BW
class Asura_PS3_MSStreamLoader
{
public:
	Asura_PS3_MSStreamLoader();
	
	void Init( const char* szFilename, void **pStreamBuffers );
	void Deinit( bool bQueued=false );
	
	void GetData( Asura_PS3_FileSystem::Handle *pxFileHandle, Asura_PS3_Sound_Info *pxSoundInfo, u_int *puBufferSizeRead );
	bool IsInUse() { return (m_eStatus != ASURA_MSSTREAM_LOADING_STATUS_INACTIVE); } //n.b. doesn't lock the mutex to read this, this should always be ok.
	ASURA_MSSTREAM_LOADING_STATUS Status() { m_xMutex.Lock(); ASURA_MSSTREAM_LOADING_STATUS eStatus = m_eStatus; m_xMutex.Unlock(); return eStatus; }

	static void QueuedDeinit( void *pData, u_int uIgnore );
	static void QueuedLoad( void *pData, u_int uIgnore );

private:
	void Load();

	// m_Mutex - use when accessing m_eStatus.
	// All other member vars are only accessed by one thread at a time (the main thread checks loader completion before accessing them).
	Asura_Mutex								m_xMutex;

	Asura_PS3_FileSystem::Handle			m_xFileHandle;	// Source sound file (should be open while streaming)
	Asura_PS3_Sound_Info					m_xSoundInfo;	// Information about this sound
	void *									m_pStreamBuffers[2];
	u_int									m_uiBufferSizeRead[2];
	char									m_szFilename[ASURA_PS3_MAX_PATH];
	volatile ASURA_MSSTREAM_LOADING_STATUS	m_eStatus;
};

// helper class for loading more data requested by streams, but on a separate thread.
class Asura_PS3_MS_LoadDataRequest
{
public:
	// load request data
	int m_iStreamHandle;
	void* m_pWriteBuffer;
};


////PGH/////////////////////////////////////////
// PS3 streamed sound for MultiStream
////////////////////////////////////////////////
class Asura_PS3_MSStreamingSound
{
public:
	Asura_PS3_MSStreamingSound();
	~Asura_PS3_MSStreamingSound();

	bool Create( const char* szFilename,  Asura_Sound_System::SoundGroup eGroup);
	void Destroy();

	void	Update();
	void	Play( bool bLooping, bool bPreload );
	void	Stop();
	void	Pause();
	void	Resume();
	void	SetVolume( float fVolume );
	void	UpdateVolume();
	void	SetPosition( float fTime );
	float	GetPosition() const;
	float	GetLength() const;
	bool	IsValid() const { return m_pxLoader || (m_iMSStreamHandle >= 0); }
	bool	IsFinished() const { return m_bStopped; }
	bool	IsPlaying() const { return !m_bStopped && !m_bPaused; }
	ASURA_MSSTREAM_LOADING_STATUS GetLoadStatus ();

	Asura_Sound_System::SoundGroup GetSoundGroup() const { return m_eSoundGroup; }

	static void LoadDataRequest( void* pThis, u_int uSize );

private:
	float		m_fVolume;					// Playback volume
	int			m_iMSStreamHandle;			// Multistream stream handle
	int			m_iMSSubBus;				// Multistream stream SubBus (includes SubBus flag mask)

	bool		m_bStopped;					// Playback status (BW stopping and pausing aren't mutually exclusive, so need to track both not just m_bPlaying)
	bool		m_bPaused;					
	int			m_iLoopStart;				// Start of looping section
	void*		m_pStreamBuffers[2];		// 2 sound streaming buffers (dynamically allocated, should be aligned)
	u_int		m_uiBufferStarts[2];		// Offset of start of stream buffer relative to entire stream
	u_int		m_uiBufferSizeRead[2];      // Amount of data read into the last buffer (only really for position calculation)
	char		m_szFilename[ASURA_PS3_MAX_PATH];

	Asura_PS3_Sound_Info			m_xSoundInfo;	// Information about this sound
	Asura_PS3_FileSystem::Handle	m_xFileHandle;	// Source sound file (should be open while streaming)
	Asura_Sound_System::SoundGroup	m_eSoundGroup;  // Volume group for this sound
	
	Asura_PS3_MSStreamLoader*		m_pxLoader;	// 
	Asura_PS3_MS_LoadDataRequest	m_xLoadDataRequest;

	float CalcDuration(int* piSampleBlockSize, int* piSamplesPerBlock) const;
	bool  DoStreamData(void* pBuffer, u_int uBufferSize, u_int& uSizeRead, bool bRestartLoop);
	
	static void StreamLocalCallback(int iStreamHandle, void* pUserData, int iCallbackType, void* pWriteBuffer, int nBufferSize);
	
	bool PostLoad(void);
	void DoPlay( void );

	ASURA_DECLARE_NO_COPYING(Asura_PS3_MSStreamingSound)
};


////PGH/////////////////////////////////////////
// PS3 Streaming sound system
////////////////////////////////////////////////
class Asura_PS3_MultiStream_Streaming
{
	static const int ASURA_PS3_MAX_STREAMING_SOUNDS = 2;
public:
	Asura_PS3_MultiStream_Streaming();

	bool Initialise();
	void Shutdown();
	void Update();
	void Reset();
	
	bool Create( int iStreamID, const char* szFilename, Asura_Sound_System::SoundGroup eSoundGroup );
	void Play( int iStreamID, bool bLooping, bool bPreload );
	void Stop( int iStreamID );
	void Release( int iStreamID );
	void Pause( int iStreamID );
	void Resume( int iStreamID );
	void SetVolume( int iStreamID, float fVol );
	bool IsFinished( int iStreamID );
	float GetPosition( int iStreamID );
	float GetLength( int iStreamID );
	void SetPosition( int iStreamID, float fTime );
	bool IsSoundReady( int iStreamID );

	void SetSoundGroupVolume(Asura_Sound_System::SoundGroup eSoundGroup, float fVolume);
	int GetMaxStreamingSounds() const { return ASURA_PS3_MAX_STREAMING_SOUNDS; }

	// static class functions
	static void AddToLoadQueue( Asura_ProduceConsumerFunction pfTaskFunction, void* pData, u_int uSizeOfData );
	static void QueuedFileClose( Asura_PS3_FileSystem::Handle& iHandle ); // adds file handle to queue of files to close and sets reference to invalid handle.

private:

	// private static functions
	static void CloseFileTask( void* pData, u_int uSize );

	// member data
	void*						m_pWorkingMemory;  
	Asura_PS3_MSStreamingSound	m_axStreamingSounds[ASURA_PS3_MAX_STREAMING_SOUNDS];
	
	// static class data
	static Asura_ProducerConsumerQueue s_xLoadingThreadQueue;

	ASURA_DECLARE_NO_COPYING(Asura_PS3_MultiStream_Streaming)
};

#endif	// ASURA_PS3_USE_MULTISTREAM

#endif // #ifndef ASURA_USE_AUDIO_SYSTEM

#endif

//eof