//#define UNITY_XBOXONE


#if UNITY_XBOXONE
using Gamepad;
#endif


namespace InControl
{
	public class XboxOneInputDevice : InputDevice
	{
		const uint AnalogLeftStickX = 0;
		const uint AnalogLeftStickY = 1;

		const uint AnalogRightStickX = 3;
		const uint AnalogRightStickY = 4;

		const uint AnalogLeftTrigger = 8;
		const uint AnalogRightTrigger = 9;

		const float LowerDeadZone = 0.2f;
		const float UpperDeadZone = 0.9f;

		internal uint JoystickId { get; private set; }

		public ulong ControllerId { get; private set; }


		public XboxOneInputDevice( uint joystickId )
			: base( "Xbox One Controller" )
		{
			JoystickId = joystickId;
			SortOrder = (int) joystickId;
			Meta = "Xbox One Device #" + joystickId;

			#if UNITY_XBOXONE
			ControllerId = XboxOneInput.GetControllerId( joystickId );
			#endif

			AddControl( InputControlType.LeftStickLeft, "Left Stick Left", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.LeftStickRight, "Left Stick Right", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.LeftStickUp, "Left Stick Up", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.LeftStickDown, "Left Stick Down", LowerDeadZone, UpperDeadZone );

			AddControl( InputControlType.RightStickLeft, "Right Stick Left", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.RightStickRight, "Right Stick Right", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.RightStickUp, "Right Stick Up", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.RightStickDown, "Right Stick Down", LowerDeadZone, UpperDeadZone );

			AddControl( InputControlType.LeftTrigger, "Left Trigger", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.RightTrigger, "Right Trigger", LowerDeadZone, UpperDeadZone );

			AddControl( InputControlType.DPadUp, "DPad Up", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.DPadDown, "DPad Down", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.DPadLeft, "DPad Left", LowerDeadZone, UpperDeadZone );
			AddControl( InputControlType.DPadRight, "DPad Right", LowerDeadZone, UpperDeadZone );

			AddControl( InputControlType.Action1, "A" );
			AddControl( InputControlType.Action2, "B" );
			AddControl( InputControlType.Action3, "X" );
			AddControl( InputControlType.Action4, "Y" );

			AddControl( InputControlType.LeftBumper, "Left Bumper" );
			AddControl( InputControlType.RightBumper, "Right Bumper" );

			AddControl( InputControlType.LeftStickButton, "Left Stick Button" );
			AddControl( InputControlType.RightStickButton, "Right Stick Button" );

			AddControl( InputControlType.View, "View" );
			AddControl( InputControlType.Menu, "Menu" );
		}

		public override void Update( ulong updateTick, float deltaTime )
		{
			#if UNITY_XBOXONE
			var lsv = new Vector2( GetAnalogValue( AnalogLeftStickX ), -GetAnalogValue( AnalogLeftStickY ) );
			UpdateLeftStickWithValue( lsv, updateTick, deltaTime );

			var rsv = new Vector2( GetAnalogValue( AnalogRightStickX ), -GetAnalogValue( AnalogRightStickY ) );
			UpdateRightStickWithValue( rsv, updateTick, deltaTime );

			UpdateWithValue( InputControlType.LeftTrigger, GetAnalogValue( AnalogLeftTrigger ), updateTick, deltaTime );
			UpdateWithValue( InputControlType.RightTrigger, GetAnalogValue( AnalogRightTrigger ), updateTick, deltaTime );

			UpdateWithState( InputControlType.DPadUp, GetButtonState( XboxOneKeyCode.GamepadButtonDPadUp ), updateTick, deltaTime );
			UpdateWithState( InputControlType.DPadDown, GetButtonState( XboxOneKeyCode.GamepadButtonDPadDown ), updateTick, deltaTime );
			UpdateWithState( InputControlType.DPadLeft, GetButtonState( XboxOneKeyCode.GamepadButtonDPadLeft ), updateTick, deltaTime );
			UpdateWithState( InputControlType.DPadRight, GetButtonState( XboxOneKeyCode.GamepadButtonDPadRight ), updateTick, deltaTime );

			UpdateWithState( InputControlType.Action1, GetButtonState( XboxOneKeyCode.GamepadButtonA ), updateTick, deltaTime );
			UpdateWithState( InputControlType.Action2, GetButtonState( XboxOneKeyCode.GamepadButtonB ), updateTick, deltaTime );
			UpdateWithState( InputControlType.Action3, GetButtonState( XboxOneKeyCode.GamepadButtonX ), updateTick, deltaTime );
			UpdateWithState( InputControlType.Action4, GetButtonState( XboxOneKeyCode.GamepadButtonY ), updateTick, deltaTime );

			UpdateWithState( InputControlType.LeftBumper, GetButtonState( XboxOneKeyCode.GamepadButtonLeftShoulder ), updateTick, deltaTime );
			UpdateWithState( InputControlType.RightBumper, GetButtonState( XboxOneKeyCode.GamepadButtonRightShoulder ), updateTick, deltaTime );

			UpdateWithState( InputControlType.LeftStickButton, GetButtonState( XboxOneKeyCode.GamepadButtonLeftThumbstick ), updateTick, deltaTime );
			UpdateWithState( InputControlType.RightStickButton, GetButtonState( XboxOneKeyCode.GamepadButtonRightThumbstick ), updateTick, deltaTime );

			UpdateWithState( InputControlType.View, GetButtonState( XboxOneKeyCode.GamepadButtonView ), updateTick, deltaTime );
			UpdateWithState( InputControlType.Menu, GetButtonState( XboxOneKeyCode.GamepadButtonMenu ), updateTick, deltaTime );

			Commit( updateTick, deltaTime );
			#endif
		}


		#if UNITY_XBOXONE
		XboxOneKeyCode GetButtonKeyCode( XboxOneKeyCode keyCode )
		{
			const int offset = XboxOneKeyCode.Gamepad2ButtonA - XboxOneKeyCode.Gamepad1ButtonA;
			return (XboxOneKeyCode) ((int) keyCode + JoystickId * offset);
		}


		bool GetButtonState( XboxOneKeyCode keyCode )
		{
			return XboxOneInput.GetKey( GetButtonKeyCode( keyCode ) );
		}


		float GetAnalogValue( uint analogId )
		{
			return Input.GetAxisRaw( "joystick " + JoystickId + " analog " + analogId );
		}
		#endif


		public bool IsConnected
		{
			get
			{ 
				#if UNITY_XBOXONE
				return XboxOneInput.IsGamepadActive( JoystickId ); 
				#else
				return false;
				#endif
			}
		}


		public override void Vibrate( float leftMotor, float rightMotor )
		{
			#if UNITY_XBOXONE
			GamepadPlugin.SetGamepadVibration( ControllerId, leftMotor, rightMotor, leftMotor, rightMotor );
			#endif
		}


		public void Vibrate( float leftMotor, float rightMotor, float leftTrigger, float rightTrigger )
		{
			#if UNITY_XBOXONE
			GamepadPlugin.SetGamepadVibration( ControllerId, leftMotor, rightMotor, leftTrigger, rightTrigger );
			#endif
		}
	}
}

