#include "stdafx.h"

#include <vector>

#include "Mix/IO.h"
#include "Mix/HID.h"
#include "Mix/Sound.h"
#include "Mix/Graphics.h"

#include "Utility/Common.h"

static const UInt32 SCREEN_WIDTH = 1024;
static const UInt32 SCREEN_HEIGHT = 768;

static const UInt32 MAX_SOUNDFILE = 32;

static const Mix::Vector2 TXT_ORIGIN( 16.0f, 48.0f );
static const Float32 TXT_MARGIN = 4.0f;
static const Float32 TXT_BG_PADDING = 2.0f;
static const Float32 TXT_BG_PADDING2 = TXT_BG_PADDING * 2.0f;

class Context
{
private:
	struct INFO
	{
		Mix::Vector2 size;
		Mix::Sound::IController* pController;
	};

	typedef std::vector<Context::INFO> InfoList;

private:
	Mix::Graphics::Utility::IFont* m_pFont;
	Context::InfoList m_InfoList;
	UInt32 m_InfoIndex;
	UInt32 m_CheckCount;

public:
	Context( void ) :
	m_InfoIndex( 0xFFFFFFFF ),
	m_CheckCount( 0 )
	{
	}

	~Context( void )
	{
		MIX_ASSERT( m_pFont == NULL );
		MIX_ASSERT( m_InfoList.size() == 0 );
	}

	void Initialize( Mix::Graphics::Utility::IFont* pFont )
	{
		MIX_ASSERT( pFont != NULL );
		MIX_ASSERT( Mix::GetEnginePtr() != NULL );

		MIX_ADD_REF( pFont );
		m_pFont = pFont;

		Mix::GetEnginePtr()->SetMessageProc( Context::MessageProc, this );
	}

	void Dispose( void )
	{
		MIX_ASSERT( Mix::GetEnginePtr() != NULL );

		Mix::GetEnginePtr()->SetMessageProc( NULL );

		MIX_RELEASE( m_pFont );

		if( m_InfoList.size() > 0 )
		{
			Context::InfoList::iterator it_begin = m_InfoList.begin();
			Context::InfoList::iterator it_end = m_InfoList.end();
			Context::InfoList::iterator it;

			for( it = it_begin; it != it_end; ++it )
			{
				Context::INFO* pInfo = &( *it );
				MIX_RELEASE( pInfo->pController );
			}

			m_InfoList.clear();
		}
	}

	void Update( void )
	{
		if( m_InfoIndex != 0xFFFFFFFF )
		{
			MIX_ASSERT( m_InfoList.size() > m_InfoIndex );

			if( m_InfoList[m_InfoIndex].pController->IsPlaying() == False )
			{
				if( m_CheckCount >= 8 )
				{
					m_InfoIndex = 0xFFFFFFFF;
				}
				else
				{
					m_CheckCount++;
				}
			}
		}
	}

	UInt32 GetNum( void ) const
	{
		return static_cast<UInt32>( m_InfoList.size() );
	}

	Mix::RectangleF GetRect( UInt32 index ) const
	{
		MIX_ASSERT( m_InfoList.size() > index );

		const Mix::Vector2& size = m_InfoList[index].size;
		Mix::RectangleF ret;

		ret.x = TXT_ORIGIN.x;
		ret.y = TXT_ORIGIN.y + ( static_cast<Float32>( m_pFont->GetHeight() ) + TXT_MARGIN ) * static_cast<Float32>( index );
		ret.width = size.x;
		ret.height = size.y;

		return ret;
	}

	const wchar_t* GetFilePath( UInt32 index ) const
	{
		MIX_ASSERT( m_InfoList.size() > index );

		return m_InfoList[index].pController->GetFilePath();
	}

	UInt32 GetActiveIndex( void ) const
	{
		return m_InfoIndex;
	}

	void Switch( UInt32 index )
	{
		MIX_ASSERT( index != 0xFFFFFFFF );
		MIX_ASSERT( m_InfoList.size() > index );

		if( m_InfoIndex == index )
		{
			m_InfoList[m_InfoIndex].pController->Stop();
			m_InfoIndex = 0xFFFFFFFF;
		}
		else
		{
			if( m_InfoIndex != 0xFFFFFFFF )
			{
				m_InfoList[m_InfoIndex].pController->Stop();
			}

			m_InfoList[index].pController->Play( False );
			m_InfoIndex = index;
		}

		m_CheckCount = 0;
	}

private:
	static void __stdcall MessageProc( HWND hWnd, UInt32 message, WPARAM wParam, LPARAM lParam, void* pData )
	{
		Context* pCtx = static_cast<Context*>( pData );

		if( message == WM_DROPFILES )
		{
			HDROP hDrop = ( HDROP )wParam;
			UInt32 fileNum = DragQueryFile( hDrop, 0xFFFFFFFF, NULL, 0 );
			wchar_t temp[4096] = { 0 };

			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( pCtx->m_InfoList.size() > 0 )
			{
				Context::InfoList::iterator it_begin = pCtx->m_InfoList.begin();
				Context::InfoList::iterator it_end = pCtx->m_InfoList.end();
				Context::InfoList::iterator it;

				for( it = it_begin; it != it_end; ++it )
				{
					Context::INFO* pInfo = &( *it );
					MIX_RELEASE( pInfo->pController );
				}

				pCtx->m_InfoList.clear();
			}

			pCtx->m_InfoIndex = 0xFFFFFFFF;
			pCtx->m_CheckCount = 0;

			////////////////////////////////////////////////////////////////////////////////////////////////////

			Mix::IO::IManager* pIOMgr = Mix::IO::GetManagerPtr();
			Mix::Sound::IManager* pSoundMgr = Mix::Sound::GetManagerPtr();

			MIX_ASSERT( pIOMgr != NULL );
			MIX_ASSERT( pSoundMgr != NULL );

			for( UInt32 i = 0; ( i < fileNum ) && ( pCtx->m_InfoList.size() < MAX_SOUNDFILE ); i++ )
			{
				DragQueryFile( hDrop, i, temp, sizeof( temp ) );

				if( pIOMgr->MountFile( temp ) == True )
				{
					Mix::Sound::IController* pController = NULL;

					Mix::IO::IReader* pReader = NULL;

					if( pIOMgr->CreateFileReader( temp, &pReader ) == True )
					{
						Mix::Sound::IController* pController = NULL;

						if( pReader->GetSize() < 1048576 )
						{
							pSoundMgr->CreateSimpleController( pReader, &pController );
						}
						else
						{
							pSoundMgr->CreateStreamingController( pReader, &pController );
						}

						if( pController != NULL )
						{
							Context::INFO info;

							info.pController = pController;
							info.size = Mix::Graphics::Utility::MeasureString( pCtx->m_pFont, pController->GetFilePath() );

							pCtx->m_InfoList.push_back( info );
						}

						MIX_RELEASE( pReader );
					}
				}
			}

			DragFinish( hDrop );

			////////////////////////////////////////////////////////////////////////////////////////////////////
		}
	}
};

int APIENTRY _tWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPTSTR lpCmdLine, int nCmdShow )
{
	Mix::ENGINE_CONFIG engineConfig;

	engineConfig.flags = Mix::EC_IO_NARROW | Mix::EC_MOUSE | Mix::EC_GRAPHICS | Mix::EC_SOUND;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"Sound\\Basic2D" );
	engineConfig.pCaption = L"Sound - Basic2D";
	engineConfig.targetSize = Mix::Point( SCREEN_WIDTH, SCREEN_HEIGHT );
	engineConfig.bAcceptDrop = True;
	engineConfig.shaderModel = Mix::Graphics::SHADER_MODEL_3;

	if( Mix::Initialize( engineConfig ) == True )
	{
		Mix::IEngine* pEngine = Mix::GetEnginePtr();
		Mix::IO::IManager* pIOMgr = Mix::IO::GetManagerPtr();
		Mix::HID::IManager* pInputMgr = Mix::HID::GetManagerPtr();
		Mix::HID::IMouse* pMouse = NULL;
		Mix::Sound::IManager* pSoundMgr = Mix::Sound::GetManagerPtr();
		Mix::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();
		Mix::Graphics::IDevice* pGraphicsDev = NULL;
		Mix::Graphics::Utility::ICanvasRenderer* pCanvasRenderer = NULL;
		Mix::Graphics::Utility::IFont* pFont = NULL;
		Mix::String tempStr;

		Context context;

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pIOMgr->MountDirectory( L"Data" );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pInputMgr->GetMouse( &pMouse );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pGraphicsMgr->GetDevice( &pGraphicsDev );

		if( pGraphicsMgr->CreateCanvasRenderer( &pCanvasRenderer, MIX_DEBUGNAME ) == True )
		{
			if( pGraphicsMgr->CreateFontFromFile( L"Data\\Font\\mspg_16_400.fnt", &pFont ) == True )
			{
				pCanvasRenderer->SetFont( pFont );
			}
		}

		////////////////////////////////////////////////////////////////////////////////////////////////////

		context.Initialize( pFont );

		////////////////////////////////////////////////////////////////////////////////////////////////////

		pEngine->ResetFPS();

		while( pEngine->Update() == True )
		{
			Mix::Vector2 mousePos = pMouse->GetPos();

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// コンテキスト
			////////////////////////////////////////////////////////////////////////////////////////////////////

			context.Update();

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// セレクト
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( context.GetNum() > 0 )
			{
				if( pMouse->GetButtonState( 0 ) & Mix::HID::PRESSED )
				{
					for( UInt32 i = 0; i < context.GetNum(); i++ )
					{
						Mix::RectangleF txtRect = context.GetRect( i );

						if( txtRect.Contains( mousePos ) == True )
						{
							context.Switch( i );
						}
					}
				}
			}

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// キャンバス
			////////////////////////////////////////////////////////////////////////////////////////////////////

			if( context.GetNum() > 0 )
			{
				UInt32 activeIndex = context.GetActiveIndex();
				Mix::Vector4 bgColor;
				Mix::Vector4 txtColor;

				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
				pCanvasRenderer->AddString( 16.0f, 16.0f, L"サウンドファイルをクリックすることで再生、または停止を行うことができます。" );

				for( UInt32 i = 0; i < context.GetNum(); i++ )
				{
					Mix::RectangleF txtRect = context.GetRect( i );
					Mix::RectangleF bgRect( txtRect.x - TXT_BG_PADDING, txtRect.y - TXT_BG_PADDING, txtRect.width + TXT_BG_PADDING2, txtRect.height + TXT_BG_PADDING2 );

					if( txtRect.Contains( mousePos ) == True )
					{
						bgColor.Set( 0.2f, 0.2f, 0.2f, 1.0f );
					}
					else
					{
						bgColor.Set( 0.1f, 0.1f, 0.1f, 1.0f );
					}

					if( i == activeIndex )
					{
						txtColor.Set( 1.0f, 1.0f, 1.0f, 1.0f );
					}
					else
					{
						txtColor.Set( 0.5f, 0.5f, 0.5f, 1.0f );
					}

					pCanvasRenderer->SetColor( bgColor );
					pCanvasRenderer->AddFillRectangle( bgRect );

					pCanvasRenderer->SetColor( txtColor );
					pCanvasRenderer->AddString( txtRect.x, txtRect.y, context.GetFilePath( i ) );
				}
			}
			else
			{
				tempStr.Sprintf( L"サウンドファイルをドラッグアンドドロップしてください。( 最大%d個 )", MAX_SOUNDFILE );
				pCanvasRenderer->SetColor( Mix::Vector4( 1.0f, 1.0f, 1.0f, 1.0f ) );
				pCanvasRenderer->AddString( 16.0f, 16.0f, tempStr.GetConstPtr() );
			}

			Utility::DrawMouseCursor( pCanvasRenderer, pMouse->GetPos() );

			pCanvasRenderer->Update();

			////////////////////////////////////////////////////////////////////////////////////////////////////
			// 描画
			////////////////////////////////////////////////////////////////////////////////////////////////////

			pGraphicsDev->SetViewBounds( 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT );
			pGraphicsDev->Clear( True, False, Mix::Vector4( 0.1f, 0.1f, 0.1f, 1.0f ) );

			if( pGraphicsDev->Begin() == True )
			{
				pCanvasRenderer->Draw();

				pGraphicsDev->End();
			}

			pGraphicsDev->Present();
		}

		context.Dispose();

		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pMouse );

		Mix::Finalize();
	}

	return 0;
}
