#include "stdafx.h"

#include "Mix/IO.h"
#include "Mix/Graphics.h"
#include "Mix/HID.h"
#include "Utility/Common.h"

#define SCREEN_WIDTH 1024
#define SCREEN_HEIGHT 768

struct BUTTON_INFO
{
	Mix::String name;
	Mix::HID::GAMEPAD_BUTTON index;
};

struct TRIGGER_INFO
{
	Mix::String name;
	Mix::HID::GAMEPAD_CONSTANT index;
};

static const Mix::Vector2 MAIN_DISP_POS( 256.0f, 256.0f );
static const Mix::Vector2 MAIN_DISP_MARGIN( 2.0f, 2.0f );
static const Mix::Vector2 MAIN_DISP_MARGIN2( MAIN_DISP_MARGIN * 2.0f );

static const Mix::Vector2 BUTTON_DISP_POS( 0.0f, 0.0f );
static const Mix::Vector2 BUTTON_DISP_SIZE( 128.0f, 24.0f );

static const Mix::Vector2 TRIGGER_DISP_POS( 0.0f, 130.0f );
static const Mix::Vector2 TRIGGER_DISP_SIZE( 128.0f, 128.0f );

static const Mix::Vector2 STICK_DISP_POS( 260.0f, 130.0f );
static const Mix::Vector2 STICK_DISP_SIZE( 128.0f, 128.0f );
static const Mix::Vector2 STICK_DISP_MARGIN( 4.0f, 4.0f );
static const Mix::Vector2 STICK_CENTER_DISP_SIZE( 8.0f, 8.0f );
static const Mix::Vector2 STICK_POINTER_DISP_SIZE( 4.0f, 4.0f );

static const Mix::Vector4 BORDER_COLOR( 0.8f, 0.8f, 0.8f, 1.0f );
static const Mix::Vector4 DISABLED_BORDER_COLOR( 0.4f, 0.4f, 0.4f, 1.0f );
static const Mix::Vector4 BACKGROUND_COLOR( 0.15f, 0.15f, 0.15f, 1.0f );
static const Mix::Vector4 TEXT_COLOR( 1.0f, 1.0f, 1.0f, 1.0f );
static const Mix::Vector4 DISABLED_TEXT_COLOR( 0.2f, 0.2f, 0.2f, 1.0f );
static const Mix::Vector4 TRIGGER_COLOR( 0.3f, 1.0f, 0.3f );
static const Mix::Vector4 STICK_CENTER_COLOR( 0.3f, 0.3f, 0.3f );
static const Mix::Vector4 STICK_POINTER_COLOR( 1.0f, 0.3f, 0.3f );

static const BUTTON_INFO BUTTON_INFO_TABLE[] = 
{
	{ L"POV-UP", Mix::HID::GAMEPAD_POV_UP },
	{ L"POV-DOWN", Mix::HID::GAMEPAD_POV_DOWN },
	{ L"POV-LEFT", Mix::HID::GAMEPAD_POV_LEFT },
	{ L"POV-RIGHT", Mix::HID::GAMEPAD_POV_RIGHT },

	{ L"BACK", Mix::HID::GAMEPAD_BACK },
	{ L"START", Mix::HID::GAMEPAD_START },
	{ L"", Mix::HID::GAMEPAD_BUTTON_MAX },
	{ L"", Mix::HID::GAMEPAD_BUTTON_MAX },

	{ L"STICK-LEFT", Mix::HID::GAMEPAD_STICK_LEFT },
	{ L"STICK-RIGHT", Mix::HID::GAMEPAD_STICK_RIGHT },
	{ L"", Mix::HID::GAMEPAD_BUTTON_MAX },
	{ L"", Mix::HID::GAMEPAD_BUTTON_MAX },

	{ L"SHOULDER-LEFT", Mix::HID::GAMEPAD_SHOULDER_LEFT },
	{ L"SHOULDER-RIGHT", Mix::HID::GAMEPAD_SHOULDER_RIGHT },
	{ L"", Mix::HID::GAMEPAD_BUTTON_MAX },
	{ L"", Mix::HID::GAMEPAD_BUTTON_MAX },

	{ L"A", Mix::HID::GAMEPAD_A },
	{ L"B", Mix::HID::GAMEPAD_B },
	{ L"X", Mix::HID::GAMEPAD_X },
	{ L"Y", Mix::HID::GAMEPAD_Y },
};

static const TRIGGER_INFO TRIGGER_INFO_TABLE[2] =
{
	{ L"TRIGGER-LEFT", Mix::HID::GAMEPAD_LEFT },
	{ L"TRIGGER-RIGHT", Mix::HID::GAMEPAD_RIGHT },
};

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

	engineConfig.flags = Mix::EC_IO_NARROW | Mix::EC_KEYBOARD | Mix::EC_GAMEPAD | Mix::EC_GRAPHICS;
	engineConfig.pRootDirectoryPath = Utility::GetRootDirectoryPath();
	engineConfig.pPluginDirectoryPath = Utility::GetPluginsDirectoryPath();
	engineConfig.pUserDirectoryPath = Utility::GetUserDirectoryPath( L"HID\\Gamepad" );
	engineConfig.pCaption = L"HID - Gamepad";
	engineConfig.targetSize = Mix::Point( SCREEN_WIDTH, SCREEN_HEIGHT );
	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* pHIDMgr = Mix::HID::GetManagerPtr();
		Mix::HID::IGamepad* pGamepadList[Mix::HID::GAMEPAD_PORT_MAX];
		Mix::HID::IKeyboard* pKeyboard = NULL;
		Mix::Graphics::IManager* pGraphicsMgr = Mix::Graphics::GetManagerPtr();
		Mix::Graphics::IDevice* pGraphicsDev = NULL;
		Mix::Graphics::Utility::ICanvasRenderer* pCanvasRenderer = NULL;
		Mix::Graphics::Utility::IFont* pFont = NULL;

		UInt32 gamepadNum = 0;
		UInt32 gamepadIndex = 0xFFFFFFFF;

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

		pIOMgr->MountDirectory( L"Data" );

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

		/*
			キーボードの取得
		*/

		pHIDMgr->GetKeyboard( &pKeyboard );

		/*
			ゲームパッドの列挙
		*/

		for( UInt32 i = 0; i < Mix::HID::GAMEPAD_PORT_MAX; i++ )
		{
			Mix::HID::IGamepad* pGamepad;

			if( pHIDMgr->GetGamepad( i, &pGamepad ) == True )
			{
				if( pGamepad->IsAvailable() == True )
				{
					pGamepadList[i] = pGamepad;

					if( gamepadIndex == 0xFFFFFFFF )
					{
						gamepadIndex = gamepadNum;
					}

					gamepadNum++;
				}
				else
				{
					pGamepadList[i] = NULL;
					MIX_RELEASE( pGamepad );
				}
			}
			else
			{
				pGamepadList[i] = NULL;
			}
		}

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

		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 );
			}
		}

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

		Mix::String tempStr;
		Mix::String apiName;

		while( pEngine->Update() == True )
		{
			////////////////////////////////////////////////////////////////////////////////////////////////////

			for( UInt32 i = 0; i < Mix::HID::GAMEPAD_PORT_MAX; i++ )
			{
				Mix::HID::KEY key = static_cast<Mix::HID::KEY>( Mix::HID::KEY_1 + i );
				Mix::HID::KEY npKey = static_cast<Mix::HID::KEY>( Mix::HID::KEY_NP1 + i );

				if( ( pKeyboard->GetKeyState( key ) & Mix::HID::PRESSED ) ||
					( pKeyboard->GetKeyState( npKey ) & Mix::HID::PRESSED ) )
				{
					if( pGamepadList[i] != NULL )
					{
						gamepadIndex = i;
						break;
					}
				}
			}

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

			pCanvasRenderer->SetColor( TEXT_COLOR );
			pCanvasRenderer->AddString( 16.0f, 16.0f, L"キーの 1〜8 でゲームパッドを選択します。" );

			for( UInt32 i = 0; i < Mix::HID::GAMEPAD_PORT_MAX; i++ )
			{
				Mix::HID::IGamepad* pGamepad = pGamepadList[i];
				UInt32 port = i + 1;

				if( pGamepad != NULL )
				{
					switch( pGamepad->GetAPI() )
					{
					case Mix::HID::GAMEPAD_XINPUT:
						apiName.Sprintf( L"XHID_%d", i % 4);
						break;
					case Mix::HID::GAMEPAD_DIRECTINPUT:
						apiName.Sprintf( L"DirectHID_%d", i % 4);
						break;
					}
				}
				else
				{
					apiName = L"-----------";
				}

				tempStr.Sprintf( L"%d : %s", port, apiName.GetConstPtr() );

				if( i == gamepadIndex )
				{
					pCanvasRenderer->SetColor( TEXT_COLOR );
				}
				else
				{
					pCanvasRenderer->SetColor( Mix::Vector4( 0.5f, 0.5f, 0.5f, 1.0f ) );
				}

				pCanvasRenderer->AddString( 16.0f, 48.0f + static_cast<Float32>( i * 16 ), tempStr.GetConstPtr() );
			}

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

			Mix::HID::IGamepad* pGamepad = ( gamepadNum > 0 )? pGamepadList[gamepadIndex] : NULL;

			/*
				ボタン
			*/

			for( UInt32 row = 0; row < 5; row++ )
			{
				for( UInt32 col = 0; col < 4; col++ )
				{
					UInt32 infoIndex = row * 4 + col;
					const BUTTON_INFO& info = BUTTON_INFO_TABLE[infoIndex];
					Mix::RectangleF bounds;

					bounds.x = MAIN_DISP_POS.x + BUTTON_DISP_POS.x + MAIN_DISP_MARGIN.x + ( BUTTON_DISP_SIZE.x + MAIN_DISP_MARGIN.x ) * static_cast<Float32>( col );
					bounds.y = MAIN_DISP_POS.y + BUTTON_DISP_POS.y + MAIN_DISP_MARGIN.y + ( BUTTON_DISP_SIZE.y + MAIN_DISP_MARGIN.y ) * static_cast<Float32>( row );
					bounds.width = BUTTON_DISP_SIZE.x;
					bounds.height = BUTTON_DISP_SIZE.y;

					if( info.name.GetNum() > 0 )
					{
						Mix::Vector2 nameSize = Mix::Graphics::Utility::MeasureString( pFont, info.name.GetConstPtr() );
						Mix::Vector2 namePos;

						namePos.x = bounds.x + ( bounds.width - nameSize.x ) * 0.5f;
						namePos.y = bounds.y + ( bounds.height - nameSize.y ) * 0.5f;

						pCanvasRenderer->SetColor( BACKGROUND_COLOR );
						pCanvasRenderer->AddFillRectangle( bounds );

						if( ( pGamepad != NULL ) &&
							( pGamepad->GetButtonState( info.index ) & Mix::HID::DOWN ) )
						{
							pCanvasRenderer->SetColor( TEXT_COLOR );
						}
						else
						{
							pCanvasRenderer->SetColor( DISABLED_TEXT_COLOR );
						}

						pCanvasRenderer->AddString( namePos, info.name.GetConstPtr() );
					}

					pCanvasRenderer->SetColor( ( pGamepad != NULL )? BORDER_COLOR : DISABLED_BORDER_COLOR );
					pCanvasRenderer->AddRectangle( bounds );
				}
			}

			/*
				トリガー
			*/

			for( UInt32 col = 0; col < 2; col++ )
			{
				const TRIGGER_INFO& info = TRIGGER_INFO_TABLE[col];
				Mix::Vector2 nameSize = Mix::Graphics::Utility::MeasureString( pFont, info.name.GetConstPtr() );
				Float32 ratio = ( pGamepad != NULL )? pGamepad->GetTriggerState( info.index ) : 0.0f;

				Mix::RectangleF bounds;
				Mix::Vector2 namePos;

				bounds.x = MAIN_DISP_POS.x + TRIGGER_DISP_POS.x + MAIN_DISP_MARGIN.x + ( TRIGGER_DISP_SIZE.x + MAIN_DISP_MARGIN.x ) * static_cast<Float32>( col );
				bounds.y = MAIN_DISP_POS.y + TRIGGER_DISP_POS.y + MAIN_DISP_MARGIN2.y;
				bounds.width = TRIGGER_DISP_SIZE.x;
				bounds.height = TRIGGER_DISP_SIZE.y;

				namePos.x = bounds.x + ( bounds.width - nameSize.x ) * 0.5f;
				namePos.y = bounds.y + ( bounds.height - nameSize.y ) * 0.5f;

				pCanvasRenderer->SetColor( BACKGROUND_COLOR );
				pCanvasRenderer->AddFillRectangle( bounds );

				if( ratio > 0.0f )
				{
					Float32 triHeight = TRIGGER_DISP_SIZE.y * ( 1.0f - ratio );
					Mix::RectangleF triBounds( bounds.x, bounds.y + triHeight, bounds.width, bounds.height - triHeight );

					pCanvasRenderer->SetColor( TRIGGER_COLOR );
					pCanvasRenderer->AddFillRectangle( triBounds );

					pCanvasRenderer->SetColor( TEXT_COLOR );
					pCanvasRenderer->AddString( namePos, info.name.GetConstPtr() );
				}
				else
				{
					pCanvasRenderer->SetColor( DISABLED_TEXT_COLOR );
					pCanvasRenderer->AddString( namePos, info.name.GetConstPtr() );
				}

				pCanvasRenderer->SetColor( ( pGamepad != NULL )? BORDER_COLOR : DISABLED_BORDER_COLOR );
				pCanvasRenderer->AddRectangle( bounds );
			}

			/*
				スティック
			*/

			for( UInt32 col = 0; col < 2; col++ )
			{
				Mix::Vector2 center;
				Mix::Vector2 move;
				Mix::Vector2 pointer;
				Mix::RectangleF bounds;
				Mix::RectangleF centerBounds;
				Mix::RectangleF pointerBounds;

				move.x = ( STICK_DISP_SIZE.x * 0.5f - STICK_POINTER_DISP_SIZE.x );
				move.y = ( STICK_DISP_SIZE.y * 0.5f - STICK_POINTER_DISP_SIZE.y );

				bounds.x = MAIN_DISP_POS.x + STICK_DISP_POS.x + MAIN_DISP_MARGIN.x + ( STICK_DISP_SIZE.x + MAIN_DISP_MARGIN.x ) * static_cast<Float32>( col );
				bounds.y = MAIN_DISP_POS.y + STICK_DISP_POS.y + MAIN_DISP_MARGIN2.y;
				bounds.width = STICK_DISP_SIZE.x;
				bounds.height = STICK_DISP_SIZE.y;

				center.x = ( bounds.x + STICK_DISP_SIZE.x * 0.5f );
				center.y = ( bounds.y + STICK_DISP_SIZE.y * 0.5f );

				if( pGamepad != NULL )
				{
					Mix::Vector2 move;

					move.x = ( STICK_DISP_SIZE.x * 0.5f - STICK_POINTER_DISP_SIZE.x - STICK_DISP_MARGIN.x );
					move.y = ( STICK_DISP_SIZE.y * 0.5f - STICK_POINTER_DISP_SIZE.y - STICK_DISP_MARGIN.y );

					pointer = move * pGamepad->GetStickState( col );
				}
				else
				{
					pointer.x = 0.0f;
					pointer.y = 0.0f;
				}

				centerBounds.x = center.x - STICK_CENTER_DISP_SIZE.x;
				centerBounds.y = center.y - STICK_CENTER_DISP_SIZE.y;
				centerBounds.width = STICK_CENTER_DISP_SIZE.x * 2.0f;
				centerBounds.height = STICK_CENTER_DISP_SIZE.y * 2.0f;

				pointerBounds.x = center.x + pointer.x - STICK_POINTER_DISP_SIZE.x;
				pointerBounds.y = center.y + pointer.y - STICK_POINTER_DISP_SIZE.y;
				pointerBounds.width = STICK_POINTER_DISP_SIZE.x * 2.0f;
				pointerBounds.height = STICK_POINTER_DISP_SIZE.y * 2.0f;

				pCanvasRenderer->SetColor( BACKGROUND_COLOR );
				pCanvasRenderer->AddFillRectangle( bounds );

				pCanvasRenderer->SetColor( STICK_CENTER_COLOR );
				pCanvasRenderer->AddFillRectangle( centerBounds );

				pCanvasRenderer->SetColor( STICK_POINTER_COLOR );
				pCanvasRenderer->AddFillRectangle( pointerBounds );

				pCanvasRenderer->SetColor( ( pGamepad != NULL )? BORDER_COLOR : DISABLED_BORDER_COLOR );
				pCanvasRenderer->AddRectangle( bounds );
			}

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

			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();
		}

		MIX_RELEASE( pFont );
		MIX_RELEASE( pCanvasRenderer );
		MIX_RELEASE( pGraphicsDev );
		MIX_RELEASE( pKeyboard );

		for( UInt32 i = 0; i < Mix::HID::GAMEPAD_PORT_MAX; i++ )
		{
			MIX_RELEASE( pGamepadList[i] );
		}

		Mix::Finalize();
	}

	return 0;
}
