// Created by Paul Gonzalez Becerra

using System;

using Saserdote.DataSystems;
using Saserdote.Mathematics;

using Tao.Sdl;

namespace Saserdote.Input
{
	public abstract class Gamepad:IDisposable
	{
		#region --- Field Variables ---
		
		// Variables
		protected string	pName;
		protected IntPtr	pJoystick;
		protected IntPtr	pHaptic;
		protected bool	bRumble;
		protected FList<GamepadButton>	buttons;
		protected FList<GamepadDpad>	dpads;
		protected FList<GamepadStick>	sticks;
		protected FList<GamepadTrigger>	triggers;
		internal GBL	buttonsHeld;
		
		#endregion // Field Variables
		
		#region --- Constructors ---
		
		public Gamepad(IntPtr pmJoystick)
		{
			pName=	"Unknown Gamepad";
			pJoystick=	pmJoystick;
			if(Sdl.SDL_JoystickIsHaptic(pJoystick)== 1)
				pHaptic=	Sdl.SDL_HapticOpenFromJoystick(pJoystick);
			else
				pHaptic=	IntPtr.Zero;
			bRumble=	(Sdl.SDL_HapticRumbleSupported(pHaptic)== Sdl.SDL_TRUE);
			if(bRumble)
				bRumble=	(Sdl.SDL_HapticRumbleInit(pHaptic)== 0);
			if(bRumble)
				Console.WriteLine("HAPTIC_RUMBLE SUPPORTED");
			buttons=	new FList<GamepadButton>();
			dpads=	new FList<GamepadDpad>();
			sticks=	new FList<GamepadStick>();
			buttonsHeld=	GBL.NONE;
			
			for(int i= 0; i< Sdl.SDL_JoystickNumButtons(pJoystick); i++)
				buttons.add(new GamepadButton(pJoystick, i));
			for(int i= 0; i< Sdl.SDL_JoystickNumHats(pJoystick); i++)
				dpads.add(new GamepadDpad(pJoystick, i));
			for(int i= 0; i< (int)(Sdl.SDL_JoystickNumAxes(joystick)/2f); i++)
			{
				if(mapStick(Sdl.SDL_JoystickGetAxis(joystick, i*2))== -1)
				{
					triggers.add(new GamepadTrigger(joystick, i*2));
					triggers.add(new GamepadTrigger(joystick, (i*2)+1));
				}
				else
					sticks.add(new GamepadStick(pJoystick, i*2, (i*2)+1));
			}
		}
		
		#endregion // Constructors
		
		#region --- Properties ---
		
		// Gets the joystick pointer of the gamepad
		public IntPtr joystick
		{
			get	{	return pJoystick;	}
		}
		
		#endregion // Properties
		
		#region --- Methods ---
		
		// Maps the stick to see if its a trigger or a stick
		private float mapStick(short value)
		{
			// Variables
			float	results=	((float)value/short.MaxValue);
			
			// Be careful of rounding error
			results=	Math.Min(results, 1f);
			results=	Math.Max(results, -1f);
			
			if(Math.Abs(results)< 0.24f)
				results=	0;
			
			return results;
		}
		
		// Gets the movement of the left stick
		public virtual Vector2 getLeftStickMovement()
		{
			return Vector2.ZERO;
		}
		
		// Gets the movement of the right stick
		public virtual Vector2 getRightStickMovement()
		{
			return Vector2.ZERO;
		}
		
		// Gets the left trigger movement
		public virtual float getLeftTriggerMovement()
		{
			return 0f;
		}
		
		// Gets the right trigger movement
		public virtual float getRightTriggerMovement()
		{
			return 0f;
		}
		
		// Activates the rumble feature of the gamepad, if any
		public bool activateRumble(float strength, uint length)
		{
			return (Sdl.SDL_HapticRumblePlay(pHaptic, strength, length)== 0);
		}
		
		// Stops the rumble feature of the gamepad
		public bool stopRumble()
		{
			return (Sdl.SDL_HapticRumbleStop(pHaptic)== 0);
		}
		
		// Gets the currently held buttons
		public GBL getHeldButtons()
		{
			// Variables
			GBL	btns=	GBL.NONE;
			
			for(int i= 0; i< buttons.size; i++)
			{
				if(buttons.items[i].pHeld)
					btns|=	buttons.items[i].pKeyRep;
			}
			for(int i= 0; i< dpads.size; i++)
			{
				if(dpads.items[i].pUp)
					btns|=	dpads.items[i].pUpKeyRep;
				if(dpads.items[i].pRight)
					btns|=	dpads.items[i].pRightKeyRep;
				if(dpads.items[i].pDown)
					btns|=	dpads.items[i].pDownKeyRep;
				if(dpads.items[i].pLeft)
					btns|=	dpads.items[i].pLeftKeyRep;
			}
			for(int i= 0; i< sticks.size; i++)
			{
				if(sticks.items[i].bUp)
					btns|=	sticks.items[i].pUpKeyRep;
				if(sticks.items[i].bRight)
					btns|=	sticks.items[i].pRightKeyRep;
				if(sticks.items[i].bDown)
					btns|=	sticks.items[i].pDownKeyRep;
				if(sticks.items[i].bLeft)
					btns|=	sticks.items[i].pLeftKeyRep;
			}
			
			return btns;
		}
		
		// Updates the gamepad
		public virtual void update()
		{
			Sdl.SDL_JoystickUpdate();
			for(int i= 0; i< buttons.size; i++)
				buttons.items[i].update();
			for(int i= 0; i< dpads.size; i++)
				dpads.items[i].update();
			for(int i= 0; i< sticks.size; i++)
				sticks.items[i].update();
		}
		
		#endregion // Methods
		
		#region --- Inherited Methods ---
		
		// Disposes of the gamepad
		public void Dispose()
		{
			Sdl.SDL_JoystickClose(pJoystick);
		}
		
		// Prints out the name of the gamepad
		public override string ToString()
		{
			return pName;
		}
		
		#endregion // Inherited Methods
	}
}

// End of File