// ------------------------------------------------------------------------
/*
	\author: Jaap Suter
   
	Copyright (C) Electronic Arts Canada Inc. 2002.  All rights reserved.	
*/
// ------------------------------------------------------------------------

/*! \file */

#ifndef REVEAL_INPUT_STATE_H
#define REVEAL_INPUT_STATE_H

#include "reveal/impl/gui_metrics.h"
#include "reveal/stdint.h"

namespace Reveal {

// -------------------------------------------------------------------------------
/*!	
	\brief Captures an input state of a mouse and some buttons;
*/
// -------------------------------------------------------------------------------
class InputState
{
public:

	/*!
		Bitflags for all the supported buttons;

		You can use these bitflags to test whether a button is down, pressed or released. 
		It is possible to bit-wise OR or AND them.
	*/
    enum Buttons
    {
        CONFIRM			 = 1 <<  0,		//!< Probably Control pad button A, or enter key
        BACK			 = 1 <<  1,		//!< Probably Control pad button B, or backspace key

		AUX_1			 = 1 <<  2,		//!< Auxillary button (probably right-mouse, shift-key, and console-pad button X);
		AUX_2			 = 1 <<  3,		//!< Auxillary button (probably ctrl-key, and console-pad button Y);

		AUX_3			 = 1 <<  4,		//!< Auxillary button (for example L2 on ps2, and white on Xbox);
		AUX_4			 = 1 <<  5,		//!< Auxillary button (for example R2 on ps2, and black on Xbox);


		LEFT_MOUSE		 = 1 <<  6,     //!< Left mouse button, no console equivalent;
		RIGHT_MOUSE		 = 1 <<  7,     //!< Right mouse button, no console equivalent;
		MIDDLE_MOUSE     = 1 <<  8,		//!< Middle mouse button, no console equivalent;
		       
        LEFT             = 1 << 10,		//!< Left-key (probably D-Pad left on a console-pad);
        RIGHT            = 1 << 11,		//!< Right-key (probably D-Pad left on a console-pad);
        UP               = 1 << 12,		//!< Up-key (probably D-Pad left on a console-pad);
        DOWN             = 1 << 13,		//!< Down-key (probably D-Pad left on a console-pad);

        ZERO             = 1 << 15,		//!< Keyboard 0, no console equivalent;
        ONE              = 1 << 16,		//!< Keyboard 1, no console equivalent;
        TWO              = 1 << 17,		//!< Keyboard 2, no console equivalent;
        THREE            = 1 << 18,		//!< Keyboard 3, no console equivalent;
        FOUR             = 1 << 19,		//!< Keyboard 4, no console equivalent;
        FIVE             = 1 << 20,		//!< Keyboard 5, no console equivalent;
        SIX              = 1 << 21,		//!< Keyboard 6, no console equivalent;
        SEVEN            = 1 << 22,		//!< Keyboard 7, no console equivalent;
        EIGHT            = 1 << 23,		//!< Keyboard 8, no console equivalent;
        NINE             = 1 << 24,		//!< Keyboard 9, no console equivalent;

        PERIOD           = 1 << 27,		//!< Keyboard period '.' key, no console equivalent;

		AUX_5			 = 1 << 28,		//!< Auxillary button (for example L3 on ps2, and left analog-push on Xbox);
		AUX_6			 = 1 << 29,		//!< Auxillary button (for example R3 on ps2, and right analog-push on Xbox);

		SELECT			 = 1 << 30,		//!< Duh;
		START			 = 1 << 31,		//!< Duh;

        MAX_BUTTON_TYPES = 1 << 31		//!< Limiter, to remind people that 32 values is the limit (on current platforms, mwauhauhauaha);
    };

	/*!
		\brief Default constructor,
		
		Puts the mouse at the center of GUI space, and resets the button state.
	*/
	InputState() : mDown( 0 ), 
				   mMouseX( Impl::GuiMetrics::SCREEN_WIDTH / 2 ), 
				   mMouseY( Impl::GuiMetrics::SCREEN_HEIGHT / 2 ), 
				   mPressed( 0 ), 
				   mReleased( 0 ), 
				   mChanged( false ) {}

	/*!
		\brief Default destructor,	
	*/
	virtual ~InputState() {}

	/*!
		\brief Updates the input state based on the parameters;
	*/
	void Update();
	
	/*!
		\brief Tests whether one or more buttons are currently down;
		\param key	One or more of the button bitwise identifiers;
		\return true if the button is down, false otherwise;
	*/
	bool Down( Reveal::uint32_t key ) const { return 0 != (DoDown() & key); }

	//! Returns the bitwise combination of all keys that are currently down;
	Reveal::uint32_t Down() const { return DoDown(); }
	
	/*!
		\brief Returns true if at least one key status changed since the last update;
	*/
	bool KeyChanged() const { return mPressed || mReleased; }

	/*!
		\brief Tests whether one or more buttons went from up to down since the previous call to update
		\param key	One or more of the button bitwise identifiers.
		\return true if the button is pressed, false otherwise;
	*/
    bool Pressed( Reveal::uint32_t key ) const { return 0 != (mPressed & key);  }

	/*!
		\brief Tests whether one or more buttons went from down to up since the previous call to update
		\param key	One or more of the button bitwise identifiers.
		\return true if the button is released, false otherwise;
	*/
    bool Released( Reveal::uint32_t key ) const { return 0 != (mReleased & key); }

	/*!
		\brief Returns the absolute horizontal mouse position
		\return Horizontal mouse positing in the range [0, Impl::GuiMetrics::SCREEN_WIDTH]
	*/
    int  MouseX() const { return mMouseX; }
	
	/*!
		\brief Returns the absolute horizontal mouse position
		\return Vertical mouse positing in the range [0, Impl::GuiMetrics::SCREEN_HEIGHT]
	*/
    int  MouseY() const { return mMouseY; }

	/*!
		\brief Returns the relative horizontal mouse movement since the previous call to update;
	*/
	int MouseRelX() const { return DoMouseRelX(); }

	/*!
		\brief Returns the relative vertical mouse movement since the previous call to update;
	*/
	int MouseRelY() const { return DoMouseRelY(); }

	/*!
		\brief Returns true if the mouse moved since the previous call to update;
	*/
	bool MouseMove() const { return (DoMouseRelX() != 0) || (DoMouseRelY() != 0); }

	/*!
		Returns the relative horizontal movement of an auxillary device (right stick on a console for example);
	*/
	int AuxRelX() const { return DoAuxRelX(); }

	/*!
		Returns the relative vertical movement of an auxillary device (right stick on a console for example);
	*/
	int AuxRelY() const { return DoAuxRelY(); }

	/*!
		Returns the pressure on a left shoulder button (or a constant pressure on a key on your keyboard for example);
	*/
	int LeftShoulder() const { return DoLeftShoulder(); }

	/*!
		Returns the pressure on a right shoulder button (or a constant pressure on a key on your keyboard for example);
	*/
	int RightShoulder() const { return DoRightShoulder(); }

	/*!
		Returns the relative movement of a scroll wheel (probably not available on a controller);
	*/
	int ScrollWheel() const { return DoScrollWheel(); }

	/*!
		Returns the name of the button (if available, otherwise empty string)
	*/
	virtual const char* GetName (Buttons /*button*/) { return ""; }

protected:

	/*!
		\brief Updates a single button (as opposed to more than one)
		\param down		true if the button is currently down;
		\param button	bitwise button identifier (only one bit set);
	*/
    void UpdateSingleButton( bool down, Reveal::uint32_t button );

	/*! 
		This function can be re-implemented by derived classes.
		It is called as the first thing in the InputState::Update() method, so
		it gives you a change to update your own state, so the other virtuals (DoDown(), DoMouseRelX() 
		and DoMouseRelY()) return the right thing;
	*/
	virtual void			 DoUpdate() {}

	//! Function must be implemented, returns the bitwise combination of the InputState::Buttons values of all current buttons that are down;
	virtual Reveal::uint32_t DoDown() const = 0;

	//! Must be implemented, returns the relative horizontal mouse movement;
	virtual int				 DoMouseRelX() const = 0;
	
	//! Must be implemented, returns the vertical horizontal mouse movement;
	virtual int				 DoMouseRelY() const = 0;

	//! Can be implemented by derived classes, returns the relative horizontal auxillary movement, for example a right stick x;
	virtual int				 DoAuxRelX() const { return 0; }
	
	//! Can be implemented by derived classes, returns the relative horizontal auxillary movement, for example a right stick x;
	virtual int				 DoAuxRelY() const { return 0; }

	//! Can be implemented by derived classes;
	virtual int				 DoLeftShoulder() const { return 0; }
	
	//! Can be implemented by derived classes;
	virtual int				 DoRightShoulder() const { return 0; }

	//! Can be implemented by derived classes;
	virtual int				 DoScrollWheel() const { return 0; }

	//! Tests whether only one button is set in the given number;
    static bool OnlyOneBitSet( Reveal::uint32_t n ) { return (n > 0) && !(n & (n - 1)); }

private:

	Reveal::uint32_t mDown;		//!< Keys that are currently down;
	int				 mMouseX;	//!< Current absolute mouse X;
    int				 mMouseY;	//!< Current absolute mouse Y;
    Reveal::uint32_t mPressed;	//!< Bitwise combination of all buttons that went from up to down since the previous call to update;
    Reveal::uint32_t mReleased;	//!< Bitwise combination of all buttons that went from down to up since the previous call to update;
	bool			 mChanged;	//!< True if the input state changed in between the last two calls to update;
};

} // Namespace Reveal;

#endif
