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

namespace Mix{ namespace HID{ namespace DX{

Gamepad* Gamepad::CreateInstance( LPDIRECTINPUTDEVICE8 pDevice, Boolean bPolling )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GENERAL, Gamepad, pDevice, bPolling );
}

Gamepad::Gamepad( LPDIRECTINPUTDEVICE8 pDevice, Boolean bPolling ) :
m_pDevice( pDevice ),
m_bPolling( bPolling ),
m_Status( Gamepad::STATUS_ACQUIRE ),
m_bAvailable( True )
{
	MIX_ASSERT( pDevice != NULL );
	MIX_ADD_REF( m_pDevice );

	Mix::Memory::Zero( &m_Data, sizeof( m_Data ) );
	m_Data.pov = 0xFFFFFFFF;

	//POV : 値は m_PovListの参照インデックス
	m_ButtonRefTable[Mix::HID::GAMEPAD_POV_UP] = 0;
	m_ButtonRefTable[Mix::HID::GAMEPAD_POV_DOWN] = 4;
	m_ButtonRefTable[Mix::HID::GAMEPAD_POV_LEFT] = 6;
	m_ButtonRefTable[Mix::HID::GAMEPAD_POV_RIGHT] = 2;

	//BUTTON : 値は m_ButtonListの参照インデックス
	m_ButtonRefTable[Mix::HID::GAMEPAD_START] = 11;
	m_ButtonRefTable[Mix::HID::GAMEPAD_BACK] = 10;
	m_ButtonRefTable[Mix::HID::GAMEPAD_STICK_LEFT] = 8;
	m_ButtonRefTable[Mix::HID::GAMEPAD_STICK_RIGHT] = 9;
	m_ButtonRefTable[Mix::HID::GAMEPAD_SHOULDER_LEFT] = 4;
	m_ButtonRefTable[Mix::HID::GAMEPAD_SHOULDER_RIGHT] = 5;
	m_ButtonRefTable[Mix::HID::GAMEPAD_A] = 2;
	m_ButtonRefTable[Mix::HID::GAMEPAD_B] = 3;
	m_ButtonRefTable[Mix::HID::GAMEPAD_X] = 0;
	m_ButtonRefTable[Mix::HID::GAMEPAD_Y] = 1;

	//TRIGGER : 値は m_ButtonListの参照インデックス
	m_TriggerRefTable[Mix::HID::GAMEPAD_LEFT] = 6;
	m_TriggerRefTable[Mix::HID::GAMEPAD_RIGHT] = 7;

	for( UInt32 i = 0; i < ( Mix::HID::DI_BUTTON_MAX * 2 ); i++ )
	{
		m_ButtonList[i] = 0;
	}

	for( UInt32 i = 0; i < ( Mix::HID::DI_POV_MAX * 2 ); i++ )
	{
		m_PovList[i] = 0;
	}
}

Gamepad::~Gamepad( void )
{
}

void Gamepad::Update( void )
{
	if( m_pDevice == NULL )
	{
		return;
	}

	HRESULT hRet;
	DI_GAMEPAD_DATA data;

	switch( m_Status )
	{
	case Gamepad::STATUS_ACQUIRE:
		//使用できる状態にする
		hRet = m_pDevice->Acquire();
		if( hRet == DI_OK )
		{
			RestoreState();
			m_Status = Gamepad::STATUS_POLL;
			m_bAvailable = True;
		}
		break;

	case Gamepad::STATUS_POLL:
		//必要な場合はポーリングする
		if( m_bPolling == True )
		{
			hRet = m_pDevice->Poll();
			if( ( hRet != DI_OK ) &&
				( hRet != DI_NOEFFECT ) )
			{
				m_Status = Gamepad::STATUS_ACQUIRE;
			}
		}

		//デバイスの状態を取得する
		hRet = m_pDevice->GetDeviceState( sizeof( data ), &data );
		if( hRet == DI_OK )
		{
			UpdateState( &data );
		}
		else
		{
			m_Status = Gamepad::STATUS_ACQUIRE;
		}

		//ロストしている場合は、ステートを退避
		if( m_Status == Gamepad::STATUS_ACQUIRE )
		{
			LostState();
			m_bAvailable = False;
		}
		break;
	}
}

void Gamepad::Dispose( void )
{
	if( m_pDevice != NULL )
	{
		m_pDevice->Unacquire();
		MIX_RELEASE( m_pDevice );
	}

	Mix::Memory::Zero( &m_Data, sizeof( m_Data ) );

	for( UInt32 i = 0; i < ( Mix::HID::DI_BUTTON_MAX * 2 ); i++ )
	{
		m_ButtonList[i] = 0;
	}

	for( UInt32 i = 0; i < ( Mix::HID::DI_POV_MAX * 2 ); i++ )
	{
		m_PovList[i] = 0;
	}

	m_Stick[Mix::HID::GAMEPAD_LEFT] = Mix::Vector2::Zero();
	m_Stick[Mix::HID::GAMEPAD_RIGHT] = Mix::Vector2::Zero();

	m_Status = Gamepad::STATUS_FINISH;

	m_bAvailable = False;
}

UInt32 Gamepad::GetButtonState( UInt32 index ) const
{
	if( index >= Mix::HID::GAMEPAD_BUTTON_MAX )
	{
		return 0;
	}

	UInt32 state = 0;

	switch( index )
	{
	case Mix::HID::GAMEPAD_POV_UP:
	case Mix::HID::GAMEPAD_POV_DOWN:
	case Mix::HID::GAMEPAD_POV_LEFT:
	case Mix::HID::GAMEPAD_POV_RIGHT:
		state = GetPovState( m_ButtonRefTable[index] );
		break;

	default:
		state = m_ButtonList[m_ButtonRefTable[index]];
	}

	return state;
}

Float32 Gamepad::GetTriggerState( UInt32 index ) const
{
	if( index >= Mix::HID::GAMEPAD_FINGER_MAX )
	{
		return 0.0f;
	}

	return ( MIX_TESTBIT( m_ButtonList[m_TriggerRefTable[index]], Mix::HID::DOWN ) == Mix::HID::DOWN )? 1.0f : 0.0f;
}

const Mix::Vector2& Gamepad::GetStickState( UInt32 index ) const
{
	if( index >= Mix::HID::GAMEPAD_FINGER_MAX )
	{
		return Gamepad::DUMMY_STICK_STATE;
	}

	return m_Stick[index];
}

Boolean Gamepad::IsAvailable( void ) const
{
	return m_bAvailable;
}

Mix::HID::GAMEPAD_API Gamepad::GetAPI( void ) const
{
	return Mix::HID::GAMEPAD_DIRECTINPUT;
}

UInt8 Gamepad::GetSourceButtonValue( UInt32 index ) const
{
	if( Mix::HID::DI_BUTTON_MAX <= index )
	{
		return 0;
	}

	return m_Data.buttons[index];
}

UInt32 Gamepad::GetSourcePovValue( UInt32 index ) const
{
	if( Mix::HID::DI_POV_MAX <= index )
	{
		return 0;
	}

	return GetPovState( index );
}

void Gamepad::SetButtonAssignment( UInt32 targetIndex, UInt32 sourceIndex )
{
	switch( targetIndex )
	{
	case Mix::HID::GAMEPAD_POV_UP:
	case Mix::HID::GAMEPAD_POV_DOWN:
	case Mix::HID::GAMEPAD_POV_LEFT:
	case Mix::HID::GAMEPAD_POV_RIGHT:
		if( sourceIndex < Mix::HID::DI_POV_MAX )
		{
			m_ButtonRefTable[targetIndex] = sourceIndex;
		}
		break;

	default:
		if( sourceIndex < Mix::HID::DI_BUTTON_MAX )
		{
			m_ButtonRefTable[targetIndex] = sourceIndex;
		}
	}
}

UInt32 Gamepad::GetButtonAssignment( UInt32 targetIndex ) const
{
	if( targetIndex >= Mix::HID::GAMEPAD_BUTTON_MAX )
	{
		return 0xFFFFFFFF;
	}

	return m_ButtonRefTable[targetIndex];
}

void Gamepad::SetTriggerAssignment( UInt32 targetIndex, UInt32 sourceIndex )
{
	if( ( targetIndex >= Mix::HID::GAMEPAD_FINGER_MAX ) &&
		( sourceIndex >= Mix::HID::DI_BUTTON_MAX ) )
	{
		return;
	}

	m_TriggerRefTable[targetIndex] = sourceIndex;
}

UInt32 Gamepad::GetTriggerAssignment( UInt32 targetIndex ) const
{
	if( targetIndex >= Mix::HID::GAMEPAD_FINGER_MAX )
	{
		return 0xFFFFFFFF;
	}

	return m_TriggerRefTable[targetIndex];
}

void Gamepad::SetTriggerThreshold( UInt32 index, UInt8 threshold )
{
}

UInt8 Gamepad::GetTriggerThreshold( UInt32 index ) const
{
	return Mix::HID::GAMEPAD_TRIGGER_RESOLUTION;
}

void Gamepad::UpdateState( const DI_GAMEPAD_DATA* pData )
{
	UInt32 i;
	UInt32 updPovDIR;

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボタン
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	for( i = 0; i < Mix::HID::DI_BUTTON_MAX; i++ )
	{
		const UInt8& oldBtn = m_Data.buttons[i];
		const UInt8& newBtn = pData->buttons[i];

		if( oldBtn == 0 )
		{
			if( newBtn == 0 )
			{
				//Old:UP New:UP
				MIX_RESETBIT( m_ButtonList[i], Mix::HID::RELEASED );
			}
			else
			{
				//Old:UP New:DOWN
				MIX_RESETBIT( m_ButtonList[i], Mix::HID::RELEASED );
				MIX_SETBIT( m_ButtonList[i], Mix::HID::PRESSED );
				MIX_SETBIT( m_ButtonList[i], Mix::HID::DOWN );
			}
		}
		else
		{
			if( newBtn == 0 )
			{
				//Old:DOWN New:UP
				MIX_RESETBIT( m_ButtonList[i], Mix::HID::PRESSED );
				MIX_RESETBIT( m_ButtonList[i], Mix::HID::DOWN );
				MIX_SETBIT( m_ButtonList[i], Mix::HID::RELEASED );
			}
			else
			{
				//Old:DOWN New:DOWN
				MIX_RESETBIT( m_ButtonList[i], Mix::HID::PRESSED );
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// POV
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	updPovDIR = ( ( pData->pov & 0x0000FFFF ) == 0x0000FFFF )? ( 0xFFFFFFFF ) : ( ( pData->pov / 4500 ) % 8 );
	for( i = 0; i < Mix::HID::DI_POV_MAX; i++ )
	{
		if( i == updPovDIR )
		{
			if( m_PovList[i] == 0 )
			{
				//Old:UP New:DOWN
				MIX_RESETBIT( m_PovList[i], Mix::HID::RELEASED );
				MIX_SETBIT( m_PovList[i], Mix::HID::PRESSED );
				MIX_SETBIT( m_PovList[i], Mix::HID::DOWN );
			}
			else
			{
				//Old:DOWN New:DOWN
				MIX_RESETBIT( m_PovList[i], Mix::HID::PRESSED );
			}
		}
		else
		{
			if( MIX_TESTBIT( m_PovList[i], Mix::HID::DOWN ) == Mix::HID::DOWN )
			{
				//Old:DOWN New:UP
				MIX_RESETBIT( m_PovList[i], Mix::HID::PRESSED );
				MIX_RESETBIT( m_PovList[i], Mix::HID::DOWN );
				MIX_SETBIT( m_PovList[i], Mix::HID::RELEASED );
			}
			else
			{
				//Old:UP New:UP
				MIX_RESETBIT( m_PovList[i], Mix::HID::RELEASED );
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////////////////////////////////////////
	// スティック
	//////////////////////////////////////////////////////////////////////////////////////////////////////////////

	Mix::Memory::Copy( &m_Data, pData, sizeof( DI_GAMEPAD_DATA ) );

	Int16 thumbs[4] =
	{
		m_Data.axis[0],
		m_Data.axis[4],
		m_Data.axis[8],
		m_Data.axis[20],
	};

	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_FINGER_MAX; i++ )
	{
		const Gamepad::STICK_CAP* pCap = &( m_StickCap[i] );
		Float32* data = &( m_Stick[i].data[0] );

		for( UInt32 j = 0; j < 2; j++ )
		{
			const Int16& thumb = thumbs[ i * 2 + j];

			if( ( thumb < pCap->deadZone ) &&
				( thumb > -pCap->deadZone ) )
			{
				data[j] = 0.0f;
			}
			else
			{
				if( thumb > 0 )
				{
					data[j] = MIX_CLAMP( MIX_FLOAT_DIV( static_cast<Float32>( thumb - pCap->deadZone ), pCap->plusNorm ), 0.0f, 1.0f );
				}
				else
				{
					data[j] = MIX_CLAMP( MIX_FLOAT_DIV( static_cast<Float32>( thumb + pCap->deadZone ), pCap->minusNorm ), -1.0f, 0.0f );
				}
			}
		}
	}
}

void Gamepad::LostState( void )
{
	UInt32 sizeButtons = ( sizeof( Int32 ) * Mix::HID::DI_BUTTON_MAX );
	UInt32 sizePovs = ( sizeof( Int32 ) * Mix::HID::DI_POV_MAX );

	Mix::Memory::Zero( &m_Data, sizeof( m_Data ) );
	Mix::Memory::Copy( &( m_ButtonList[Mix::HID::DI_BUTTON_MAX] ), &( m_ButtonList[0] ), sizeButtons );
	Mix::Memory::Zero( &( m_ButtonList[0] ), sizeButtons );

	Mix::Memory::Copy( &( m_PovList[Mix::HID::DI_POV_MAX] ), &( m_PovList[0] ), sizePovs );
	Mix::Memory::Zero( &( m_PovList[0] ), sizePovs );

	m_Stick[Mix::HID::GAMEPAD_LEFT] = Mix::Vector2::Zero();
	m_Stick[Mix::HID::GAMEPAD_RIGHT] = Mix::Vector2::Zero();
}

void Gamepad::RestoreState( void )
{
	::CopyMemory( &( m_ButtonList[0] ), &( m_ButtonList[Mix::HID::DI_BUTTON_MAX] ), ( sizeof( UInt32 ) * Mix::HID::DI_BUTTON_MAX ) );
	::CopyMemory( &( m_PovList[0] ), &( m_PovList[Mix::HID::DI_POV_MAX] ), ( sizeof( Int32 ) * Mix::HID::DI_POV_MAX ) );
}

UInt32 Gamepad::GetPovState( UInt32 index ) const
{
	UInt32 last = Mix::HID::DI_POV_MAX - 1;
//	UInt32 base = m_ButtonRefTable[index];
	UInt32 table[3] = { 0 };
	UInt32 ret = 0;

//	table[0] = base;
//	table[1] = ( base > 0 )? ( base - 1 ) : ( last );
//	table[2] = ( base < last )? ( base + 1 ) : 0;

	table[0] = index;
	table[1] = ( index > 0 )? ( index - 1 ) : ( last );
	table[2] = ( index < last )? ( index + 1 ) : 0;

	for( UInt32 i = 0; i < 3; i++ )
	{
		UInt32 temp = m_PovList[table[i]];
		if( temp > 0 )
		{
			ret = temp;
		}
	}

	return ret;
}

}}}
