//  Controls.h - a class for handling control events.
//
//	Vamos Automotive Simulator
//  Copyright (C) 2003 Sam Varner
//
//  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 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_
#define _CONTROLS_H_

#include <sigc++/sigc++.h>

#include <vector>
#include <limits>
#include <iostream>

class jsJoystick;

namespace Vamos_World
{
  class Gl_World;

  struct Bad_Axis
  {
	Bad_Axis (int ax, int num) : axis (ax), num_axes (num) {};
	int axis;
	int num_axes;
  };

  // Derive from SigC::Object so member functions can be GL callbacks.
  class Controls : public SigC::Object
  {
  public:
	typedef bool (Gl_World::* Callback_Func) (double, double);

	enum Direction { X = 0, Y = 1 };

  private:
	struct Callback
	{
	  Callback (Gl_World* obj = 0, 
				Callback_Func func = 0, 
				double d1 = 0.0,
				double d2 = 0.0,
				double d3 = 0.0,
				double d4 = 0.0);

	  Gl_World* object;
	  Callback_Func function;
	  double data1;
	  double data2;
	  double data3;
	  double data4;
	};

	typedef std::vector <std::pair <int, Callback> > Callback_List;

	class Keyboard
	{
	  Callback_List m_key_down_map;
	  Callback_List m_key_up_map;

	  void call_key_callbacks (const Callback_List& callbacks, 
							   int match, double value) const;

	public:
	  void bind_key_down (int key, 
						  Gl_World* world, 
						  Callback_Func func,
						  double time = 0.0);
	  void bind_key_up (int key, 
						Gl_World* world, 
						Callback_Func func,
					  double time = 0.0);
	  
	  void key_press (int key);
	  void key_release (int key);
	};

	class Continuous_Control
	{
	protected:
	  Callback_List m_button_down_map;
	  Callback_List m_button_up_map;
	  Callback_List m_motion_map;

	  void call_button_callbacks (const Callback_List& callbacks,
								  int button, double value) const;
	  void call_button_callbacks (const Callback_List& callbacks,
								  int match,  
								  int dont_match, 
								  double value) const;

	  virtual bool button_matches (int button, 
								   int match, 
								   int dont_match) const = 0;
	  virtual int translate_button (int button) const { return button; }

	public:
	  virtual ~Continuous_Control () {};

	  void bind_button_down (int button, 
							 Gl_World* world, 
							 Callback_Func func,
							 double time = 0.0);
	  void bind_button_up (int button, 
						   Gl_World* world, 
						   Callback_Func func,
						   double time = 0.0);
	};

	class Joystick : public Continuous_Control
	{
	  jsJoystick* mp_js_joystick;
	  bool m_joystick_is_present;
	  int m_num_axes;
	  float* mp_js_axes;
	  int m_last_buttons;

	  void call_axis_callbacks (const Callback_List& callbacks) const;

	  bool button_matches (int button, int match, int dont_match) const;
	  int translate_button (int button) const { return 1 << (button - 1); }

	public:
	  Joystick ();
	  ~Joystick ();

	  void bind_axis (int axis, 
					  Gl_World* world, 
					  Callback_Func func, 
					  double factor = 1.0,
					  double offset = 0.0,
					  double minimum = 0.0,
					  double deadband = 0.0);

	  void update ();
	};

	class Mouse : public Continuous_Control
	{
	  int m_window_width;
	  int m_window_height;

	  void call_motion_callbacks (const Callback_List& callbacks,
								  int x, int y) const;

	  bool button_matches (int button, int match, int dont_match) const;
	  double get_position (int dir, int x, int y) const;

	public:
	  void bind_motion (int axis, 
						Gl_World* world, 
						Callback_Func func, 
						double min,
						double min_position,
						double max,
						double max_position);
	  void update_button (int button, int state);
	  void update_motion (int x, int y);
	  void update_window_size (int width, int height);
	};

	Keyboard* mp_keyboard;
	Joystick* mp_joystick;
	Mouse* mp_mouse;

	bool m_auto_clutch;
	static const int char_bits = std::numeric_limits <unsigned char>::digits;

	int translate_special_key (int key)
	{
	  // Move `key' outside of the range of non-special keys.
	  return key ^ (1 << char_bits);
	}

  public:
	Controls ();
	~Controls ();

	void bind_key_down (int key, 
						Gl_World* world, 
						Callback_Func func,
						double time = 0.0);
	void bind_key_up (int key, 
					  Gl_World* world, 
					  Callback_Func func,
					  double time = 0.0);
	void bind_special_key_down (int key, 
								Gl_World* world, 
								Callback_Func func,
								double time = 0.0);
	void bind_special_key_up (int key, 
							  Gl_World* world, 
							  Callback_Func func,
							  double time = 0.0);

	void bind_button_down (int button, 
						   Gl_World* world, 
						   Callback_Func func,
						   double time = 0.0);
	void bind_button_up (int button, 
						 Gl_World* world, 
						 Callback_Func func,
						 double time = 0.0);
	void bind_axis (int axis, 
					Gl_World* world, 
					Callback_Func func, 
					double factor = 1.0,
					double offset = 0.0,
					double minimum = 0.0,
					double deadband = 0.0);

	void bind_mouse_motion (int axis,
							Gl_World* world, 
							Callback_Func func,
							double factor = 1.0,
							double offset = 0.0,
							double minimum = 0.0,
							double deadband = 0.0);
	void bind_mouse_button_down (int button, 
								 Gl_World* world, 
								 Callback_Func func,
								 double time = 0.0);
	void bind_mouse_button_up (int button, 
							   Gl_World* world, 
							   Callback_Func func,
							   double time = 0.0);

	void key_press (unsigned char key, int, int);
	void key_release (unsigned char key, int, int);
	void special_key_press (int key, int, int);
	void special_key_release (int key, int, int);
	void mouse_motion (int x, int y);
	void mouse_button (int button, int state, int, int);

	void set_window_size (int width, int height);
	void update ();
  };
}

#endif // not _CONTROLS_H_
