// Open SanAndreas
// (c) 2006-2008 Stanislav "listener" Golovin
// 
// input device handling (CInputEvents и CPad)

// internal keycode values
enum RsKeyCode {
	rsESC		= 0x3E8,
	rsF1		= 0x3E9,
	rsF2		= 0x3EA,
	rsF3		= 0x3EB,
	rsF4		= 0x3EC,
	rsF5		= 0x3ED,
	rsF6		= 0x3EE,
	rsF7		= 0x3EF,
	rsF8		= 0x3F0,
	rsF9		= 0x3F1,
	rsF10		= 0x3F2,
	rsF11		= 0x3F3,
	rsF12		= 0x3F4,
	rsINSERT	= 0x3F5,
	rsDEL		= 0x3F6,
	rsHOME		= 0x3F7,
	rsEND		= 0x3F8,
	rsPGUP		= 0x3F9,
	rsPGDN		= 0x3FA,
	rsUP		= 0x3FB,
	rsDOWN		= 0x3FC,
	rsLEFT		= 0x3FD,
	rsRIGHT		= 0x3FE,
	rsDIV		= 0x3FF,	// keypad '/'
	rsMUL		= 0x400,	// keypad '*'
	rsADD		= 0x401,	// keypad '+'
	rsSUB		= 0x402,	// keypad '-'
	rsEXTDEL	= 0x403,
	rsDECIMAL	= 0x403,
	rsEXTEND	= 0x404,
	rsNUM1		= 0x404,
	rsEXTDOWN	= 0x405,
	rsNUM2		= 0x405,
	rsEXTPGDN	= 0x406,
	rsNUM3		= 0x406,
	rsEXTLEFT	= 0x407,
	rsNUM4		= 0x407,
	rsNUM5		= 0x408,
	rsNUMLOCK	= 0x409,
	rsEXTRIGHT	= 0x40A,
	rsNUM6		= 0x40A,
	rsEXTHOME	= 0x40B,
	rsNUM7		= 0x40B,
	rsEXTUP		= 0x40C,
	rsNUM8		= 0x40C,
	rsEXTPGUP	= 0x40D,
	rsNUM9		= 0x40D,
	rsEXTINSERT = 0x40E,
	rsNUM0		= 0x40E,
	rsENTER		= 0x40F,
	rsSCROLL	= 0x410,
	rsPAUSE		= 0x411,
	rsBACK		= 0x412,
	rsTAB		= 0x413,
	rsCAPSLOCK	= 0x414,
	rsEXTENTER	= 0x415,
	rsLSHIFT	= 0x416,
	rsRSHIFT	= 0x417,
	rsSHIFT		= 0x418,
	rsLCTRL		= 0x419,
	rsRCTRL		= 0x41A,
	rsLMENU		= 0x41B,
	rsRMENU		= 0x41C,
	rsLWIN		= 0x41D,
	rsRWIN		= 0x41E,
	rsAPPS		= 0x41F,
	rsNULL		= 0x420
};

enum RsMouseButtons {
	rsLMB		= 0,
	rsRMB		= 1,
	rsMMB		= 2,
	rsWHEELUP	= 3,
	rsWHEELDOWN	= 4,
	rsBMX1		= 5,
	rsBMX2		= 6,
	rsMBNONE	= 7	// 
};
enum InputEvent {
	evPED_FIREWEAPON			= 0,
	evPED_FIREWEAPON_ALT		= 1,
	evPED_CYCLE_WEAPON_RIGHT	= 2,
	evPED_CYCLE_WEAPON_LEFT		= 3,
	evGO_FORWARD				= 4,
	evGO_BACK					= 5,
	evGO_LEFT					= 6,
	evGO_RIGHT					= 7,
	evPED_SNIPER_ZOOM_IN		= 8,
	evPED_SNIPER_ZOOM_OUT		= 9,
	evVEHICLE_ENTER_EXIT		= 10,
	evCAMERA_CHANGE_VIEW_ALL_SITUATIONS	= 11,
	evPED_JUMPING				= 12,
	evPED_SPRINT				= 13,
	evPED_LOOKBEHIND			= 14,
	evPED_DUCK					= 15,
	evPED_ANSWER_PHONE			= 16,
	evSNEAK_ABOUT				= 17,
	evVEHICLE_FIREWEAPON		= 18,
	evVEHICLE_FIREWEAPON_ALT	= 19,
	evVEHICLE_STEERLEFT			= 20,
	evVEHICLE_STEERRIGHT		= 21,
	evVEHICLE_STEERUP			= 22,
	evVEHICLE_STEERDOWN			= 23,
	evVEHICLE_ACCELERATE		= 24,
	evVEHICLE_BRAKE				= 25,
	evVEHICLE_RADIO_STATION_UP	= 26,
	evVEHICLE_RADIO_STATION_DOWN= 27,
	evVEHICLE_HORN				= 29,
	evTOGGLE_SUBMISSIONS		= 30,
	evVEHICLE_HANDBRAKE			= 31,
	evPED_1RST_PERSON_LOOK_LEFT	= 32,
	evPED_1RST_PERSON_LOOK_RIGHT= 33,
	evVEHICLE_LOOKLEFT			= 34,
	evVEHICLE_LOOKRIGHT			= 35,
	evVEHICLE_LOOKBEHIND		= 36,
	evVEHICLE_MOUSELOOK			= 37,
	evVEHICLE_TURRETLEFT		= 38,
	evVEHICLE_TURRETRIGHT		= 39,
	evVEHICLE_TURRETUP			= 40,
	evVEHICLE_TURRETDOWN		= 41,
	evPED_CYCLE_TARGET_LEFT		= 42,
	evPED_CYCLE_TARGET_RIGHT	= 43,
	evPED_CENTER_CAMERA_BEHIND_PLAYER	= 44,
	evPED_LOCK_TARGET			= 45,
	evNETWORK_TALK				= 46,
	evCONVERSATION_YES			= 47,
	evCONVERSATION_NO			= 48,
	evGROUP_CONTROL_FWD			= 49,
	evGROUP_CONTROL_BWD			= 50,
	evPED_1RST_PERSON_LOOK_UP	= 51,
	evPED_1RST_PERSON_LOOK_DOWN	= 52,
	evTOGGLE_DPAD				= 54,
	evSWITCH_DEBUG_CAM_ON		= 55,
	evTAKE_SCREEN_SHOT			= 56,
	evSHOW_MOUSE_POINTER_TOGGLE	= 57,
	evLAST	= 0x7FFFFFFF
};

#define EVENT_COUNT	59

#pragma pack(push, 1)
// keyboard scan results
// NOTE: this structure is different from original in-game structure
struct KeyState {
	WORD	stdKeys[256];	
	WORD	extKeys[56];	// rsESC-rsNULL, access as extKeys[keycode-rsESC];
};

// TODO: DIMOUSESTATE2
struct MouseState {	
	BYTE	lmb;
	BYTE	rmb;
	BYTE	mmb;
	BYTE	wheelUp;
	BYTE	wheelDn;
	BYTE	bmx1;
	BYTE	bmx2;
	BYTE	__align;
	float	Z;
	float	X;
	float	Y;
};


struct CPadConfig {
	DWORD	_f0;
	BYTE	present;
	BYTE	zAxisPresent;
	BYTE	rzAxisPresent;
	BYTE	__align;
	DWORD	vendorID;
	DWORD	productID;
};

class CInputEvents {
public:	

	void	initialize ();	// >> 530500 (3)

	// called from CPad::update
	void	processKeyboard ();	// >> 531140
	void	processMouse ();

	bool	isKeyPressed (RsKeyCode key);	// >> 52DDB0 (4)

	// control mapping
	void	mapAllButtons ();	// >> 531F20 (3)
	void	mapKeyboard ();
	void	mapMouseButtons ();	// >> 52F6F0 (2)
	void	mapPadButtons (int buttonCount);	// >> 530B00 (2)
	void	mapKey (InputEvent event, RsKeyCode	key, int type);	// >> 530640 (3) -- map keyboard or joypad button
	void	mapMouseButton (InputEvent event, int button);	// >> 52F590

	void	initPriority (InputEvent event);	// >> 52F5F0 (3/5)


	void	initEventNames ();	// >> 52D260 (1)
	void	initEvent (InputEvent event, DWORD type);// >> 52FD70	(59)

	
	bool	isKeyJustPressed (RsKeyCode key);	// >> 52E450 (5)

	int		getPadButtonJustPressed ();	// >> 52D1E0 (2)
	

	void mapEvent (InputEvent event, RsKeyCode keycode, DWORD type); // >> 530490 (141)


	void writeToSettings (DWORD * data);	//	>> 52D200 (modified)
	bool readFromSettings (DWORD * data);	// 


	struct Event {
		DWORD keycode;
		DWORD priority;
	};

	BYTE		_f0;
	BYTE		_f1;
	BYTE		_f2;
	BYTE		_f3;
	DIJOYSTATE2	prevPadState;
	DIJOYSTATE2	currPadState;
	char		eventNames[EVENT_COUNT][40];
	BYTE		_fB5C[17];	// pad buttons ?
	BYTE		_align[3];
	Event		events[EVENT_COUNT][4];
	BYTE		_f12D0[16];
	BYTE		_f12E0;
	BYTE		_align2[3];
};

// TODO: вообще-то структура должна называться PlayerInputData, т.к. для пада в ней хранятся аналоговые значения
// TODO: переименовать поля
struct PadData {
	short	keys[24];

	void init () { memset (this, 0, sizeof (PadData)); }
};


struct CPad {

	bool	isAnyPuttonPressed ();	// >> 53EFF0 (2)

	void initialize (BYTE a0, BYTE a4); // >> 541A70 (14)

	static void		update ();	// main input processing function
	void			processMouse ();	// >> 53F3C0 (CPad::update)
	void			processPad (int padNum);	// >> 746A10 (CPad::update)

	HRESULT			getMouseState (DIMOUSESTATE2 * dm);	// >> 746ED0 (CPad::processMouse)	TODO: move to CMouse (ogPlatform.h)
	bool			isMouseUsed ();

	PadData			padData;	// 000
	PadData			_f30;	// 030  // prevPadState
	DWORD			_f60;	// 060
	DWORD			_f64;	// 064
	DWORD			_f68;	// 068
	DWORD			_f6C;	// 06C
	DWORD			_f70;	// 070
	DWORD			_f74;	// 074
	PadData			_f78;	// 078 // kbdData1
	PadData			_fA8;	// 0A8 // kbdData2
	PadData			_fD8;	// 0D8
	BYTE			_f108;	// 108
	BYTE			_f109;	// 109
	WORD			_f10A;	// 10A (state 0..3) // controlState
	WORD			_f10C;	// 10C
	WORD			_f10E;	// 10E // stateFlags
	BYTE			_f110;	// 110
	DWORD			_f111;	// 111
	BYTE			_f115;	// 115
	BYTE			_f116;	// 116
	BYTE			_f117;	// 117 // menuActive
	BYTE			_f118;	// 118 // DisableVehicleControls16&14
	BYTE			_f119;	// 119 // DisableEnterExitVehicleButton
	BYTE			_f11A;	// 11A // DisableCrouchButton 
	BYTE			_f11B;	// 11B // DisableFireButton 
	BYTE			_f11C;	// 11C
	BYTE			_f11D;	// 11D // DisableScrollWeaponButtons 
	BYTE			_f11E;	// 11E // DisableJumpButton 
	BYTE			_f11F;	// 11F // DisableActionButton 
	DWORD			padQueryTime;	// 120
	DWORD			_f124;	// 124
	DWORD			_f128;	// 128
	DWORD			_f12C;	// 12C
	BYTE			_f130;	// 130
	BYTE			_f131;	// 131
	WORD			_f132;	// 132
};

struct PsGlobalType {
	HWND		window;
	HINSTANCE	instance;
	DWORD		fullscreen;
	float		lastMousePosX;
	float		lastMousePosY;
	// ... TODO: more fields here
//	RwV2d       lastMousePos;
	DWORD		_f14;	// TODO: unknown yet
	LPDIRECTINPUT8			diInterface;
	LPDIRECTINPUTDEVICE8	diMouse;
	LPDIRECTINPUTDEVICE8	diDevice1;
	LPDIRECTINPUTDEVICE8	diDevice2;
};
#pragma pack(pop)

extern CInputEvents * inputEvents;
extern CPad * pad;

void diMouseInit (BYTE exclusive);


// mouse states (triple-buffered :-)
extern MouseState * activeMouseState;
extern MouseState * currMouseState;
extern MouseState * prevMouseState;

// TODO: move to CMenuManager
extern BYTE	* mouseInvertX;
extern BYTE	* mouseInvertY;

extern CPadConfig * padConfig;
extern PsGlobalType * ps;	// TODO: make static

extern BYTE (*isCurrentModeFullscreen)();	// TODO: перенести в более адекватное место
