#pragma once

#include "Mix/Module.h"
#include "Mix/Thread.h"
#include "Mix/Event.h"
#include "Mix/CriticalSection.h"
#include "Mix/Plugin/Sound/Decoder.h"
#include "Mix/Sound/IManager.h"
#include "Mix/Private/Sound/Types.h"

namespace Mix{
	class UserFile;
	namespace Plugin{
		namespace Sound{
			class IDecoder;
		}
	}
}

namespace Mix{ namespace Sound{

	class WaveReader;
	class Task;
	class Listener;

	class Manager : public Mix::Sound::IManager
	{
	private:
		enum PRIVATE_VALUE
		{
			MIN_CHANNELS = 2, //最小チャンネル数
			MIN_BITSPERSAMPLE = 16,	//最小ビット数
			MIN_SAMPLEPERSEC = 44100,	//最小サンプル数

			SLEEP_TIME = 1,	//ストリーミングスレッドのスリープ時間
		};

		class DecoderPlugin
		{
		private:
			typedef bool ( __stdcall* CheckFormatFunc )( const void* pData, unsigned int& size );
			typedef bool ( __stdcall* CreatDecoderFunc )( Mix::Plugin::Sound::IDecoder** ppDecoder );

		private:
			HMODULE m_hModule;
			UInt32 m_KeySize;

		private:
			DecoderPlugin( void );
			~DecoderPlugin( void );

		public:
			static DecoderPlugin* CreateInstance( HMODULE hModule );

			void Destroy( void );

			UInt32 GetKeySize( void ) const;
			Boolean CheckFormat( const void* pKey, UInt32& keySize );

			Boolean CreateDecoder( Mix::Plugin::Sound::IDecoder** ppDecoder );

		private:
			static const char* FN_CHECK_FORMAT;
			static const char* FN_CREATE_DECODER;
		};

		typedef Mix::STL::List<Mix::Memory::SECTION_SOUND, Manager::DecoderPlugin*> DecoderPluginList;
		typedef Mix::STL::List<Mix::Memory::SECTION_SOUND, Mix::Sound::Task*> TaskList;

	public:
		static Manager* CreateInstance( void );

	private:
		Mix::Module m_X3DAModule;
		Mix::Sound::PX3DAudioCalculate m_X3DACalcFunc;

		IXAudio2* m_pXAudio;
		IXAudio2MasteringVoice* m_pMasterVoice;
		XAUDIO2_DEVICE_DETAILS m_Details;

		X3DAUDIO_HANDLE m_hX3DAudio;

		Mix::IO::IManager* m_pFileMgr;

		Mix::Thread m_Thread;
		Mix::Event m_EndEvent;

		Mix::STL::Vector<Mix::Memory::SECTION_SOUND, UInt8> m_KeyTemp;
		DecoderPluginList m_DecoderPluginList;

		TaskList m_TaskList;
		Mix::CriticalSection m_TaskListSync;

	private:
		Manager( void );
		virtual ~Manager( void );

		Mix::IO::IReader* CreateReader( const wchar_t* pFileName, Boolean bBuffered );
		Mix::Plugin::Sound::IDecoder* CreateDecoder( Mix::Sound::WaveReader* pSource, const wchar_t* pFailedMsg );

		void AddTask( Mix::Sound::Task* pTask );

		const wchar_t* GetXA2ResultText( HRESULT ret );

		static void __cdecl ThreadEntry_Main( void* pArg );
		void ThreadMain( void );

	public:
		Boolean Initialize( Mix::UserFile* pSysReport );

		Boolean LoadPlugin( Mix::Plugin::TYPE type, HMODULE hModule );

		Boolean CreateStreamingController( const wchar_t* actionMsg,
										Mix::IO::IReader* pReader,
										Mix::Sound::IController** ppController );

		Boolean CreateSimpleEmitter(	const wchar_t* actionMsg,
										const wchar_t* pFilePath,
										Mix::Sound::IListener* pListener,
										const WAVEFORMATEX* pFormat,
										Mix::Memory::IBuffer* pBuffer,
										const Mix::Vector3& localFront,
										const Mix::Vector3& localUp,
										Mix::Sound::IEmitter** ppEmitter );

		Boolean CreateStreamingEmitter(	const wchar_t* actionMsg,
										Mix::Sound::IListener* pListener,
										Mix::IO::IReader* pReader,
										const Mix::Vector3& localFront,
										const Mix::Vector3& localUp,
										Mix::Sound::IEmitter** ppEmitter );

		Boolean CloneSimpleController( const Mix::Sound::IController* pSrc, Mix::Sound::IController** ppDst );
		Boolean CloneStreamingController( Mix::IO::IReader* pSrcReader, Mix::Sound::IController** ppDst );
		Boolean CloneSimpleEmitter( Mix::Sound::Listener* pListener, const wchar_t* pFileName, const WAVEFORMATEX* pFormat, Mix::Memory::IBuffer* pBuffer, const Mix::Vector3& forward, const Mix::Vector3& up, Mix::Sound::IEmitter** ppDst );
		Boolean CloneStreamingEmitter( Mix::Sound::Listener* pListener, Mix::IO::IReader* pSrcReader, const Mix::Vector3& forward, const Mix::Vector3& up, Mix::Sound::IEmitter** ppDst );

	public:
		virtual Boolean CreateSimpleController( const wchar_t* pFilePath, Mix::Sound::IController** ppController );
		virtual Boolean CreateSimpleController( Mix::IO::IReader* pReader, Mix::Sound::IController** ppController );
		virtual Boolean CreateStreamingController( const wchar_t* pFilePath, Boolean bBuffered, Mix::Sound::IController** ppController );
		virtual Boolean CreateStreamingController( Mix::IO::IReader* pReader, Mix::Sound::IController** ppController );

		virtual Boolean CreateListener( const Mix::Vector3& localFront, const Mix::Vector3& localUp, Mix::Sound::IListener** ppListener, const wchar_t* pDebugName = NULL );
		virtual Boolean CreateSimpleEmitter( Mix::Sound::IListener* pListener, const wchar_t* pFilePath, const Mix::Vector3& forward, const Mix::Vector3& up, Mix::Sound::IEmitter** ppEmitter );
		virtual Boolean CreateSimpleEmitter( Mix::Sound::IListener* pListener, Mix::IO::IReader* pReader, const Mix::Vector3& forward, const Mix::Vector3& up, Mix::Sound::IEmitter** ppEmitter );
		virtual Boolean CreateStreamingEmitter( Mix::Sound::IListener* pListener, const wchar_t* pFilePath, Boolean bBuffered, const Mix::Vector3& forward, const Mix::Vector3& up, Mix::Sound::IEmitter** ppEmitter );
		virtual Boolean CreateStreamingEmitter( Mix::Sound::IListener* pListener, Mix::IO::IReader* pReader, const Mix::Vector3& forward, const Mix::Vector3& up, Mix::Sound::IEmitter** ppEmitter );

	private:
		static const wchar_t* FAILED_INITIALIZE;
		static const wchar_t* FAILED_LOADPLUGIN;
		static const wchar_t* FAILED_CREATESTATIC;
		static const wchar_t* FAILED_CREATESTREAM;
		static const wchar_t* FAILED_CREATEEMITTER_STATIC;
		static const wchar_t* FAILED_CREATEEMITTER_STREAM;
		static const wchar_t* FAILED_CREATELISTENER;
		static const wchar_t* FAILED_CREATEDISTANCECURVE;
		static const wchar_t* FAILED_CLONESTATIC;
		static const wchar_t* FAILED_CLONESTREAM;

		static const wchar_t* STR_STREAM;
		static const wchar_t* STR_STATIC_EMITTER;
		static const wchar_t* STR_STREAM_EMITTER;

		static const wchar_t* STR_CREATE;
		static const wchar_t* STR_DUPLICATE;

		static const wchar_t* g_XAudio2ResultTextTable[5];
	};

}}
