
//////////////////////////////////////////////////////////////////////////////
// Asura_Platform_Rumble_XInput.cpp
// JCT - 01.04.09
// Copyright (c) 2009 Rebellion. All rights reserved.
//
// Support for Xbox 360 controllers/peripherals.
// Note: Left motor is the low-frequency motor (ASURA_PC_RUMBLE_MOTOR1).
//		 Right motor is the high-frequency motor (ASURA_PC_RUMBLE_MOTOR2).
//		 They can be set independently to provide different effects.
//////////////////////////////////////////////////////////////////////////////

// I N C L U D E S ///////////////////////////////////////////////////////////

#include "Core/Asura.h"

#ifdef ASURA_PC_SUPPORT_XINPUT

// This file's header
#include "Input/Asura_Platform_Rumble_XInput.h"

// Optionally include file contents
#if (defined ASURA_SUPPORT_CONTROLLER_RUMBLE) && (ASURA_SUPPORT_CONTROLLER_RUMBLE != 0)

// Asura headers
#include "Debugging/Asura_Assert.h"
#include "Input/Asura_Rumble_System.h"

// PC Includes
#include <XInput.h>

// C L A S S   D E C L A R A T I O N S //////////////////////////////////////

class Xbox360_Controller_Rumble_Capability
{
	public:
	
		bool m_bCanRumble;
};

// G L O B A L   V A R I A B L E S ///////////////////////////////////////////

static Xbox360_Controller_Rumble_Capability g_axDeviceCapabilites[XUSER_MAX_COUNT];

// F U N C T I O N   D E F I N I T I O N S ///////////////////////////////////

void Asura_Rumble_XInput::Platform_Initialise()
{
	//
	// Check our rumble capabilities.
	// If a controller's plugged in, this always seems to be in port 0 on the PC, regardless of which USB port it's in.
	//
	for(u_int uUser = 0; uUser < XUSER_MAX_COUNT ; ++uUser)
	{
		XINPUT_CAPABILITIES xCapabilities;
		g_axDeviceCapabilites[uUser].m_bCanRumble = false;
		DWORD uResult = XInputGetCapabilities( uUser, XINPUT_FLAG_GAMEPAD, &xCapabilities );
		Asura_Assertf( ( uResult == ERROR_SUCCESS ) || ( uResult == ERROR_DEVICE_NOT_CONNECTED ) , ("Failed to get controller capabilites, error %u", uResult));
		if(		(uResult == ERROR_SUCCESS )
			&&	( xCapabilities.Vibration.wLeftMotorSpeed	> 0 )		//The api is a bit vague about detecting vibration, but presumably all controllers have it?
			&&	( xCapabilities.Vibration.wRightMotorSpeed	> 0 ) )
		{
			g_axDeviceCapabilites[uUser].m_bCanRumble	= true;
		}
	}
}


void Asura_Rumble_XInput::Platform_Shutdown()
{
	Platform_StopAllRumbles();
}

void Asura_Rumble_XInput::Platform_PlayCurrentRumbles()
{
	for(u_int uUser = 0; uUser < XUSER_MAX_COUNT ; ++uUser)
	{
		if ( g_axDeviceCapabilites[uUser].m_bCanRumble )
		{
			#ifdef ASURA_ASSERT
			float fMotorSmall = Asura_Rumble_System::GetCurrentMotorMagnitude(0, ASURA_PC_RUMBLE_MOTOR1);
			float fMotorLarge = Asura_Rumble_System::GetCurrentMotorMagnitude(0, ASURA_PC_RUMBLE_MOTOR2);
			Asura_Assertf( ( fMotorSmall >= 0.0f ) && ( fMotorSmall <= 1.0f ), ( "Expected a motor magnitude between 0 and 1 for vibration (got %.2f)", fMotorSmall ) );
			Asura_Assertf( ( fMotorLarge >= 0.0f ) && ( fMotorLarge <= 1.0f ), ( "Expected a motor magnitude between 0 and 1 for vibration (got %.2f)", fMotorSmall ) );
			#endif

			XINPUT_VIBRATION xVibration;

			// Only 1 controller plugged in at a time, so always use index 0
			xVibration.wLeftMotorSpeed	= static_cast< WORD >( Asura_Rumble_System::GetCurrentMotorMagnitude(0, ASURA_PC_RUMBLE_MOTOR1) * 65535.0f );
			xVibration.wRightMotorSpeed = static_cast< WORD >( Asura_Rumble_System::GetCurrentMotorMagnitude(0, ASURA_PC_RUMBLE_MOTOR2) * 65535.0f );

			#ifdef ASURA_ASSERT
			DWORD uResult = XInputSetState ( uUser, &xVibration );
			//its not that unlikely Xinput will be busy, we dont really care since we set this so often
			if( ( uResult != ERROR_DEVICE_NOT_CONNECTED ) && ( uResult != ERROR_BUSY ) ) 
			{
				Asura_Assertf( uResult == ERROR_SUCCESS, ( "Failed to set vibration on controller %u, error %u", uUser, uResult ) );
			}
			#else
			XInputSetState ( uUser, &xVibration );
			#endif
		}
    }
}


void Asura_Rumble_XInput::Platform_StopAllRumbles()
{
	for(u_int uUser = 0; uUser < XUSER_MAX_COUNT ; ++uUser)
	{
		if(g_axDeviceCapabilites[uUser].m_bCanRumble)
		{
			XINPUT_VIBRATION xVibration;
			xVibration.wLeftMotorSpeed = 0;
			xVibration.wRightMotorSpeed = 0;
			DWORD uResult = ERROR_BUSY;
			//Naughty busy loop, should be okay unless xinput bombs somehow
			while(uResult == ERROR_BUSY)
			{
				uResult = XInputSetState ( uUser, &xVibration );
				#ifdef ASURA_ASSERT
				if( ( uResult != ERROR_DEVICE_NOT_CONNECTED ) && ( uResult != ERROR_BUSY ) )
				{
					Asura_Assertf( uResult == ERROR_SUCCESS, ( "Failed to stop vibration on controller %u, error %u", uUser, uResult ) );
				}
				#endif
			}
		}
	}
}


#endif // #if (defined ASURA_SUPPORT_CONTROLLER_RUMBLE) && (ASURA_SUPPORT_CONTROLLER_RUMBLE != 0) 
#endif	// ASURA_PC_SUPPORT_XINPUT
