#include "Mix/Private/HID/GamepadManager.h"

#include <wbemidl.h>
#include <oleauto.h>

#include "Mix/Private/HID/DX/Gamepad.h"
#include "Mix/Private/HID/XB/Gamepad.h"

namespace Mix{ namespace HID{

const wchar_t* GamepadManager::FAILED_INITIALIZE = L"インプットマネージャの初期化に失敗";

DIOBJECTDATAFORMAT GamepadManager::OBJ_DATA_FORMAT[61] =
{
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 0] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 1] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 2] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 3] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 4] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 5] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 6] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 7] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 8] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[ 9] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[10] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[11] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[12] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[13] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[14] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[15] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[16] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[17] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[18] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[19] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[20] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[21] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[22] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[23] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[24] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[25] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[26] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[27] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[28] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[29] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[30] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },
	{ &GUID_Button, MIX_OFFSETOF( DI_GAMEPAD_DATA, buttons[31] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_BUTTON, 0 },

	{ &GUID_XAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 0] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTPOSITION },
	{ &GUID_XAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 1] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTVELOCITY },
	{ &GUID_XAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 2] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTACCEL },
	{ &GUID_XAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 3] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTFORCE },

	{ &GUID_YAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 4] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTPOSITION },
	{ &GUID_YAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 5] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTVELOCITY },
	{ &GUID_YAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 6] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTACCEL },
	{ &GUID_YAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 7] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTFORCE },

	{ &GUID_ZAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 8] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTPOSITION },
	{ &GUID_ZAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[ 9] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTVELOCITY },
	{ &GUID_ZAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[10] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTACCEL },
	{ &GUID_ZAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[11] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTFORCE },

	{ &GUID_RxAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[12] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTPOSITION },
	{ &GUID_RxAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[13] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTVELOCITY },
	{ &GUID_RxAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[14] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTACCEL },
	{ &GUID_RxAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[15] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTFORCE },

	{ &GUID_RyAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[16] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTPOSITION },
	{ &GUID_RyAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[17] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTVELOCITY },
	{ &GUID_RyAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[18] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTACCEL },
	{ &GUID_RyAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[19] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTFORCE },

	{ &GUID_RzAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[20] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTPOSITION },
	{ &GUID_RzAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[21] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTVELOCITY },
	{ &GUID_RzAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[22] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTACCEL },
	{ &GUID_RzAxis, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[23] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTFORCE },	

	{ &GUID_Slider, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[24] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTPOSITION },
	{ &GUID_Slider, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[25] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTVELOCITY },
	{ &GUID_Slider, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[26] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTACCEL },
	{ &GUID_Slider, MIX_OFFSETOF( DI_GAMEPAD_DATA, axis[27] ), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_AXIS, DIDOI_ASPECTFORCE },

	{ &GUID_POV, MIX_OFFSETOF( DI_GAMEPAD_DATA, pov), DIDFT_OPTIONAL | DIDFT_ANYINSTANCE | DIDFT_POV, 0 },
};

const DIDATAFORMAT GamepadManager::DATA_FORMAT =
{ 
	sizeof( DIDATAFORMAT ),
	sizeof( DIOBJECTDATAFORMAT ),
	DIDF_ABSAXIS,
	sizeof( DI_GAMEPAD_DATA ),
	sizeof( GamepadManager::OBJ_DATA_FORMAT ) / sizeof( DIOBJECTDATAFORMAT ),
	GamepadManager::OBJ_DATA_FORMAT,
};


/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Mix::HID::GamepadManager
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

GamepadManager::GamepadManager( void ) :
m_hWnd( NULL ),
m_pDI( NULL )
{
	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_PORT_MAX; i++ )
	{
		m_pGamepadList[i] = NULL;
	}
}

GamepadManager::~GamepadManager( void )
{
}

Boolean GamepadManager::Start( HWND hWnd, Mix::UserFile* pSysReport )
{
	MIX_ASSERT( hWnd != NULL );

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// XInput
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	m_pGamepadList[Mix::HID::GAMEPAD_XI_A] = XB::Gamepad::CreateInstance( 0 );
	if( m_pGamepadList[Mix::HID::GAMEPAD_XI_A] == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s", FAILED_INITIALIZE, Mix::STR_OUTOFMEMORY );
		return False;
	}

	m_pGamepadList[Mix::HID::GAMEPAD_XI_B] = XB::Gamepad::CreateInstance( 1 );
	if( m_pGamepadList[Mix::HID::GAMEPAD_XI_B] == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s", FAILED_INITIALIZE, Mix::STR_OUTOFMEMORY );
		return False;
	}

	m_pGamepadList[Mix::HID::GAMEPAD_XI_C] = XB::Gamepad::CreateInstance( 2 );
	if( m_pGamepadList[Mix::HID::GAMEPAD_XI_C] == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s", FAILED_INITIALIZE, Mix::STR_OUTOFMEMORY );
		return False;
	}

	m_pGamepadList[Mix::HID::GAMEPAD_XI_D] = XB::Gamepad::CreateInstance( 3 );
	if( m_pGamepadList[Mix::HID::GAMEPAD_XI_D] == NULL )
	{
		MIX_LOG_ERROR( L"%s : %s", FAILED_INITIALIZE, Mix::STR_OUTOFMEMORY );
		return False;
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// DirectInput
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	HRESULT ret;
	ENUMDEVICE enumDev;

	//ウィンドウハンドル
	m_hWnd = hWnd;

	//DirectInput8を作成
	ret = ::CoCreateInstance( CLSID_DirectInput8, NULL, CLSCTX_ALL, IID_IDirectInput8, ( void** )&m_pDI );
	if( ret != S_OK )
	{
		::MessageBox(::GetActiveWindow(), L"DirectX が正しくインストールされていません : DirectInput8(1)", Mix::STR_ERROR, MB_OK | MB_ICONSTOP);
		MIX_LOG_ERROR(L"%s : CoCreateInstance %s", FAILED_INITIALIZE, Mix::STR_RETERROR);
		return False;
	}

	//DirectInput8を初期化
	ret = m_pDI->Initialize( ::GetModuleHandle( NULL ), DIRECTINPUT_VERSION );
	if( ret != DI_OK )
	{
		MIX_LOG_ERROR( L"%s : IDirectInput8::Initialize %s", FAILED_INITIALIZE, Mix::STR_RETERROR );
		return False;
	}

	pSysReport->WriteLine( L"[ゲームパッド一覧]" );

	//列挙
	enumDev.pMgr = this;
	enumDev.pSysReport = pSysReport;
	enumDev.port = Mix::HID::GAMEPAD_DI_A;
	ret = m_pDI->EnumDevices( DI8DEVCLASS_GAMECTRL, GamepadManager::DI_EnumDeviceCallback, &enumDev, DIEDFL_ATTACHEDONLY );
	if( ret != DI_OK )
	{
		MIX_LOG_ERROR( L"%s : IDirectInput8::DI_EnumDevices %s", FAILED_INITIALIZE, Mix::STR_RETERROR );
		return False;
	}

	if( ( m_pGamepadList[Mix::HID::GAMEPAD_DI_A] == NULL ) &&
		( m_pGamepadList[Mix::HID::GAMEPAD_XI_A] == NULL ) )
	{
		pSysReport->WriteLine( L"  ありません" );
	}

	pSysReport->WriteLine( L"" );

	//空きを埋める
	for( UInt32 i = Mix::HID::GAMEPAD_DI_A; i <= Mix::HID::GAMEPAD_DI_D; i++ )
	{
		if( m_pGamepadList[i] == NULL )
		{
			m_pGamepadList[i] = Mix::HID::NullGamepad::CreateInstance();
			if( m_pGamepadList[i] == NULL )
			{
				MIX_LOG_ERROR( L"%s : %s", FAILED_INITIALIZE, Mix::STR_OUTOFMEMORY );
				return False;
			}
		}
	}

	return True;
}

void GamepadManager::Terminate( void )
{
	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_PORT_MAX; i++ )
	{
		if( m_pGamepadList[i] != NULL )
		{
			m_pGamepadList[i]->Dispose();
			MIX_RELEASE( m_pGamepadList[i] );
		}
	}

	MIX_RELEASE( m_pDI );
}

void GamepadManager::Update( void )
{
	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_PORT_MAX; i++ )
	{
		if( m_pGamepadList[i] != NULL )
		{
			m_pGamepadList[i]->Update();
		}
	}
}

Boolean GamepadManager::Get( UInt32 port, Mix::HID::IGamepad** ppGamepad )
{
	if( ( port >= Mix::HID::GAMEPAD_PORT_MAX ) ||
		( m_pGamepadList[port] == NULL ) )
	{
		return False;
	}

	Mix::HID::Gamepad* pGamepad = m_pGamepadList[port];

	MIX_ADD_REF( pGamepad );
	( *ppGamepad ) = pGamepad;

	return True;
}

Boolean GamepadManager::Find( Mix::HID::GAMEPAD_API api, Mix::HID::IGamepad** ppGamepad )
{
	UInt32 port = ( api == Mix::HID::GAMEPAD_DIRECTINPUT )? Mix::HID::GAMEPAD_DI_A : Mix::HID::GAMEPAD_XI_A;
	Mix::HID::Gamepad* pGamepad = NULL;

	for( UInt32 i = 0; ( i < Mix::HID::GAMEPAD_PORT_MAX ) && ( pGamepad == NULL ); i++ )
	{
		Mix::HID::Gamepad* pTempGamepad = m_pGamepadList[port];

		if( ( pTempGamepad != NULL ) &&
			( pTempGamepad->IsAvailable() == True ) )
		{
			pGamepad = pTempGamepad;
		}
		else
		{
			port = ( port + 1 ) % Mix::HID::GAMEPAD_PORT_MAX;
		}
	}

	if( pGamepad == NULL )
	{
		return False;
	}

	MIX_ADD_REF( pGamepad );
	( *ppGamepad ) = pGamepad;

	return True;
}

Boolean GamepadManager::CheckXInputDevice( const GUID& productGuid )
{
	IWbemLocator* pIWbemLocator = NULL;
	IEnumWbemClassObject* pEnumDevices = NULL;
	IWbemClassObject* pDevices[20] = { 0 };
	IWbemServices* pIWbemServices = NULL;
	BSTR bstrNamespace = NULL;
	BSTR bstrDeviceID = NULL;
	BSTR bstrClassName = NULL;
	DWORD uReturned = 0;
	Boolean bIsXinputDevice = False;
	Boolean bCleanupCOM = False;
	UINT iDevice = 0;
	VARIANT var;
	HRESULT hr;

	hr = ::CoInitialize( NULL );
	bCleanupCOM = SUCCEEDED( hr );

	hr = CoCreateInstance(	__uuidof( WbemLocator ),
							NULL,
							CLSCTX_INPROC_SERVER,
							__uuidof( IWbemLocator ),
							( LPVOID* )&pIWbemLocator );
	if( ( FAILED( hr ) ) ||
		( pIWbemLocator == NULL ) )
	{
		goto LCleanup;
	}

	bstrNamespace = SysAllocString( L"\\\\.\\root\\cimv2" );
	if( bstrNamespace == NULL )
	{
		goto LCleanup;
	}

	bstrClassName = SysAllocString( L"Win32_PNPEntity" );
	if( bstrClassName == NULL )
	{
		goto LCleanup;
	}

	bstrDeviceID = SysAllocString( L"DeviceID" );
	if( bstrDeviceID == NULL )
	{
		goto LCleanup;
	}

	hr = pIWbemLocator->ConnectServer( bstrNamespace, NULL, NULL, 0L, 0L, NULL, NULL, &pIWbemServices );
	if( ( FAILED( hr ) ) ||
		( pIWbemServices == NULL ) )
	{
		goto LCleanup;
	}

	::CoSetProxyBlanket(	pIWbemServices,
							RPC_C_AUTHN_WINNT,
							RPC_C_AUTHZ_NONE,
							NULL,
							RPC_C_AUTHN_LEVEL_CALL,
							RPC_C_IMP_LEVEL_IMPERSONATE,
							NULL,
							EOAC_NONE );

	hr = pIWbemServices->CreateInstanceEnum( bstrClassName, 0, NULL, &pEnumDevices );
	if( ( FAILED( hr ) ) ||
		( pEnumDevices == NULL ) )
	{
		goto LCleanup;
	}

	for( ;; )
	{
		hr = pEnumDevices->Next( 10000, 20, pDevices, &uReturned );

		if( FAILED( hr ) )
		{
			goto LCleanup;
		}

		if( uReturned == 0 )
		{
			break;
		}

		for( iDevice = 0; iDevice < uReturned; iDevice++ )
		{
			// For each device, get its device ID
			hr = pDevices[iDevice]->Get( bstrDeviceID, 0L, &var, NULL, NULL );
			if( ( SUCCEEDED( hr ) ) &&
				( var.vt == VT_BSTR ) &&
				( var.bstrVal != NULL ) )
			{
				// Check if the device ID contains "IG_".  If it does, then it's an XInput device
				// This information can not be found from DirectInput
				if( ::wcsstr( var.bstrVal, L"IG_" ) )
				{
					// If it does, then get the VID/PID from var.bstrVal
					DWORD dwPid = 0;
					DWORD dwVid = 0;

					WCHAR* strVid = ::wcsstr( var.bstrVal, L"VID_" );
					if( ( strVid != NULL ) &&
						( ::swscanf_s( strVid, L"VID_%4X", &dwVid ) != 1 ) )
					{
						dwVid = 0;
					}

					WCHAR* strPid = ::wcsstr( var.bstrVal, L"PID_" );
					if( ( strPid != NULL ) &&
						( ::swscanf_s( strPid, L"PID_%4X", &dwPid ) != 1 ) )
					{
						dwPid = 0;
					}

					// Compare the VID/PID to the DHID device
					DWORD dwVidPid = MAKELONG( dwVid, dwPid );
					if( dwVidPid == productGuid.Data1 )
					{
						bIsXinputDevice = True;
						goto LCleanup;
					}
				}
			}

			MIX_RELEASE( pDevices[iDevice] );
		}
	}

LCleanup:
	if( bstrNamespace != NULL )
	{
		::SysFreeString( bstrNamespace );
	}

	if( bstrDeviceID != NULL )
	{
		::SysFreeString( bstrDeviceID );
	}

	if( bstrClassName != NULL )
	{
		::SysFreeString( bstrClassName );
	}

	for( iDevice = 0; iDevice < 20; iDevice++ )
	{
		MIX_RELEASE( pDevices[iDevice] );
	}

	MIX_RELEASE( pEnumDevices );
	MIX_RELEASE( pIWbemLocator );
	MIX_RELEASE( pIWbemServices );

	if( bCleanupCOM == True )
	{
		::CoUninitialize();
	}

	return bIsXinputDevice;
}

BOOL CALLBACK GamepadManager::DI_EnumDeviceCallback( LPCDIDEVICEINSTANCE lpddi, LPVOID pvRef )
{
	ENUMDEVICE* pEnumDev = static_cast<ENUMDEVICE*>( pvRef );

	if( CheckXInputDevice( lpddi->guidProduct ) == True )
	{
		//XInputデバイスは列挙しない
		pEnumDev->pSysReport->WriteLine( L"  [%s]", lpddi->tszProductName );
		pEnumDev->pSysReport->WriteLine( L"    タイプ : XInput" );
		pEnumDev->pSysReport->WriteLine( L"    インスタンス名 : %s", lpddi->tszInstanceName );
		return DIENUM_CONTINUE;
	}
	else
	{
		pEnumDev->pSysReport->WriteLine( L"  [%s]", lpddi->tszProductName );
		pEnumDev->pSysReport->WriteLine( L"    タイプ : DirectInput" );
		pEnumDev->pSysReport->WriteLine( L"    インスタンス名 : %s", lpddi->tszInstanceName );
	}

	GamepadManager* pMgr = pEnumDev->pMgr;
	LPDIRECTINPUTDEVICE8 pDevice;
	DIDEVCAPS devCaps;
	ENUMAXIS enumAxis;

	//デバイスを作成する
	if( pMgr->m_pDI->CreateDevice( lpddi->guidInstance, &pDevice, NULL ) != DI_OK )
	{
		return DIENUM_CONTINUE;
	}

	//デバイスの能力を取得する
	::ZeroMemory( &devCaps, sizeof( devCaps ) );
	devCaps.dwSize = sizeof( devCaps );
	if( pDevice->GetCapabilities( &devCaps ) != DI_OK )
	{
		MIX_RELEASE( pDevice );
		return DIENUM_CONTINUE;
	}

	//除外処理
	if( ( Mix::HID::DI_AXIS_MIN > devCaps.dwAxes ) ||
		( Mix::HID::DI_BUTTON_MIN > devCaps.dwButtons ) )
	{
		MIX_RELEASE( pDevice );
		return DIENUM_CONTINUE;
	}

	//デバイスの強調レベルを設定
	if( pDevice->SetCooperativeLevel( pMgr->m_hWnd, DISCL_EXCLUSIVE | DISCL_FOREGROUND ) != DI_OK )
	{
		MIX_RELEASE( pDevice );
		return DIENUM_CONTINUE;
	}

	//デバイスのデータフォーマットの設定
	if( pDevice->SetDataFormat( &GamepadManager::DATA_FORMAT ) != DI_OK )
	{
		MIX_RELEASE( pDevice );
		return DIENUM_CONTINUE;
	}

	//デバイスの軸を設定
	enumAxis.pDevice = pDevice;
	enumAxis.bSuccess = True;
	if( ( pDevice->EnumObjects( GamepadManager::DI_EnumAxisCallback, &enumAxis, DIDFT_AXIS ) != DI_OK ) ||
		( enumAxis.bSuccess == False ) )
	{
		MIX_RELEASE( pDevice );
		return DIENUM_CONTINUE;
	}

	//インターフェースの作成
	pMgr->m_pGamepadList[pEnumDev->port] = DX::Gamepad::CreateInstance( pDevice, MIX_TESTBIT( devCaps.dwFlags, DIDC_POLLEDDATAFORMAT ) == DIDC_POLLEDDATAFORMAT );
	if( pMgr->m_pGamepadList[pEnumDev->port] == NULL )
	{
		MIX_RELEASE( pDevice );
		return DIENUM_CONTINUE;
	}

	//次のポートへ
	pEnumDev->port++;

	return ( pEnumDev->port <= Mix::HID::GAMEPAD_DI_D )? DIENUM_CONTINUE : DIENUM_STOP;
}

BOOL CALLBACK GamepadManager::DI_EnumAxisCallback( LPCDIDEVICEOBJECTINSTANCE lpddoi, LPVOID pvRef )
{
	GamepadManager::ENUMAXIS* pEnumAxis = static_cast<GamepadManager::ENUMAXIS*>( pvRef );
	BOOL bRet = DIENUM_CONTINUE;

	DIPROPRANGE dipr;
	DIPROPDWORD dipd;

	//軸の範囲を設定
	if( ( lpddoi->dwType & DIDFT_ABSAXIS ) == DIDFT_ABSAXIS )
	{
		::ZeroMemory( &dipr, sizeof( dipr ) );
		dipr.diph.dwSize = sizeof( DIPROPRANGE );
		dipr.diph.dwHeaderSize = sizeof( DIPROPHEADER );
		dipr.diph.dwHow = DIPH_BYID;
		dipr.diph.dwObj = lpddoi->dwType;
		dipr.lMin = -32768;
		dipr.lMax = 32767;
		if( pEnumAxis->pDevice->SetProperty( DIPROP_RANGE, &( dipr.diph ) ) != DI_OK )
		{
			pEnumAxis->bSuccess = False;
			bRet = DIENUM_STOP;
		}
	}

	//軸のデッドゾーンを設定
	::ZeroMemory( &dipd, sizeof( dipd ) );
	dipd.diph.dwSize = sizeof( DIPROPDWORD );
	dipd.diph.dwHeaderSize = sizeof( DIPROPHEADER );
	dipd.diph.dwHow = DIPH_BYID;
	dipd.diph.dwObj = lpddoi->dwType;
	dipd.dwData = 0;
	if( pEnumAxis->pDevice->SetProperty( DIPROP_DEADZONE, &( dipd.diph ) ) != DI_OK )
	{
		pEnumAxis->bSuccess = False;
		bRet = DIENUM_STOP;
	}

	return bRet;
}

}}
