/***************************************************************************
 *            controls.h
 *
 *  Sat Jun  4 08:17:40 2005
 *  Copyright  2005  Joe Venzon
 *  joe@venzon.net
 ****************************************************************************/

/*
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */
 
#ifndef _CONTROLS_H

#ifdef __APPLE__ 
#include "definitions_mac.h" 
#else 
#include "definitions.h" 
#endif 

 
#include <stdio.h>			// Header File For Standard Input/Output
#include <GL/gl.h>
#include <GL/glu.h>
#include <SDL/SDL.h>
#ifdef __APPLE__
#include <SDL_image/SDL_image.h>
#else
#include <SDL/SDL_image.h>
#endif
#include <string>
#include <sstream>
#include <iostream>
#include <fstream>
#include <cmath>
#include <cassert>

#include <list>

#include "utility.h"
#include "keyman.h"
#include "settings.h"

#include "globals.h"

#define MAX_JOYSTICKS 32
#define MAX_AXES 10

using namespace std;

enum ControllerEnum { Joy, Key, Mouse, Unknown };
enum JoyTypeEnum { Button, Axis, UnknownType };
enum JoyAxisTypeEnum { Positive, Negative, Both };
enum MouseTypeEnum { MButton, Motion, UnknownMouse };
enum MouseDirEnum { Up, Down, Left, Right };

class CONTROL
{
private:
	string controlname;
	ControllerEnum controltype;

	int joynum;
	int joyaxis;
        JoyAxisTypeEnum joyaxistype;
	int joybutton;
	bool joybuttonlaststate;
	JoyTypeEnum joytype;
	bool joypushdown;

	int keycode; //requires lookup in keyman database
	bool onetime;
	bool keypushdown;

	MouseTypeEnum mousetype;
	int mbutton;
	MouseDirEnum mdir;
	bool last_mouse_state;
	bool mouse_push_down;

	float deadzone;
	float exponent;
	float gain;

public:
	CONTROL()
	  : controlname(""), controltype(Unknown), joynum(0), joyaxis(0), joyaxistype(Both), joybutton(0),
	    joybuttonlaststate(false), joytype(UnknownType), joypushdown(false), keycode(0), onetime(false),
	    keypushdown(false), mousetype(UnknownMouse), mbutton(0), mdir(Up), last_mouse_state(false),
	    mouse_push_down(false), deadzone(0.0), exponent(1.0), gain(1.0) { }
	CONTROL( const CONTROL& c )
	  : controlname(c.controlname), controltype(c.controltype), joynum(c.joynum), joyaxis(c.joyaxis),
	    joyaxistype(c.joyaxistype), joybutton(c.joybutton), joybuttonlaststate(c.joybuttonlaststate),
	    joytype(c.joytype), joypushdown(c.joypushdown), keycode(c.keycode), onetime(c.onetime),
	    keypushdown(c.keypushdown), mousetype(c.mousetype), mbutton(c.mbutton), mdir(c.mdir),
	    last_mouse_state(c.last_mouse_state), mouse_push_down(c.mouse_push_down), deadzone(c.deadzone),
	    exponent(c.exponent), gain(c.gain) { }
	void SetName(string cname) {controlname = cname;}
	void SetType(string type) {if (type == "joy") controltype = Joy; else if (type == "key") controltype = Key; else if( type == "mouse" ) controltype = Mouse; else {controltype = Unknown; cout << "Unknown control type: " << type << endl;}}
	ControllerEnum GetType() {return controltype;}
	string GetName() {return controlname;}
	void SetKeyCode(int nkey) {keycode = nkey;}
	int GetKeyCode() {return keycode;}
	void SetOneTime(bool ot) {onetime = ot;}
	bool GetOneTime() {return onetime;}
	void SetKeyPushDown(bool npd) {keypushdown = npd;}
	bool GetKeyPushDown() {return keypushdown;}
	void SetJoyNum(int jn) {joynum = jn;}
	int GetJoyNum() {return joynum;}
	void SetJoyAxis(int ja) {joyaxis = ja;}
	void SetJoyAxisType(JoyAxisTypeEnum ap) {joyaxistype = ap;}
	int GetJoyAxis() {return joyaxis;}
	JoyAxisTypeEnum GetJoyAxisType() {return joyaxistype;}
	void SetJoyButton(int jb) {joybutton = jb;}
	int GetJoyButton() {return joybutton;}
	void SetJoyType(string type) {if (type == "button") joytype = Button; else if (type == "axis") joytype = Axis; else {joytype = UnknownType; cout << "Unknown joystick input type: " << type << endl;}}
	JoyTypeEnum GetJoyType() {return joytype;}
	void Dump();
	void SetJoyButtonLastState(bool ls) {joybuttonlaststate = ls;}
	bool GetJoyButtonLastState() {return joybuttonlaststate;}
	void SetJoyPushDown(bool pd) {joypushdown = pd;}
	bool GetJoyPushDown() {return joypushdown;}
	MouseTypeEnum GetMouseType() { return mousetype; }
	void SetMouseType( string mt ) { if( mt == "button" ) mousetype = MButton; else if( mt == "motion" ) mousetype = Motion; else { mousetype = UnknownMouse; cout << "Unknown mouse input type: " << mt << endl; } }
	MouseDirEnum GetMouseDirection() { return mdir; }
	void SetMouseDirection( string md ) { if( md == "up" ) mdir = Up; else if( md == "down" ) mdir = Down; else if( md == "left" ) mdir = Left; else if( md == "right" ) mdir = Right; else { mdir = Left; cout << "mouse direction not found: " << md << endl; } }
	int GetMouseButton() { return mbutton; }
	void SetMouseButton( string mb ) { int mbut = 0; istringstream mbss; mbss.str( mb ); mbss >> mbut; mbutton = mbut; }
	void SetMouseButton( int mb ) { mbutton = mb; }
	bool GetLastMouseState() { return last_mouse_state; }
	void SetLastMouseState( bool new_state ) { last_mouse_state = new_state; }
	bool GetMousePushDown() { return mouse_push_down; }
	void SetMousePushDown( bool new_push_down ) { mouse_push_down = new_push_down; }
	
	void SetDeadzone(float newdeadzone) {deadzone = newdeadzone;}
	void SetExponent(float newexponent) {exponent = newexponent;}
	void SetGain(float newgain) {gain = newgain;}
	float GetDeadzone() {return deadzone;}
	float GetExponent() {return exponent;}
	float GetGain() {return gain;}
};

class GAMECONTROLS
{
private:
	//CONTROL * controls;
	list <CONTROL> controls;
	//int numcontrols;
	void ControlParseError(int line, ifstream & ffrom);
	ofstream error_log;
	string joysticktype; //wheel or joystick
	string compensation;
	float calibration[MAX_JOYSTICKS][MAX_AXES][2];
	SDL_Joystick * js[MAX_JOYSTICKS];

	list <CONTROL>::iterator control_iterator;

	void Clear();

public:
	GAMECONTROLS();
	~GAMECONTROLS();
	void LoadControls() {LoadControls(settings.GetSettingsDir() + "/controls");}
	void LoadControls(string fname);
	//list <CONTROL> & GetControls();
	void ControlIteratorReset() {control_iterator = controls.begin();}
	CONTROL * ControlIteratorGetControl() {if (control_iterator != controls.end()) return &(*control_iterator); else return NULL;}
	void ControlIteratorIncrement() {control_iterator++;}
	int GetNumControls();
	string GetJoystickType() {return joysticktype;}
	void SetJoystickType(string newtype) {joysticktype = newtype;}
	void SetControls(CONTROL * newcontrols, int newnum);
	void WriteControlFile();
	void WriteControlFile(string fname);
	string GetCompensation() {return compensation;}
	void SetCompensation(string newcomp) {compensation = newcomp;}
	//string GetDeadzone() {return deadzone;} //this is now a per-control setting
	//void SetDeadzone(string newdeadzone) {deadzone = newdeadzone;}

	void DeleteControlsWithAction(string actionstr);
	void AddControl(CONTROL & other);

	void LoadCalibration();
	void WriteCalibration();
	float GetCalibration(int joynum, int joyaxis, bool max);
	void MinimizeCalibration();
	void SetCalibrationPoint(int joynum, int joyaxis, float val);
	
	float ApplyCalibration(int joynum, int joyaxis, float val);
	float ApplyDeadzone(float dz, float val);
	float ApplyGain(float gain, float val);
	float ApplyExponent(float exponent, float val);

	SDL_Joystick * GetJoy(int joynum) {return js[joynum];}
	SDL_Joystick ** Get_js() {return js;}
	void InitJoy();
	void DeinitJoy();
	void UpdateSettings();
};

#define _CONTROLS_H
#endif /* _CONTROLS_H */
