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

namespace Mix{ namespace HID{ namespace XB{

const UInt32 Gamepad::XI_BUTTON_TABLE[Mix::HID::GAMEPAD_BUTTON_MAX] =
{
	XINPUT_GAMEPAD_DPAD_UP,
	XINPUT_GAMEPAD_DPAD_DOWN,
	XINPUT_GAMEPAD_DPAD_LEFT,
	XINPUT_GAMEPAD_DPAD_RIGHT,
	XINPUT_GAMEPAD_START,
	XINPUT_GAMEPAD_BACK,
	XINPUT_GAMEPAD_LEFT_THUMB,
	XINPUT_GAMEPAD_RIGHT_THUMB,
	XINPUT_GAMEPAD_LEFT_SHOULDER,
	XINPUT_GAMEPAD_RIGHT_SHOULDER,
	XINPUT_GAMEPAD_A,
	XINPUT_GAMEPAD_B,
	XINPUT_GAMEPAD_X,
	XINPUT_GAMEPAD_Y,
};

Gamepad* Gamepad::CreateInstance( UInt32 index )
{
	return MIX_LIB_NEW_T( Mix::Memory::SECTION_GENERAL, Gamepad, index );
}

Gamepad::Gamepad( UInt32 index ) :
m_Index( index ),
m_bRefreshButtonState( False ),
m_bAvailable( False )
{
	XINPUT_STATE state;

	Mix::Memory::Zero( &state, sizeof( state ) );
	Mix::Memory::Zero( &m_XIState, sizeof( m_XIState ) );
	Mix::Memory::Zero( m_ButtonStateList, sizeof( m_ButtonStateList ) );
	Mix::Memory::Zero( m_Trigger, sizeof( m_Trigger ) );
	m_Stick[Mix::HID::GAMEPAD_LEFT] = Mix::Vector2::Zero();
	m_Stick[Mix::HID::GAMEPAD_RIGHT] = Mix::Vector2::Zero();
	SetTriggerThreshold( Mix::HID::GAMEPAD_LEFT, XINPUT_GAMEPAD_TRIGGER_THRESHOLD );
	SetTriggerThreshold( Mix::HID::GAMEPAD_RIGHT, XINPUT_GAMEPAD_TRIGGER_THRESHOLD );

	if( XInputGetState( m_Index, &state ) == ERROR_SUCCESS )
	{
		UpdateState( state );
		m_bAvailable = True;
	}
}

Gamepad::~Gamepad( void )
{
}

void Gamepad::Update( void )
{
	XINPUT_STATE nextXIState;

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボタンステートを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

	if( m_bRefreshButtonState == True )
	{
		for( UInt32 i = 0; i < Mix::HID::GAMEPAD_BUTTON_MAX; i++ )
		{
			m_ButtonStateList[i] &= Mix::HID::DOWN;
		}

		m_bRefreshButtonState = False;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// 全てのステートを更新
	////////////////////////////////////////////////////////////////////////////////////////////////////

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

	if( XInputGetState( m_Index, &nextXIState ) == ERROR_SUCCESS )
	{
		if( nextXIState.dwPacketNumber != m_XIState.dwPacketNumber )
		{
			UpdateState( nextXIState );
		}

		m_bAvailable = True;
	}
	else
	{
		ResetState();

		m_bAvailable = False;
	}
}

void Gamepad::Dispose( void )
{
	ResetState();

	m_bAvailable = False;
}

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

	return m_ButtonStateList[index];
}

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

	return m_Trigger[index];
}

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

UInt8 Gamepad::GetSourceButtonValue( UInt32 index ) const
{
	return 0;
}

UInt32 Gamepad::GetSourcePovValue( UInt32 index ) const
{
	return 0;
}

void Gamepad::SetButtonAssignment( UInt32 targetIndex, UInt32 sourceIndex )
{
}

UInt32 Gamepad::GetButtonAssignment( UInt32 targetIndex ) const
{
	return 0xFFFFFFFF;
}

void Gamepad::SetTriggerAssignment( UInt32 targetIndex, UInt32 sourceIndex )
{
}

UInt32 Gamepad::GetTriggerAssignment( UInt32 targetIndex ) const
{
	return 0xFFFFFFFF;
}

void Gamepad::SetTriggerThreshold( UInt32 index, UInt8 threshold )
{
	if( index < Mix::HID::GAMEPAD_FINGER_MAX )
	{
		threshold = MIX_CLAMP( threshold, 0, Mix::HID::GAMEPAD_TRIGGER_RESOLUTION );

		m_TriggerCap[index].threshold = threshold;
		m_TriggerCap[index].norm = static_cast<Float32>( 255 - threshold );
	}
}

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

	return m_TriggerCap[index].threshold;
}

void Gamepad::UpdateState( const XINPUT_STATE& nextXIState )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボタン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_BUTTON_MAX; i++ )
	{
		UInt32 bit = Gamepad::XI_BUTTON_TABLE[i];

		if( ( MIX_TESTBIT( m_XIState.Gamepad.wButtons, bit ) != bit ) &&
			( MIX_TESTBIT( nextXIState.Gamepad.wButtons, bit ) == bit ) )
		{
			m_ButtonStateList[i] = Mix::HID::PRESSED | Mix::HID::DOWN;
			m_bRefreshButtonState = True;
		}
		else if(	( MIX_TESTBIT( m_XIState.Gamepad.wButtons, bit ) == bit ) &&
					( MIX_TESTBIT( nextXIState.Gamepad.wButtons, bit ) != bit ) )
		{
			m_ButtonStateList[i] = Mix::HID::RELEASED;
			m_bRefreshButtonState = True;
		}
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トリガー
	////////////////////////////////////////////////////////////////////////////////////////////////////

	UInt8 triggers[2] =
	{
		nextXIState.Gamepad.bLeftTrigger,
		nextXIState.Gamepad.bRightTrigger,
	};

	for( UInt32 i = 0; i < 2; i++ )
	{
		const Gamepad::TRIGGER_CAP* pCap = &( m_TriggerCap[i] );
		const UInt8& trigger = triggers[i];

		if( trigger < pCap->threshold )
		{
			m_Trigger[i] = 0.0f;
		}
		else
		{
			m_Trigger[i] = MIX_FLOAT_SATURATE( MIX_FLOAT_DIV( trigger - pCap->threshold, pCap->norm ) );
		}
	}

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

	Int16 thumbs[4] =
	{
		nextXIState.Gamepad.sThumbLX,
		nextXIState.Gamepad.sThumbLY,
		nextXIState.Gamepad.sThumbRX,
		nextXIState.Gamepad.sThumbRY,
	};

	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
			{
				Float32 value;

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

				data[j] = ( j == 0 )? value : -value;
			}
		}
	}

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

	m_XIState = nextXIState;
}

void Gamepad::ResetState( void )
{
	////////////////////////////////////////////////////////////////////////////////////////////////////
	// ボタン
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_BUTTON_MAX; i++ )
	{
		m_ButtonStateList[i] = 0;
	}

	////////////////////////////////////////////////////////////////////////////////////////////////////
	// トリガー
	////////////////////////////////////////////////////////////////////////////////////////////////////

	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_FINGER_MAX; i++ )
	{
		m_Trigger[i] = 0.0f;
	}

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

	for( UInt32 i = 0; i < Mix::HID::GAMEPAD_FINGER_MAX; i++ )
	{
		m_Stick[i] = Mix::Vector2::Zero();
	}
}

}}}
