' Copyright (c) 2015-2022 Bruce A Henderson
'
' This software is provided 'as-is', without any express or implied
' warranty. In no event will the authors be held liable for any damages
' arising from the use of this software.
'
' Permission is granted to anyone to use this software for any purpose,
' including commercial applications, and to alter it and redistribute it
' freely, subject to the following restrictions:
'
'    1. The origin of this software must not be misrepresented; you must not
'    claim that you wrote the original software. If you use this software
'    in a product, an acknowledgment in the product documentation would be
'    appreciated but is not required.
'
'    2. Altered source versions must be plainly marked as such, and must not be
'    misrepresented as being the original software.
'
'    3. This notice may not be removed or altered from any source
'    distribution.
'
SuperStrict

Rem
bbdoc: GameController and Joystick Mapping
End Rem
Module SDL.SDLGameController

Import "common.bmx"


Rem
bbdoc: Checks if the given joystick is supported by the game controller interface.
returns: #True if the given joystick is supported by the game controller interface, #False if it isn't or it's an invalid index.
about: @port is joystick port, up to #JoyCount.
End Rem
Function SDLIsGameController:Int(port:Int)
	Return SDL_IsGameController(port)
End Function

Rem
bbdoc: A game controller or mapped joystick.
End Rem
Type TSDLGameController

	Field controllerPtr:Byte Ptr

	Rem
	bbdoc: Opens a game controller to use.
	returns: A #TSDLGameController instance or #Null if an error occurred; call #SDLGetError() for more information.
	about: The @port passed as an argument refers to the N'th game controller on the system.
	A call to #Close should be made when you have finished with this game controller.
	End Rem
	Function Open:TSDLGameController(port:Int)
		Local this:TSDLGameController = New TSDLGameController
		this.controllerPtr = SDL_GameControllerOpen(port)
		If this.controllerPtr Then
			Return this
		End If
	End Function
	
	Rem
	bbdoc: Closes the game controller.
	End Rem
	Method Close()
		If controllerPtr Then
			SDL_GameControllerClose(controllerPtr)
			controllerPtr = Null
		End If
	End Method

	Rem
	bbdoc: Checks if a controller has been opened and is currently connected.
	returns: #True if the controller has been opened and currently connected, or #False if it has not.
	End Rem
	Method GetAttached:Int()
		Return SDL_GameControllerGetAttached(controllerPtr)
	End Method
	
	Rem
	bbdoc: Gets the current state of an axis control on a game controller.
	returns: Axis state (including 0) on success or 0 (also) on failure; call #SDLGetError for more information.
	about: The axis indices start at index 0.
	The state is a value ranging from -32768 to 32767. Triggers, however, range from 0 to 32767 (they never return a negative value).
	End Rem
	Method GetAxis:Int(axis:ESDLGameControllerAxis)
		Return SDL_GameControllerGetAxis(controllerPtr, axis)
	End Method
	
	Rem
	bbdoc: Gets the current state of a button on a game controller.
	returns: 1 for pressed state or 0 for not pressed state or error; call #SDLGetError for more information.
	about: The button indices start at index 0.
	End Rem
	Method GetButton:Int(button:ESDLGameControllerButton)
		Return SDL_GameControllerGetButton(controllerPtr, button)
	End Method
	
	Rem
	bbdoc: Gets the implementation dependent name for the game controller.
	returns: The implementation dependent name for the game controller, or #Null if there is no name or the controller is invalid.
	End Rem
	Method GetName:String()
		Local n:Byte Ptr = SDL_GameControllerName(controllerPtr)
		If n Then
			Return String.FromUTF8String(n)
		End If
	End Method
	
	Rem
	bbdoc: Gets the current mapping of the Game Controller.
	returns: A #String that has the controller's mapping or #Null if no mapping is available; call #SDLGetError for more information.
	about: More information about the mapping can be found at #AddMapping.
	End Rem
	Method GetMapping:String()
		Local n:Byte Ptr = SDL_GameControllerMapping(controllerPtr)
		If n Then
			Local s:String = String.FromUTF8String(n)
			SDL_free(n)
			Return s
		End If
	End Method
	
	Rem
	bbdoc: Returns the type of this currently opened controller.
	End Rem
	Method GetType:ESDLGameControllerType()
		Return SDL_GameControllerGetType(controllerPtr)
	End Method
	
	Rem
	bbdoc: Returns whether the game controller has a given axis.
	End Rem
	Method HasAxis:Int(axis:ESDLGameControllerAxis)
		Return SDL_GameControllerHasAxis(controllerPtr, axis)
	End Method
	
	Rem
	bbdoc: Returns whether the game controller has a given button.
	End Rem
	Method HasButton:Int(button:ESDLGameControllerButton)
		Return SDL_GameControllerHasButton(controllerPtr, button)
	End Method

	Rem
	bbdoc: Gets the number of touchpads on the game controller.
	End Rem
	Method GetNumTouchpads:Int()
		Return SDL_GameControllerGetNumTouchpads(controllerPtr)
	End Method
	
	Rem
	bbdoc: Gets the number of supported simultaneous fingers on a touchpad on the game controller.
	End Rem
	Method GetNumTouchpadFingers:Int(touchpad:Int)
		Return SDL_GameControllerGetNumTouchpadFingers(controllerPtr, touchpad)
	End Method
	
	Rem
	bbdoc: Gets the current state of a finger on a touchpad on the game controller.
	End Rem
	Method GetTouchpadFinger:Int(touchpad:Int, finger:Int, state:Byte Var, x:Float Var, y:Float Var, pressure:Float Var)
		Return SDL_GameControllerGetTouchpadFinger(controllerPtr, touchpad, finger, Varptr state, Varptr x, Varptr y, Varptr pressure)
	End Method
	
	Rem
	bbdoc: Returns whether the game controller has a particular sensor.
	returns: #True if the sensor exists, #False otherwise.
	End Rem
	Method HasSensor:Int(sensorType:ESDLSensorType)
		Return SDL_GameControllerHasSensor(controllerPtr, sensorType)
	End Method
	
	Rem
	bbdoc: Sets whether data reporting for the game controller sensor is enabled.
	returns: 0, or -1 if an error occurred.
	End Rem
	Method SetSensorEnabled:Int(sensorType:ESDLSensorType, enabled:Int)
		Return SDL_GameControllerSetSensorEnabled(controllerPtr, sensorType, enabled)
	End Method
	
	Rem
	bbdoc: Queries whether sensor data reporting is enabled for the game controller.
	returns: #True if the sensor is enabled, #False otherwise.
	End Rem
	Method IsSensorEnabled:Int(sensorType:ESDLSensorType)
		Return SDL_GameControllerIsSensorEnabled(controllerPtr, sensorType)
	End Method
	
	Rem
	bbdoc: Gets the current state of a game controller sensor.
	returns: 0, or -1 if an error occurred.
	about: The number of values and interpretation of the data is sensor dependent.
 	End Rem
	Method SDL_GameControllerGetSensorData:Int(sensorType:ESDLSensorType, data:Float Ptr, numValues:Int)
		Return SDL_GameControllerGetSensorData(controllerPtr, sensorType, data, numValues)
	End Method
	
	Rem
	bbdoc: Starts a rumble effect.
	returns: 0, or -1 if rumble isn't supported on this controller.
	about: Each call to this method cancels any previous rumble effect, and calling it with 0 intensity stops any rumbling.
	End Rem
	Method Rumble:Int(lowFrequencyRumble:Short, highFrequencyRumble:Short, durationMs:UInt)
		Return SDL_GameControllerRumble(controllerPtr, lowFrequencyRumble, highFrequencyRumble, durationMs)
	End Method
	
	Rem
	bbdoc: Starts a rumble effect in the game controller's triggers
	returns: 0, or -1 if rumble isn't supported on this controller.
	about: Each call to this function cancels any previous trigger rumble effect, and calling it with 0 intensity stops any rumbling.
	End Rem
	Method RumbleTriggers:Int(leftRumble:Short, rightRumble:Short, durationMs:UInt)
		Return SDL_GameControllerRumbleTriggers(controllerPtr, leftRumble, rightRumble, durationMs)
	End Method
	
	Rem
	bbdoc: Returns whether the controller has an LED.
	returns: #True, or #False if this controller does not have a modifiable LED.
	End Rem
	Method HasLED:Int()
		Return SDL_GameControllerHasLED(controllerPtr)
	End Method
	
	Rem
	bbdoc: Updates a controller's LED color.
	returns: 0, or -1 if this controller does not have a modifiable LED.
	End Rem
	Method SDL_GameControllerSetLED:Int(red:Byte, green:Byte, blue:Byte)
		Return SDL_GameControllerSetLED(controllerPtr, red, green, blue)
	End Method

	Rem
	bbdoc: Queries whether the game controller has rumble support.
	returns: #True, or #False if this controller does not have rumble support.
	End Rem
	Method HasRumble:Int()
		Return SDL_GameControllerHasRumble(controllerPtr)
	End Method

	Rem
	bbdoc: Queries whether the game controller has rumble support on triggers.
	returns: #True, or #False if this controller does not have trigger rumble support.
	End Rem
	Method HasRumbleTriggers:Int()
		Return SDL_GameControllerHasRumbleTriggers(controllerPtr)
	End Method

	Rem
	bbdoc: Gets the player index of an opened game controller.
	returns: The player index for the controller, or -1 if it's not available.
	about: For XInput controllers this returns the XInput user index.
	End Rem
	Method GetPlayerIndex:Int(handle:Byte Ptr)
		Return SDL_GameControllerGetPlayerIndex(controllerPtr)
	End Method

	Rem
	bbdoc: Sets the player index of an opened game controller.
	End Rem
	Method SetPlayerIndex(index:Int)
		SDL_GameControllerSetPlayerIndex(controllerPtr, index)
	End Method
	
	Rem
	bbdoc: Adds support for controllers that SDL is unaware of or to cause an existing controller to have a different binding.
	returns: 1 if a new mapping is added, 0 if an existing mapping is updated, -1 on error; call #SDLGetError for more information.
	about: The mapping string has the format "GUID,name,mapping", where GUID is the string value from #GetGUIDString, name is the human
	readable string for the device and mappings are controller mappings to joystick ones. Under Windows there is a
	reserved GUID of "xinput" that covers all XInput devices. The mapping format for joystick is:
	
	| Mapping  | Description  |
	|---|---|
	| bX   | a joystick button, index X  |
	| hX.Y | hat X with value Y |
	| aX | axis X of the joystick |

	Buttons can be used as a controller axes and vice versa.

	This string shows an example of a valid mapping for a controller: `"341a3608000000000000504944564944,Afterglow PS3 Controller,a:b1,b:b2,y:b3,x:b0,start:b9,guide:b12,back:b8,dpup:h0.1,dpleft:h0.8,dpdown:h0.4,dpright:h0.2,leftshoulder:b4,rightshoulder:b5,leftstick:b10,rightstick:b11,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:b6,righttrigger:b7"`
	End Rem
	Function AddMapping:Int(mapping:String)
		Local m:Byte Ptr = mapping.ToUTF8String()
		Local res:Int = SDL_GameControllerAddMapping(m)
		MemFree(m)
		Return res
	End Function
	
	Rem
	bbdoc: Converts a string into an enum representation for an #ESDLGameControllerAxis.
	returns: The #ESDLGameControllerAxis enum corresponding to the input string, or ESDLGameControllerAxis.INVALID if no match was found.
	End Rem
	Function GetAxisFromString:ESDLGameControllerAxis(txt:String)
		Local t:Byte Ptr = txt.ToUTF8String()
		Local axis:ESDLGameControllerAxis = SDL_GameControllerGetAxisFromString(t)
		MemFree(t)
		Return axis
	End Function
	
	Rem
	bbdoc: Turns a string into a button mapping.
	returns: A button mapping (#ESDLGameControllerButton) on success or ESDLGameControllerButton.INVALID on failure.
	End Rem
	Function GetButtonFromString:ESDLGameControllerButton(txt:String)
		Local t:Byte Ptr = txt.ToUTF8String()
		Local button:ESDLGameControllerButton = SDL_GameControllerGetButtonFromString(t)
		MemFree(t)
		Return button
	End Function
	
	Rem
	bbdoc: Turns an axis enum into a string mapping.
	End Rem
	Function GetStringForAxis:String(axis:ESDLGameControllerAxis)
		Return String.FromUTF8String(SDL_GameControllerGetStringForAxis(axis))
	End Function
	
	Rem
	bbdoc: Turns a button enum into a string mapping.
	End Rem
	Function GetStringForButton:String(button:ESDLGameControllerButton)
		Return String.FromUTF8String(SDL_GameControllerGetStringForButton(button))
	End Function
	
	Rem
	bbdoc: Gets the implementation dependent name for the game controller.
	returns: The implementation dependent name for the game controller, or #Null if there is no name or the index is invalid.
	End Rem
	Function NameForIndex:String(port:Int)
		Local n:Byte Ptr = SDL_GameControllerNameForIndex(port)
		If n Then
			Return String.FromUTF8String(n)
		End If
	End Function
	
	Rem
	bbdoc: Gets the type of a game controller.
	about: This can be called before any controllers are opened.
	End Rem
	Function TypeForIndex:ESDLGameControllerType(port:Int)
		Return SDL_GameControllerTypeForIndex(port)
	End Function
	
End Type
