//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//
//===========================================================================//
#ifndef INPUTSYSTEM_H
#define INPUTSYSTEM_H
#ifdef _WIN32
#pragma once
#endif


#ifdef WIN32
#if !defined( _X360 )
#define _WIN32_WINNT 0x502
#include <windows.h>
#include <zmouse.h>
#include "xbox/xboxstubs.h"
#include "../../dx9sdk/include/XInput.h"
#endif
#endif

#ifdef POSIX
#include "posix_stubs.h"
#endif // POSIX

#include "appframework/ilaunchermgr.h"

#include "inputsystem/iinputsystem.h"
#include "tier2/tier2.h"

#include "inputsystem/ButtonCode.h"
#include "inputsystem/AnalogCode.h"
#include "bitvec.h"
#include "tier1/utlvector.h"
#include "tier1/utlflags.h"

#if defined( _X360 )
#include "xbox/xbox_win32stubs.h"
#include "xbox/xbox_console.h"
#endif

#include "steam/steam_api.h"

#define TOUCH_FINGER_MAX_COUNT 10

//-----------------------------------------------------------------------------
// Implementation of the input system
//-----------------------------------------------------------------------------
class CInputSystem : public CTier2AppSystem< IInputSystem >
{
	typedef CTier2AppSystem< IInputSystem > BaseClass;

public:
	// Constructor, destructor
	CInputSystem();
	virtual ~CInputSystem();

	// Inherited from IAppSystem
	virtual	InitReturnVal_t Init();
	virtual bool Connect( CreateInterfaceFn factory );

	virtual void Shutdown();

	// Inherited from IInputSystem
	virtual void AttachToWindow( void* hWnd );
	virtual void DetachFromWindow();
	virtual void EnableInput( bool bEnable );
	virtual void EnableMessagePump( bool bEnable );
	virtual int GetPollTick() const;
	virtual void PollInputState();
	virtual bool IsButtonDown( ButtonCode_t code ) const;
	virtual int GetButtonPressedTick( ButtonCode_t code ) const;
	virtual int GetButtonReleasedTick( ButtonCode_t code ) const;
	virtual int GetAnalogValue( AnalogCode_t code ) const;
	virtual int GetAnalogDelta( AnalogCode_t code ) const;
	virtual int GetEventCount() const;
	virtual const InputEvent_t* GetEventData() const;
	virtual void PostUserEvent( const InputEvent_t &event );
	virtual int GetJoystickCount() const;
	virtual void EnableJoystickInput( int nJoystick, bool bEnable );
	virtual void EnableJoystickDiagonalPOV( int nJoystick, bool bEnable );
	virtual void SampleDevices( void );
	virtual void SetRumble( float fLeftMotor, float fRightMotor, int userId );
	virtual void StopRumble( void );
	virtual void ResetInputState( void );
	virtual void SetPrimaryUserId( int userId );
	virtual const char *ButtonCodeToString( ButtonCode_t code ) const;
	virtual const char *AnalogCodeToString( AnalogCode_t code ) const;
	virtual ButtonCode_t StringToButtonCode( const char *pString ) const;
	virtual AnalogCode_t StringToAnalogCode( const char *pString ) const;
	virtual ButtonCode_t VirtualKeyToButtonCode( int nVirtualKey ) const;
	virtual int ButtonCodeToVirtualKey( ButtonCode_t code ) const;
	virtual ButtonCode_t ScanCodeToButtonCode( int lParam ) const;
	virtual void SleepUntilInput( int nMaxSleepTimeMS );
	virtual int GetPollCount() const;
	virtual void SetCursorPosition( int x, int y );
#if defined( WIN32 ) && !defined ( _X360 )
	virtual void *GetHapticsInterfaceAddress() const;
#else
	virtual void *GetHapticsInterfaceAddress() const { return NULL;}	
#endif
	bool GetRawMouseAccumulators( int& accumX, int& accumY );
	virtual bool GetTouchAccumulators( int fingerId, float &dx, float &dy );

	virtual void SetConsoleTextMode( bool bConsoleTextMode );

	// Windows proc
	LRESULT WindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );


private:
	enum
	{
		STICK1_AXIS_LEFT,
		STICK1_AXIS_RIGHT,
		STICK1_AXIS_DOWN,
		STICK1_AXIS_UP,
		STICK2_AXIS_LEFT,
		STICK2_AXIS_RIGHT,
		STICK2_AXIS_DOWN,
		STICK2_AXIS_UP,
		MAX_STICKAXIS
	};

	// track Xbox stick keys from previous frame
	enum
	{
		LASTKEY_STICK1_X,
		LASTKEY_STICK1_Y,
		LASTKEY_STICK2_X,
		LASTKEY_STICK2_Y,
		MAX_LASTKEY,
	};

	enum
	{
		INPUT_STATE_QUEUED = 0,
		INPUT_STATE_CURRENT,

		INPUT_STATE_COUNT,
	};

public:

	struct JoystickInfo_t
	{
		void *m_pDevice;  // Really an SDL_GameController*, NULL if not present.
		void *m_pHaptic;  // Really an SDL_Haptic*
		float m_fCurrentRumble;
		bool m_bRumbleEnabled;
		int m_nButtonCount;
		int m_nAxisFlags;
		int m_nDeviceId;
		bool m_bHasPOVControl;
		bool m_bDiagonalPOVControlEnabled;
		unsigned int m_nFlags;
		unsigned int m_nLastPolledButtons;
		unsigned int m_nLastPolledAxisButtons;
		unsigned int m_nLastPolledPOVState;
		unsigned long m_pLastPolledAxes[MAX_JOYSTICK_AXES];
	};

	struct xdevice_t
	{
		int					userId;
		byte				type;
		byte				subtype;
		word				flags;
		bool				active;
		XINPUT_STATE		states[2];
		int					newState;
		xKey_t				lastStickKeys[MAX_LASTKEY];
		int					stickThreshold[MAX_STICKAXIS];
		float				stickScale[MAX_STICKAXIS];
		int					quitTimeout;
		int					dpadLock;
		// rumble
		XINPUT_VIBRATION	vibration;
		bool				pendingRumbleUpdate;
	};

	struct appKey_t
	{
		int repeats;
		int	sample;
	};

	struct InputState_t
	{
		// Analog states
		CBitVec<BUTTON_CODE_LAST> m_ButtonState;
		int m_ButtonPressedTick[BUTTON_CODE_LAST];
		int m_ButtonReleasedTick[BUTTON_CODE_LAST];
		int m_pAnalogDelta[ANALOG_CODE_LAST];
		int m_pAnalogValue[ANALOG_CODE_LAST];
		CUtlVector< InputEvent_t > m_Events;
		bool m_bDirty;
	};

	// Initializes all Xbox controllers
	void InitializeXDevices( void );

	// Opens an Xbox controller
	void OpenXDevice( xdevice_t* pXDevice, int userId );

	// Closes an Xbox controller
	void CloseXDevice( xdevice_t* pXDevice );

	// Samples the Xbox controllers
	void PollXDevices( void );

	// Samples an Xbox controller and queues input events
	void ReadXDevice( xdevice_t* pXDevice );

	// Submits force feedback data to an Xbox controller
	void WriteToXDevice( xdevice_t* pXDevice );

	// Sets rumble values for an Xbox controller
	void SetXDeviceRumble( float fLeftMotor, float fRightMotor, int userId );

	// Posts an Xbox key event, ignoring key repeats 
	void PostXKeyEvent( int nUserId, xKey_t xKey, int nSample );

	// Dispatches all joystick button events through the game's window procs
	void ProcessEvent( UINT uMsg, WPARAM wParam, LPARAM lParam );

	// Initializes joysticks
	void InitializeJoysticks( void );

	// Shut down joysticks
	void ShutdownJoysticks( void );

	// Samples the joystick
	void PollJoystick( void );

	// Update the joystick button state
	void UpdateJoystickButtonState( int nJoystick );

	// Update the joystick POV control
	void UpdateJoystickPOVControl( int nJoystick );

	// Record button state and post the event
	void JoystickButtonEvent( ButtonCode_t button, int sample );
	
	// Init touch
	void InitializeTouch( void );
	
	// Shut down touch	
	void ShutdownTouch( void );
	

#if defined( WIN32 ) && !defined ( _X360 )
	// NVNT attaches window to novint devices
	void AttachWindowToNovintDevices( void * hWnd );

	// NVNT detaches window from novint input
	void DetachWindowFromNovintDevices( void );

	// NVNT Initializes novint devices
	void InitializeNovintDevices( void );

	// NVNT Samples a novint device
	void PollNovintDevices( void );

	// NVNT Update the novint device button state
	void UpdateNovintDeviceButtonState( int nDevice );

	// NVNT Record button state and post the event
	void NovintDeviceButtonEvent( ButtonCode_t button, int sample );

	//Added called and set to true when binding input and set to false once bound
	void SetNovintPure( bool bPure );
#else
	void SetNovintPure( bool bPure ) {} // to satify the IInput virtual interface	
#endif
	// Chains the window message to the previous wndproc
	LRESULT ChainWindowMessage( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam );

	// Post an event to the queue
	void PostEvent( int nType, int nTick, int nData = 0, int nData2 = 0, int nData3 = 0 );

	// Deals with app deactivation (sends a bunch of button released messages)
	void ActivateInputSystem( bool bActivated );

	// Determines all mouse button presses
	int ButtonMaskFromMouseWParam( WPARAM wParam, ButtonCode_t code = BUTTON_CODE_INVALID, bool bDown = false ) const;

	// Updates the state of all mouse buttons
	void UpdateMouseButtonState( int nButtonMask, ButtonCode_t dblClickCode = BUTTON_CODE_INVALID );

	// Copies the input state record over
	void CopyInputState( InputState_t *pDest, const InputState_t &src, bool bCopyEvents );

	// Post an button press/release event to the queue
	void PostButtonPressedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode );
	void PostButtonReleasedEvent( InputEventType_t nType, int nTick, ButtonCode_t scanCode, ButtonCode_t virtualCode );

	// Release all buttons
	void ReleaseAllButtons( int nFirstButton = 0, int nLastButton = BUTTON_CODE_LAST - 1 );

	// Zero analog state
	void ZeroAnalogState( int nFirstState, int nLastState );

	// Converts xbox keys to button codes
	ButtonCode_t XKeyToButtonCode( int nUserId, int nXKey ) const;

	// Computes the sample tick
	int ComputeSampleTick();

	// Clears the input state, doesn't generate key-up messages
	void ClearInputState();

	// Called for mouse move events. Sets the current x and y and posts events for the mouse moving.
	void UpdateMousePositionState( InputState_t &state, short x, short y );

	// Initializes SteamControllers - Returns true if steam is running and finds controllers, otherwise false
	bool InitializeSteamControllers( void );

	// Returns number of connected Steam Controllers
	uint32 GetNumSteamControllersConnected();

	// Update and sample steam controllers
	void PollSteamControllers( void );

#if defined( PLATFORM_WINDOWS_PC )
	void PollInputState_Windows();
#endif

	void JoystickHotplugAdded( int joystickIndex );
	void JoystickHotplugRemoved( int joystickId );
	void JoystickButtonPress( int joystickId, int button ); // button is a SDL_CONTROLLER_BUTTON;
	void JoystickButtonRelease( int joystickId, int button ); // same as above.
	void JoystickAxisMotion( int joystickId, int axis, int value );

	void FingerEvent( int eventType, int fingerId, float x, float y, float dx, float dy );	
	
	// Steam Controller
	void ReadSteamController( int iIndex );
	void PostKeyEvent( int iIndex, sKey_t sKey, int nSample );
	const int GetSteamPadDeadZone( ESteamPadAxis axis );
	bool IsSteamControllerConnected( void ) { return m_bSteamController; }
	bool IsSteamControllerActive( void );
	void ActivateSteamControllerActionSetForSlot( uint64 nSlot, GameActionSet_t eActionSet );
	ControllerActionSetHandle_t GetActionSetHandle( GameActionSet_t eActionSet );
	ControllerActionSetHandle_t GetActionSetHandle( const char* szActionSet );
	bool GetControllerStateForSlot( int nSlot );
	int GetSteamControllerIndexForSlot( int nSlot );
	bool GetRadialMenuStickValues( int nSlot, float &fX, float &fY );
	virtual ISteamController* SteamControllerInterface();
	bool InitializeSteamControllerActionSets();

	// Gets the action origin (i.e. which physical input) maps to the given action name
	virtual EControllerActionOrigin GetSteamControllerActionOrigin( const char* action, GameActionSet_t action_set );
	virtual EControllerActionOrigin GetSteamControllerActionOrigin( const char* action, ControllerActionSetHandle_t action_set_handle );

	// Maps a Steam Controller action origin to a string (consisting of a single character) in our SC icon font
	virtual const wchar_t*	GetSteamControllerFontCharacterForActionOrigin( EControllerActionOrigin origin );

	// Maps a Steam Controller action origin to a short text string (e.g. "X", "LB", "LDOWN") describing the control.
	// Prefer to actually use the icon font wherever possible.
	virtual const wchar_t* GetSteamControllerDescriptionForActionOrigin( EControllerActionOrigin origin );

	// Converts SteamController keys to button codes
	ButtonCode_t SKeyToButtonCode( int nUserId, int nXKey ) const;

	// This is called with "true" by dedicated server initialization (before calling Init) in order to
	// force us to skip initialization of Steam (which messes up dedicated servers).
	virtual void SetSkipControllerInitialization( bool bSkip )
	{
		m_bSkipControllerInitialization = bSkip;
	}

	virtual void StartTextInput();

#if defined( USE_SDL )
	void PollInputState_Platform();

	ILauncherMgr *m_pLauncherMgr;
#endif

	WNDPROC m_ChainedWndProc;
	HWND m_hAttachedHWnd;
	bool m_bEnabled;
	bool m_bPumpEnabled;
	bool m_bIsPolling;

	// Current button state
	InputState_t m_InputState[INPUT_STATE_COUNT];

	// Current action set
	GameActionSet_t m_currentActionSet[STEAM_CONTROLLER_MAX_COUNT];

	DWORD m_StartupTimeTick;
	int m_nLastPollTick;
	int m_nLastSampleTick;
	int m_nPollCount;

	// Mouse wheel hack
	UINT m_uiMouseWheel;

	// Joystick info
	CUtlFlags<unsigned short> m_JoysticksEnabled;
	int m_nJoystickCount;
	bool m_bJoystickInitialized;
	bool m_bTouchInitialized;
	bool m_bXController;
	JoystickInfo_t m_pJoystickInfo[ MAX_JOYSTICKS ];

	// Steam Controller
	struct steampad_t
	{
		steampad_t()
		{
			m_nHardwareIndex = 0;
			m_nJoystickIndex = INVALID_USER_ID;
			m_nLastPacketIndex = 0;
			active = false;
			memset( lastAnalogKeys, 0, sizeof( lastAnalogKeys ) );
		}
		bool				active;

		sKey_t				lastAnalogKeys[MAX_STEAMPADAXIS];
		appKey_t			m_appSKeys[SK_MAX_KEYS];
		// Hardware index and joystick index don't necessarily match
		// Joystick index will depend on the order of multiple initialized devices
		// Which could include other controller types
		// Hardware index should line up 1:1 with the order they're polled
		// and could change based on removing devices, unlike Joystick Index
		uint32				m_nHardwareIndex;
		int					m_nJoystickIndex;
		uint32				m_nLastPacketIndex;
	};

	float m_pRadialMenuStickVal[STEAM_CONTROLLER_MAX_COUNT][2];
	steampad_t m_Device[STEAM_CONTROLLER_MAX_COUNT];
	uint32 m_unNumConnected;
	float m_flLastSteamControllerInput;
	int m_nJoystickBaseline;
	int m_nControllerType[MAX_JOYSTICKS+STEAM_CONTROLLER_MAX_COUNT];

	bool m_bSteamController;						// true if the Steam Controller system has been initialized successfully (this doesn't mean one is actually connected necessarily)
	bool m_bSteamControllerActionsInitialized;		// true if our action sets and handles were successfully initialized (this doesn't mean a controller is necessarily connected, or that in-game client actions were initialized)
	bool m_bSteamControllerActive;					// true if our action sets and handles were successfully initialized *and* that at least one controller is actually connected and switched on.

#if defined( WIN32 ) && !defined ( _X360 )
	// NVNT Novint device info
	int m_nNovintDeviceCount;
	bool m_bNovintDevices;
#endif

	// Xbox controller info
	appKey_t	m_appXKeys[ XUSER_MAX_COUNT ][ XK_MAX_KEYS ];
	xdevice_t	m_XDevices[ XUSER_MAX_COUNT ];
	int			m_PrimaryUserId;

	// raw mouse input
	bool m_bRawInputSupported;
	int	 m_mouseRawAccumX, m_mouseRawAccumY;

	float m_touchAccumX[TOUCH_FINGER_MAX_COUNT], m_touchAccumY[TOUCH_FINGER_MAX_COUNT];

	// For the 'SleepUntilInput' feature
	HANDLE m_hEvent;

	CSysModule   *m_pXInputDLL;
	CSysModule   *m_pRawInputDLL;
	
#if defined( WIN32 ) && !defined ( _X360 )
	// NVNT falcon module
	CSysModule	 *m_pNovintDLL;
#endif

	bool m_bConsoleTextMode;

public:
	// Steam API context for use by input system for access to steam controllers.
	CSteamAPIContext& SteamAPIContext() { return m_SteamAPIContext; }

private:
	CSteamAPIContext m_SteamAPIContext;
	bool m_bSkipControllerInitialization;
};

#endif // INPUTSYSTEM_H