/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: sgx/include/sgx/input/inputmanager.hpp

*/

#ifndef SGX_INPUT_MANAGER_HPP
#define SGX_INPUT_MANAGER_HPP		1

class CSGXInputManager {
public:
	typedef unsigned char tKey;

	typedef enum {
		eButton0 = 0,
		eButton1,
		eButton2,
		eButton3,
		eButton4,
		eButton5,
		eButton6,
		eButton7,
		eButton8,
		eButton9,
		eButton10,
		eButton11,
		eButton12,
		eButton13,
		eButton14,
		eButton15,
		//
		eFirstStick,
		eStick0x,
		eStick0y,
		eStick1x,
		eStick1y,
		eStick2x,
		eStick2y,
		eStick3x,
		eStick3y,
		//
		eLastControl = SGX_ENUM_PADDING
	} tControl;

	typedef enum {
		eMouseButtonLeft = 0,
		eMouseButtonRight,
		eMouseButtonMiddle,
		eMouseButtonLast = SGX_ENUM_PADDING
	} tMouseButton;

typedef enum {
	   ePhysical0 = 0,
	   ePhysical1,
	   ePhysical2,
	   ePhysical3,

	   eFirst,
	   eSecond,
	   eThird,
	   eFourth,

	   eAllLogical,
	   eGUIForwards,
	   eGUIBackwards,
	   eGUISelect,

	   eLastController = SGX_ENUM_PADDING
	} tController;

	class CControlCommand;
	class CCommandHandlerParameters {
	public:
		typedef enum {
			eAxisX,
			eAxisY,
			eAxisZ,
			eAxisPitch,
			eAxisRoll,
			eAxisYaw,
		} tAxis;

		CCommandHandlerParameters(CSGXInputManager::tControl commandControl, tREAL32 rangeFrom=0, tREAL32 rangeTo=1.0f);
		CCommandHandlerParameters(const CControlCommand *pCommandControl, tREAL32 rangeFrom=0, tREAL32 rangeTo=1.0f);
		tREAL32	prepareRange(const tREAL32 value) const;

		const CControlCommand *pCommand;

		tAxis	axis;

		// What portion of the stick are we considering?
		tREAL32 fRangeFrom;
		tREAL32 fRangeTo;

		// What range is fed back to the user
		tREAL32 fTargetScaleFrom;
		tREAL32 fTargetScaleRangeTo;

		CCommandHandlerParameters() : pCommand(NULL){}
	};

	class CControlCommand {
	public:
		virtual tBOOL	wasPressed(const CCommandHandlerParameters &params) const =0;		// for analog controls, returns TRUE if the stick moved into the 0/1 area last frame
		virtual tBOOL	wasReleased(const CCommandHandlerParameters &params) const =0;
		virtual tBOOL	getState(const CCommandHandlerParameters &params) const =0;		// on/off - up/down. If the derived control is analog, then _it_ determined the deadband
		virtual tREAL32	getPosition(const CCommandHandlerParameters &params)  const =0;		// variable, if supported, or 0 to 1 if not
	};


	class CControlStickAxis : public CControlCommand {
	public:
	};

	class CControlStick : public CControlCommand {
	public:
		tREAL32           	m_fPosX, m_fPosY;
		tREAL32           	m_fRawPosX, m_fRawPosY;

		CControlStick();
		void reset();
		void update(const tREAL32 telaps);
		void applyPosition(const tREAL32 x, const tREAL32 y);
		void setControlRange(const tREAL32 rangeX);
		void setControlRange(const tREAL32 rangeX, const tREAL32 rangeY);
		void setDeadZone(const tREAL32 range);
		void setDeadZone(const tREAL32 rangeX, const tREAL32 rangeY);
		void setClampX(const tREAL32 minX, const tREAL32 minY);
		void setClampY(const tREAL32 minY, const tREAL32 minZ);
		
		virtual tBOOL	wasPressed(const CCommandHandlerParameters &params) const;
		virtual tBOOL	wasReleased(const CCommandHandlerParameters &params) const;
		virtual tBOOL	getState(const CCommandHandlerParameters &params) const;
		virtual tREAL32	getPosition(const CCommandHandlerParameters &params) const;
	
	private:
		tREAL32 			m_fDeadZoneX;
		tREAL32 			m_fDeadZoneY;
		tREAL32 			m_fGivenDeadZoneX, m_fGivenDeadZoneY;
		tREAL32				m_fControlRangeX;	// we scale the input from a range of +/- m_fControlRange to +- 1
		tREAL32				m_fControlRangeY;
		tREAL32				m_fScaleRangeX;		// pre-computed value of controlRange-deadzone
		tREAL32				m_fScaleRangeY;
		tREAL32				m_fClampMinX, m_fClampMinY;
		tREAL32				m_fClampMaxX, m_fClampMaxY;
	};

	class CControlButton : public CControlCommand {
	public:
		tBOOL             	m_bPressed, m_bReleased;
		tBOOL             	m_bIsDown, m_bIsUp;

		CControlButton();
		void reset();
		
		void update(const tREAL32 telaps);

		void applyButton(const tBOOL down) { if (down) applyButtonDown(); else applyButtonUp(); }
		void applyButtonDown();
		void applyButtonUp();
		void applyButtonPressed();
		void applyButtonReleased();
		
		virtual tBOOL	wasPressed(const CCommandHandlerParameters &params) const;
		virtual tBOOL	wasReleased(const CCommandHandlerParameters &params) const;
		virtual tBOOL	getState(const CCommandHandlerParameters &params) const;
		virtual tREAL32	getPosition(const CCommandHandlerParameters &params) const;
	};

	class CKeyboardKey : public CControlButton {
	public:
	};

	//
	// Basic controllers
	//
	class CControllerMouse {	
		// TODO: call them controllers. call the above controls
	public:

		tINT32				x,y;
		CControlButton		m_Buttons[SGX_MAXINP_MOUSEBUTTONS];

		CControllerMouse();
		virtual void reset();
		virtual void update(const tREAL32 telaps, CControllerMouse &result);
		virtual tBOOL isPresent() const;
	};

	class CControllerKeyboard {
	public:
		CKeyboardKey		m_Key[SGX_MAXINP_KEYBOARD_KEYS];

		CControllerKeyboard();
		virtual void reset();
		virtual void update(const tREAL32 telaps, CControllerKeyboard &result);
		virtual tBOOL isPresent() const;
	};

	class CControllerJoypad {
	public:
		tBOOL				m_Valid;
		CControlButton		m_Buttons[SGX_MAXINP_BUTTONS];
		CControlStick		m_Sticks[SGX_MAXINP_STICKS];
		CSGXRumbleDevice 	*m_pRumble;

		CControllerJoypad();	// this is so derived classes don't need the (idx) version [which creates a null rumble device]
		CControllerJoypad(tUINT32 idx);
		virtual void 	reset();
		virtual void 	update(const tREAL32 telaps, CSGXInputManager::CControllerJoypad &result);	
		virtual tBOOL 	isPresent() const;
		virtual void 	suspend(const tBOOL suspend = TRUE);
		virtual void	stopAllRumbles();
	};

	class CCommandHandler {
	};

	// The input manager
	static CSGXInputManager *create();
	static CSGXInputManager *get();

	virtual void 		initializeGeneric();
	virtual void		initialize();
	virtual void		createMapping();
	virtual tUINT32		getNumControllers();
	virtual tUINT32		getMaxControllers();	// must be unchanging

	virtual CSGXInputManager::CControllerJoypad *	createJoypad(tUINT32 idx);
	virtual CSGXInputManager::CControllerKeyboard *	createKeyboard();
	virtual CSGXInputManager::CControllerMouse *	createMouse();

	virtual void		update(const tREAL32 telaps);

	virtual void  		suspend(const tBOOL suspend = TRUE);
	virtual void		stopAllRumbles();

	// Application (from platform-specific)
	//. TODO: Remove because this is available from the low-level manager driver
	virtual void		applyKeyboardKeyUp(const tKey &key);
	virtual void		applyKeyboardKeyDown(const tKey &key);
	virtual void		applyKeyboardPressed(const tKey &key);
	virtual void		applyKeyboardReleased(const tKey &key);

	virtual void		applyMouseButton(tMouseButton button, tBOOL bState);
	virtual void		applyMousePosition(const tINT32 x, const tINT32 y);

	// Query (to end user)
	virtual tBOOL		isKeyboardKeyDown(const tKey &key);
	virtual tBOOL		isKeyboardKeyUp(const tKey &key);
	virtual tBOOL		isKeyboardKeyPressed(const tKey &key);
	virtual tBOOL		isKeyboardKeyReleased(const tKey &key);
	virtual tUINT32		getKeyboardState(tBYTE *bitStates);	// should be at least 8 bytes

	virtual tBOOL		isMouseButtonDown(const tMouseButton &button);
	virtual tBOOL		isMouseButtonUp(const tMouseButton &button);
	virtual tBOOL		isMouseButtonPressed(const tMouseButton &button);
	virtual tBOOL		isMouseButtonReleased(const tMouseButton &button);
	virtual tINT32		getMouseX();
	virtual tINT32		getMouseY();

	virtual void		setCommandHandlerKeyboardMapping(const tUINT32 *pCmdIDKeyList, const tUINT32 size);
	virtual void		setCommandHandlerKeyboardMapping(const tUINT32 cmdID, const tUINT32 key);
	virtual tBOOL		setCommandHandler(const sgxString &id, const CCommandHandlerParameters &cmd);
	virtual tBOOL		setCommandHandler(const tUINT32 id, const CCommandHandlerParameters &cmd);
	virtual tBOOL		getCommandState(const sgxString &id);
	virtual tBOOL		getCommandState(const tUINT32 id);
	virtual tBOOL		wasCommandPressed(const tUINT32 id);
	virtual tBOOL		wasCommandReleased(const tUINT32 id);
	virtual tREAL32		getCommandPosition(const sgxString &id);
	virtual tREAL32		getCommandPosition(const tUINT32 id);
	virtual void		resetCommandHandler();

	virtual const CControllerJoypad *	getController(const tController controller);	// actually returns the device
	virtual const CControlButton &		getControlButton(const tController controller, const tControl button);
	virtual const CControlStick  &		getControlStick(const tController controller, const tControl stick);
	virtual const CKeyboardKey  &		getControlKeyboardKey(const tKey key);
	
protected:
	static CSGXInputManager *m_pSingleton;

	struct {
    tBOOL    bState;
    tREAL32  fState;
    tBOOL    bPressed;
    tBOOL    breleased;
} _ButtonState;

	//
	// Buffered states. Reflecting the current state
	// (copied to working state on update)
	// These are dynamically allocated because they might
	// contain plaform-specific features
	CControllerJoypad    	**m_pDeviceController;
	CControllerKeyboard		*m_pDeviceKeyboard;
	CControllerMouse		*m_pDeviceMouse;
	tUINT32					*m_Mapping;

	// This abstracts the simple button presses into a logical meaning
	// for the various commands.
	sgxHashMap<tUINT32, CCommandHandlerParameters>		m_CommandHandlers;

	// This struct (and the associated m_Mouse and m_Keyboard) are
	// cross-platform versions that contains the actual data that is
	// queriable by the user.

	// These are always the genric versions
	CControllerJoypad    **m_pController;
	CControllerKeyboard	m_Keyboard;
	CControllerMouse		m_Mouse;
};

#endif /* SGX_INPUT_MANAGER_HPP */
