/* zlib/libpng license below:
Copyright (c) 2004-2009 Pierre-Michel Ricordel (pricorde{AT}rigsofrods{DOT}com), Thomas Fischer (thomas{AT}rigsofrods{DOT}com)

This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.

Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:

    1. The origin of this software must not be misrepresented; you must not
    claim that you wrote the original software. If you use this software
    in a product, an acknowledgment in the product documentation would be
    appreciated but is not required.

    2. Altered source versions must be plainly marked as such, and must not be
    misrepresented as being the original software.

    3. This notice may not be removed or altered from any source
    distribution.
*/

#ifndef __InputEngine_H__
#define __InputEngine_H__


#include "OISEvents.h"
#include "OISInputManager.h"
#include "OISMouse.h"
#include "OISKeyboard.h"
#include "OISJoyStick.h"
//#include <OgreFrameListener.h>
#include <OgreWindowEventUtilities.h>
#include <OgrePrerequisites.h>
#include <OgreUTFString.h>
#include <map>

//class ExampleFrameListener;
// some shortcut
#define INPUTENGINE InputEngine::Instance()

// config filename
#define CONFIGFILENAME "input.map"
#define MAX_JOYSTICKS 10
#define MAX_JOYSTICK_SLIDERS 4
#define MAX_JOYSTICK_AXIS 32

enum grabtypes {
	GRAB_ALL=0,
	GRAB_DYNAMICALLY,
	GRAB_NONE
};

enum eventtypes
{
	ET_NONE=0,
	ET_Keyboard,
	ET_MouseButton,
	ET_MouseAxisX,
	ET_MouseAxisY,
	ET_MouseAxisZ,
	ET_JoystickButton,
	ET_JoystickAxisAbs,
	ET_JoystickAxisRel,
	ET_JoystickPov,
	ET_JoystickSliderX,
	ET_JoystickSliderY
};

enum events
{
	// has to start at zero, since we iterate over it at times
	EV_AIRPLANE_BRAKE=0, //!< normal brake for an aircraft.
	EV_AIRPLANE_ELEVATOR_DOWN, //!< pull the elevator down in an aircraft.
	EV_AIRPLANE_ELEVATOR_UP, //!< pull the elevator up in an aircraft.
	EV_AIRPLANE_FLAPS_FULL, //!< full flaps in an aircraft.
	EV_AIRPLANE_FLAPS_LESS, //!< one step less flaps.
	EV_AIRPLANE_FLAPS_MORE, //!< one step more flaps.
	EV_AIRPLANE_FLAPS_NONE, //!< no flaps.
	EV_AIRPLANE_PARKING_BRAKE, //!< airplane parking brake.
	EV_AIRPLANE_REVERSE, //!< reverse the turboprops
	EV_AIRPLANE_RUDDER_LEFT, //!< rudder left
	EV_AIRPLANE_RUDDER_RIGHT, //!< rudder right
	EV_AIRPLANE_STEER_LEFT, //!< steer left
	EV_AIRPLANE_STEER_RIGHT, //!< steer right
	EV_AIRPLANE_THROTTLE_AXIS, //!< throttle axis. Only use this if you have fitting hardware :) (i.e. a Slider)
	EV_AIRPLANE_THROTTLE_DOWN, //!< decreases the airplane thrust
	EV_AIRPLANE_THROTTLE_FULL, //!< full thrust
	EV_AIRPLANE_THROTTLE_NO, //!< no thrust
	EV_AIRPLANE_THROTTLE_UP, //!< increase the airplane thrust
	EV_AIRPLANE_TOGGLE_ENGINES, //!< switch all engines on / off
	EV_BOAT_CENTER_RUDDER, //!< center the rudder
	EV_BOAT_REVERSE, //!< no thrust
	EV_BOAT_STEER_LEFT, //!< steer left a step
	EV_BOAT_STEER_LEFT_AXIS, //!< steer left (analog value!)
	EV_BOAT_STEER_RIGHT, //!< steer right a step
	EV_BOAT_STEER_RIGHT_AXIS, //!< steer right (analog value!)
	EV_BOAT_THROTTLE_AXIS, //!< throttle axis. Only use this if you have fitting hardware :) (i.e. a Slider)
	EV_BOAT_THROTTLE_DOWN, //!< decrease throttle
	EV_BOAT_THROTTLE_UP, //!< increase throttle
	EV_CAELUM_DECREASE_TIME, //!< decrease day-time
	EV_CAELUM_DECREASE_TIME_FAST, //!< decrease day-time a lot faster
	EV_CAELUM_INCREASE_TIME, //!< increase day-time
	EV_CAELUM_INCREASE_TIME_FAST, //!< increase day-time a lot faster
	EV_CAMERA_CHANGE, //!< change camera mode
	EV_CAMERA_LOOKBACK, //!< look back (toggles between normal and lookback)
	EV_CAMERA_RESET, //!< reset the camera position
	EV_CAMERA_ROTATE_DOWN, //!< rotate camera down
	EV_CAMERA_ROTATE_LEFT, //!< rotate camera left
	EV_CAMERA_ROTATE_RIGHT, //!< rotate camera right
	EV_CAMERA_ROTATE_UP, //!< rotate camera up
	EV_CAMERA_ZOOM_IN, //!< zoom camera in
	EV_CAMERA_ZOOM_IN_FAST, //!< zoom camera in faster
	EV_CAMERA_ZOOM_OUT, //!< zoom camera out
	EV_CAMERA_ZOOM_OUT_FAST, //!< zoom camera out faster
	EV_CHARACTER_BACKWARDS, //!< step backwards with the character
	EV_CHARACTER_FORWARD, //!< step forward with the character
	EV_CHARACTER_JUMP, //!< let the character jump
	EV_CHARACTER_LEFT, //!< rotate character left
	EV_CHARACTER_RIGHT, //!< rotate character right
	EV_CHARACTER_RUN, //!< let the character run
	EV_CHARACTER_SIDESTEP_LEFT, //!< sidestep to the left
	EV_CHARACTER_SIDESTEP_RIGHT, //!< sidestep to the right
	EV_COMMANDS_01, //!< Command 1
	EV_COMMANDS_02, //!< Command 2
	EV_COMMANDS_03, //!< Command 3
	EV_COMMANDS_04, //!< Command 4
	EV_COMMANDS_05, //!< Command 5
	EV_COMMANDS_06, //!< Command 6
	EV_COMMANDS_07, //!< Command 7
	EV_COMMANDS_08, //!< Command 8
	EV_COMMANDS_09, //!< Command 9
	EV_COMMANDS_10, //!< Command 10
	EV_COMMANDS_11, //!< Command 11
	EV_COMMANDS_12, //!< Command 12
	EV_COMMANDS_13, //!< Command 13
	EV_COMMANDS_14, //!< Command 14
	EV_COMMANDS_15, //!< Command 15
	EV_COMMANDS_16, //!< Command 16
	EV_COMMANDS_17, //!< Command 17
	EV_COMMANDS_18, //!< Command 18
	EV_COMMANDS_19, //!< Command 19
	EV_COMMANDS_20, //!< Command 20
	EV_COMMANDS_21, //!< Command 21
	EV_COMMANDS_22, //!< Command 22
	EV_COMMANDS_23, //!< Command 23
	EV_COMMANDS_24, //!< Command 24
	EV_COMMANDS_25, //!< Command 25
	EV_COMMANDS_26, //!< Command 26
	EV_COMMANDS_27, //!< Command 27
	EV_COMMANDS_28, //!< Command 28
	EV_COMMANDS_29, //!< Command 29
	EV_COMMANDS_30, //!< Command 30
	EV_COMMANDS_31, //!< Command 31
	EV_COMMANDS_32, //!< Command 32
	EV_COMMANDS_33, //!< Command 33
	EV_COMMANDS_34, //!< Command 34
	EV_COMMANDS_35, //!< Command 35
	EV_COMMANDS_36, //!< Command 36
	EV_COMMANDS_37, //!< Command 37
	EV_COMMANDS_38, //!< Command 38
	EV_COMMANDS_39, //!< Command 39
	EV_COMMANDS_40, //!< Command 40
	EV_COMMANDS_41, //!< Command 41
	EV_COMMANDS_42, //!< Command 42
	EV_COMMANDS_43, //!< Command 43
	EV_COMMANDS_44, //!< Command 44
	EV_COMMANDS_45, //!< Command 45
	EV_COMMANDS_46, //!< Command 46
	EV_COMMANDS_47, //!< Command 47
	EV_COMMANDS_48, //!< Command 48
	EV_COMMON_CONSOLEDISPLAY, //!< show / hide the console
	EV_COMMON_CONSOLEMODE, //!< toggle appearance of console
	EV_COMMON_ENTER_CHAT, //!< enter the chat
	EV_COMMON_ENTER_OR_EXIT_TRUCK, //!< enter or exit a truck
	EV_COMMON_HIDE_GUI, //!< hide all GUI elements
	EV_COMMON_LOCK, //!< connect hook node to a node in close proximity
	EV_COMMON_MAP_ALPHA, //!< toggle translucency of overview-map
	EV_COMMON_OUTPUT_POSITION, //!< write current position to log (you can open the logfile and reuse the position)
	EV_COMMON_PRESSURE_LESS, //!< decrease tire pressure (note: only very few trucks support this)
	EV_COMMON_PRESSURE_MORE, //!< increase tire pressure (note: only very few trucks support this)
	EV_COMMON_QUIT_GAME, //!< exit the game
	EV_COMMON_RESCUE_TRUCK, //!< teleport to rescue truck
	EV_COMMON_RESET_TRUCK, //!< reset truck to original starting position
	EV_COMMON_SCREENSHOT, //!< take a screenshot
	EV_COMMON_SECURE_LOAD, //!< tie a load to the truck
	EV_COMMON_SHOW_SKELETON, //!< toggle skeleton display mode
	EV_COMMON_START_TRUCK_EDITOR, //!< start the old truck editor
	EV_COMMON_TOGGLE_CUSTOM_PARTICLES, //!< toggle particle cannon
	EV_COMMON_TOGGLE_MAT_DEBUG, //!< debug purpose - dont use
	EV_COMMON_TOGGLE_RENDER_MODE, //!< toggle render mode (solid, wireframe and points)
	EV_COMMON_TOGGLE_REPLAY_MODE, //!< deprecated key - without function
	EV_COMMON_TOGGLE_STATS, //!< toggle Ogre statistics (FPS etc.)
	EV_COMMON_TOGGLE_TRUCK_BEACONS, //!< toggle truck beacons
	EV_COMMON_TOGGLE_TRUCK_LIGHTS, //!< toggle truck front lights
	EV_COMMON_TRUCK_INFO, //!< toggle truck HUD
	EV_COMMON_VIEW_MAP, //!< toggle map modes
	EV_COMMON_FOV_LESS, //!<decreases the current FOV value
	EV_COMMON_FOV_MORE, //!<increases the current FOV value
	EV_GRASS_LESS, //!< EXPERIMENTAL: remove some grass
	EV_GRASS_MORE, //!< EXPERIMENTAL: add some grass
	EV_GRASS_MOST, //!< EXPERIMENTAL: set maximum amount of grass
	EV_GRASS_NONE, //!< EXPERIMENTAL: remove grass completely
	EV_GRASS_SAVE, //!< EXPERIMENTAL: save changes to the grass density image
	EV_MAP_IN, //!< zoom into the overview map in interactive mode
	EV_MAP_INTERACTIVE_TOGGLE, //!< toggle overview map interactive mode
	EV_MAP_OUT, //!< zoom into the overview map in interactive mode
	EV_MENU_DOWN, //!< select next element in current category
	EV_MENU_LEFT, //!< select previous category
	EV_MENU_RIGHT, //!< select next category
	EV_MENU_SELECT, //!< select focussed item and close menu
	EV_MENU_UP, //!< select previous element in current category
	EV_TERRAINEDITOR_BUILT, //!< place currently selected object at current position
	EV_TERRAINEDITOR_PITCHBACKWARD, //!< pitch object backward
	EV_TERRAINEDITOR_PITCHFOREWARD, //!< pitch object foreward
	EV_TERRAINEDITOR_ROTATELEFT, //!< rotate object left
	EV_TERRAINEDITOR_ROTATERIGHT, //!< rotate object right
	EV_TERRAINEDITOR_SELECTROAD, //!< switch to road laying mode
	EV_TERRAINEDITOR_TOGGLEOBJECT, //!< toggle between available objects
	EV_TERRAINEDITOR_TOGGLEROADTYPE, //!< toggle between available road types
	EV_TRUCK_ACCELERATE, //!< accelerate the truck
	EV_TRUCK_AUTOSHIFT_DOWN, //!< shift automatic transmission one gear down
	EV_TRUCK_AUTOSHIFT_UP, //!< shift automatic transmission one gear up
	EV_TRUCK_BLINK_LEFT, //!< toggle left direction indicator (blinker)
	EV_TRUCK_BLINK_RIGHT, //!< toggle right direction indicator (blinker)
	EV_TRUCK_BLINK_WARN, //!< toggle all direction indicators
	EV_TRUCK_BRAKE, //!< brake
	EV_TRUCK_HORN, //!< truck horn
	EV_TRUCK_LIGHTTOGGLE1,  //!< toggle custom light 1
	EV_TRUCK_LIGHTTOGGLE2,  //!< toggle custom light 2
	EV_TRUCK_LIGHTTOGGLE3,  //!< toggle custom light 3
	EV_TRUCK_LIGHTTOGGLE4,  //!< toggle custom light 4
	EV_TRUCK_LIGHTTOGGLE5,  //!< toggle custom light 5
	EV_TRUCK_LIGHTTOGGLE6,  //!< toggle custom light 6
	EV_TRUCK_LIGHTTOGGLE7,  //!< toggle custom light 7
	EV_TRUCK_LIGHTTOGGLE8,  //!< toggle custom light 8
	EV_TRUCK_LIGHTTOGGLE9,  //!< toggle custom light 9
	EV_TRUCK_LIGHTTOGGLE10, //!< toggle custom light 10
	EV_TRUCK_MANUAL_CLUTCH, //!< manual clutch (for manual transmission)
	EV_TRUCK_PARKING_BRAKE, //!< toggle parking brake
	EV_TRUCK_SHIFT_DOWN, //!< shift one gear down in manual transmission mode
	EV_TRUCK_SHIFT_NEUTRAL, //!< shift to neutral gear in manual transmission mode
	EV_TRUCK_SHIFT_UP, //!< shift one gear up in manual transmission mode
	EV_TRUCK_SHIFT_GEAR_REVERSE, //!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR1,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR2,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR3,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR4,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR5,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR6,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR7,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR8,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR9,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR10,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR11,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR12,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR13,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR14,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR15,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR16,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR17,//!< shift directly into this gear
	EV_TRUCK_SHIFT_GEAR18,//!< shift directly into this gear
	
	EV_TRUCK_SHIFT_LOWRANGE, //!< select low range (1-6) for H-shaft 
	EV_TRUCK_SHIFT_MIDRANGE, //!< select middle range (7-12) for H-shaft
	EV_TRUCK_SHIFT_HIGHRANGE, //!< select high range (13-18) for H-shaft
	
	EV_TRUCK_STARTER, //!< hold to start the engine
	EV_TRUCK_STEER_LEFT, //!< steer left
	EV_TRUCK_STEER_RIGHT, //!< steer right
	EV_TRUCK_SWITCH_SHIFT_MODES, //!< toggle between transmission modes
	EV_TRUCK_TOGGLE_CONTACT, //!< toggle ignition
	EV_TRUCK_TOGGLE_AXLE_LOCK,

	EV_COMMON_SHOWTRUCKTOOL,
	EV_COMMON_RELOAD_ROADS,
	EV_COMMON_FULLSCREEN_TOGGLE,
	EV_CAMERA_FREE_MODE_FIX,
	EV_CAMERA_FREE_MODE,
	EV_TRUCK_LEFT_MIRROR_LEFT,
	EV_TRUCK_LEFT_MIRROR_RIGHT,
	EV_TRUCK_RIGHT_MIRROR_LEFT,
	EV_TRUCK_RIGHT_MIRROR_RIGHT,
	EV_COMMON_REPLAY_FORWARD,
	EV_COMMON_REPLAY_BACKWARD,
	EV_COMMON_REPLAY_FAST_FORWARD,
	EV_COMMON_REPLAY_FAST_BACKWARD,
	EV_AIRPLANE_AIRBRAKES_NONE,
	EV_AIRPLANE_AIRBRAKES_FULL,
	EV_AIRPLANE_AIRBRAKES_LESS,
	EV_AIRPLANE_AIRBRAKES_MORE,

	EV_AIRPLANE_THROTTLE,
	EV_COMMON_TRUCK_REMOVE,
	EV_COMMON_NETCHATDISPLAY,
	EV_COMMON_NETCHATMODE,
	EV_CHARACTER_ROT_UP,
	EV_CHARACTER_ROT_DOWN,
	EV_CHARACTER_UP,
	EV_CHARACTER_DOWN,


	EV_MODE_BEFORELAST,
	EV_MODE_LAST
};

struct eventInfo_t
{
	Ogre::String name;
	int eventID;
	Ogre::String defaultKey;
	Ogre::String description;
};

extern eventInfo_t eventInfo[];

typedef struct
{
	// general
	enum eventtypes eventtype;
	// keyboard
	int keyCode;
	bool explicite;
	bool ctrl;
	bool shift;
	bool alt;
	//mouse
	int mouseButtonNumber;
	//joystick buttons
	int joystickNumber;
	int joystickButtonNumber;
	// joystick axis
	int joystickAxisNumber;
	float joystickAxisDeadzone;
	float joystickAxisLinearity;
	int  joystickAxisRegion;
	bool joystickAxisReverse;
	bool joystickAxisHalf;
	bool joystickAxisUseDigital;


	// POVs
	int joystickPovNumber;
	int joystickPovDirection;
	
	// Sliders
	int joystickSliderNumber;
	int joystickSliderReverse;

	//others
	char configline[128];
	char group[128];
	char tmp_eventname[128];
	char comments[1024];
	int suid; //session unique id
} event_trigger_t;

class InputEngine : public OIS::MouseListener, public OIS::KeyListener, public OIS::JoyStickListener
{
public:
	static InputEngine & Instance();
	void Capture();
	float getEventValue(int eventID, bool pure=false);
	bool getEventBoolValue(int eventID);
	bool isEventAnalog(int eventID);
	bool getEventBoolValueBounce(int eventID, float time=0.2f);
	float getEventBounceTime(int eventID);
	static bool instanceExists() ;
	// we need to use hwnd here, as we are also using this in the configurator
	bool setup(size_t hwnd, bool capture=false, bool capturemouse=false, int grabMode=0);
	Ogre::String getKeyForCommand(int eventID);
	bool isKeyDown(OIS::KeyCode mod);

	std::map<int, std::vector<event_trigger_t> > &getEvents() { return events; };
	
	Ogre::String getDeviceName(event_trigger_t evt);
	std::string getEventTypeName(int type);

	int getCurrentKeyCombo(std::string *combo);
	int getCurrentJoyButton(int &joystickNumber, int &button);
	std::string getKeyNameForKeyCode(OIS::KeyCode keycode);
	void resetKeys();
	OIS::JoyStickState *getCurrentJoyState(int joystickNumber);
	int getJoyComponentCount(OIS::ComponentType type, int joystickNumber);
	std::string getJoyVendor(int joystickNumber);
	void smoothValue(float &ref, float value, float rate);
	bool saveMapping(std::string outfile=CONFIGFILENAME, size_t hwnd=0, int joyNum=-10);
	bool appendLineToConfig(std::string line, std::string outfile=CONFIGFILENAME);
	bool loadMapping(std::string outfile=CONFIGFILENAME, bool append=false);

	void destroy();

	Ogre::String getEventCommand(int eventID);
	static int resolveEventName(Ogre::String eventName);
	static Ogre::String eventIDToName(int eventID);
	event_trigger_t *getEventBySUID(int suid);

	bool isEventDefined(int eventID);
	void addEvent(int eventID, event_trigger_t t);
	bool deleteEventBySUID(int suid);
	Ogre::UTFString getKeyLine() { return keyInput; };
	void resetKeyLine() { keyInput=""; };
	void setRecordInput(bool value) { recordChat=value; };
	bool getInputsChanged() { return inputsChanged; };
	void prepareShutdown();
	OIS::MouseState getMouseState();
	// some custom methods
	void windowResized(Ogre::RenderWindow* rw);

	bool reloadConfig(std::string outfile=CONFIGFILENAME);
	bool updateConfigline(event_trigger_t *t);

	void grabMouse(bool enable);
	void hideMouse(bool visible);
	void setMousePosition(int x, int y, bool padding=true);

	int getKeboardKeyForCommand(int eventID);

	void updateKeyBounces(float dt);
	void completeMissingEvents();
	int getNumJoysticks() { return free_joysticks; };

protected:
	InputEngine();
	~InputEngine();
	InputEngine(const InputEngine&);
	InputEngine& operator= (const InputEngine&);
	static InputEngine* myInstance;

	//OIS Input devices
	OIS::InputManager* mInputManager;
	OIS::Mouse*    mMouse;
	OIS::Keyboard* mKeyboard;
	OIS::JoyStick* mJoy[MAX_JOYSTICKS];
	int free_joysticks;

	// JoyStickListener
	bool buttonPressed( const OIS::JoyStickEvent &arg, int button );
	bool buttonReleased( const OIS::JoyStickEvent &arg, int button );
	bool axisMoved( const OIS::JoyStickEvent &arg, int axis );
	bool sliderMoved( const OIS::JoyStickEvent &, int );
	bool povMoved( const OIS::JoyStickEvent &, int );

	// KeyListener
	bool keyPressed( const OIS::KeyEvent &arg );
	bool keyReleased( const OIS::KeyEvent &arg );

	// MouseListener
	bool mouseMoved( const OIS::MouseEvent &arg );
	bool mousePressed( const OIS::MouseEvent &arg, OIS::MouseButtonID id );
	bool mouseReleased( const OIS::MouseEvent &arg, OIS::MouseButtonID id );

	// this stores the key/button/axis values
	std::map<int, bool> keyState;
	OIS::JoyStickState joyState[MAX_JOYSTICKS];
	OIS::MouseState mouseState;

	// define event aliases
	std::map<int, std::vector<event_trigger_t> > events;
	std::map<int, float > event_times;


	bool processLine(char *line);
	bool captureMode;

	//ExampleFrameListener *mefl;

	void initAllKeys();
	std::map<std::string, OIS::KeyCode> allkeys;
	std::map<std::string, OIS::KeyCode>::iterator allit;

	float deadZone(float axis, float dz);
	float axisLinearity(float axisValue, float linearity);

	float logval(float val);
	std::string getEventGroup(Ogre::String eventName);
	bool mappingLoaded;

	Ogre::UTFString keyInput;
	bool recordChat;
	bool inputsChanged;
	bool fileExists(char* filename);
	int grabMode;

	event_trigger_t newEvent();
};

#endif
