//  Gl_World.h - handles interactions between a car and its environment.
//
//	Vamos Automotive Simulator
//  Copyright (C) 2001--2004 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 _GL_WORLD_H_
#define _GL_WORLD_H_

#include <vamos/body/Gl_Car.h>
#include <vamos/geometry/Three_Vector.h>
#include <vamos/geometry/XML_Parser.h>
#include <vamos/world/World.h>
#include <vamos/world/Controls.h>

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

namespace Vamos_World
{
  class Sounds;

  void display_proxy ();
  void reshape_proxy (int width, int height);
  void key_proxy (unsigned char key, int x, int y);
  void key_up_proxy (unsigned char key, int x, int y);
  void special_key_proxy (int key, int x, int y);
  void special_key_up_proxy (int key, int x, int y);
  void mouse_motion_proxy (int x, int y);
  void mouse_button_proxy (int button, int state, int x, int y);
  void animate_proxy ();
  void timer_proxy (int value);

  class Gl_Window
  {
	int m_width;
	int m_height;
	double m_aspect;
	int m_id;
	
  public:
	Gl_Window (int width, int height, const char* name);
	~Gl_Window ();
	
	double aspect () const { return m_aspect; }
	int width () const { return m_width; }
	int height () const { return m_height; }

	void resize (int width, int height);
  };

  class Timer : public SigC::Object
  {
	int m_timeout;
	double m_time_step;
	double m_max_time_step;
	int m_frames;

	void start ();

  public:
	Timer (double interval_to_average_over);

	void update ();
	void add_frame () { m_frames++; }
	void set_max_time_step (double step) { m_max_time_step = step; }
	double get_time_step () const { return m_time_step; }
	int steps_per_frame () const 
	{ return int (m_time_step / m_max_time_step) + 1; }
	double get_substep () const { return m_time_step / steps_per_frame (); }
  };

  class Gl_World : public World, public SigC::Object
  {
	friend class World_Reader;
	friend class Controls_Reader;

	std::string m_data_dir;
	std::string m_world_file;
	std::string m_controls_file;

	Sounds* mp_sounds;
	Controls* mp_controls;
	Timer* mp_timer;
	Gl_Window* mp_window;
	GLuint m_font;

	enum View
	{
	  BODY_VIEW,
	  MAP_VIEW,
	  WORLD_VIEW
	};

	View m_view;

	int m_frames;
	double m_max_time_step;
	bool m_paused;

	void animate ();
	void draw_timing_info ();
	void draw_string (const std::string& str, double x, double y);
	void display ();
	void reshape (int width, int height);

	void show_full_window ();
	void set_car_view (Vamos_Body::Car* car);
	void set_map_view ();
	void set_world_view (const Vamos_Track::Camera& camera);
	void draw_track (bool draw_sky,
					 const Vamos_Geometry::Three_Vector& 
					 view_position = Vamos_Geometry::
					 Three_Vector (0.0, 0.0, 0.0));
	void draw_cars (bool draw_interior, bool draw_focused_car = true);
	void draw_mirror_views ();
	void show_scene ();

	void initialize_graphics (int* argc, char** argv);
	void set_callbacks ();
	void set_attributes ();

  public:
	Gl_World (int argc, char** argv, 
			  Vamos_Track::Strip_Track* track, 
			  Atmosphere* atmosphere,
			  Sounds* sounds);
	~Gl_World ();

	void add_car (Vamos_Body::Car* car);

	// Read the world definition file.
	void read (std::string data_dir = "", 
			   std::string world_file = "",
			   std::string controls_file = "");

	void start ();

	void set_paused (bool is_paused);

	// Methods registered with the controls system.
	// reset() and restart() defined in a base class are also
	// registered. 
	bool fill_tank (double, double);   // Fill the car's gas tank.
	bool pause (double, double);       // Pause the program.
	bool quit (double, double);        // Quit the program.
	bool read_car (double, double);    // Read the car definition file.
	bool read_track (double, double);  // Read the track definition file.
	bool read_world (double, double);  // Read the world definition file.
	bool reset_car (double, double);   // Put the car back on the track.
	bool restart_car (double, double); // Put the car at the starting line.
	bool start_engine (double, double);// Start the car's engine.

	bool cycle_view (double, double);  // Change the view.

	bool initial_shift_up (double, double);
	bool initial_shift_down (double, double);
	bool shift_up (double, double);
	bool shift_down (double, double);
	bool initial_shift_up_disengage (double, double);
	bool initial_shift_down_disengage (double, double);
	bool shift_up_disengage (double, double);
	bool shift_down_disengage (double, double);
	bool initial_clutch (double, double);
	bool clutch (double, double);
	bool initial_engage_clutch (double, double);
	bool initial_disengage_clutch (double, double);
	bool engage_clutch (double, double);
	bool disengage_clutch (double, double);
	bool gas (double, double);
	bool brake (double, double);
	bool steer (double, double);
	bool steer_left (double, double);
	bool steer_right (double, double);
	bool pan_left (double, double);
	bool pan_right (double, double);
	bool focus_previous_car (double, double);
	bool focus_next_car (double, double);

	void set_max_time_step (double step) 
	{ mp_timer->set_max_time_step (step); }

	void set_focused_car (size_t index);
  };

  class World_Reader : public Vamos_Geometry::XML_Parser
  {
	void on_start_tag (const Vamos_Geometry::XML_Tag& tag); 
	void on_end_tag (const Vamos_Geometry::XML_Tag& tag); 
	void on_data (std::string data_string);

	std::string m_tag;
	std::string m_path;

	Gl_World* mp_world;

  public:
	World_Reader (std::string file_name, Gl_World* world);
  };

  struct Unknown_Function
  {
	std::string m_function;
	Unknown_Function (std::string func) : m_function (func) {};
  };
  
  // Controls_Reader is here because it needs Gl_World's
  // declarations.   If it were in Controls.h, then Controls.h and
  // Gl_World.h would be dependent on each other.
  class Controls_Reader : public Vamos_Geometry::XML_Parser
  {
	void on_start_tag (const Vamos_Geometry::XML_Tag& tag); 
	void on_end_tag (const Vamos_Geometry::XML_Tag& tag); 
	void on_data (std::string data_string);

	std::string m_tag;
	std::string m_path;

	Gl_World* mp_world;
	Controls* mp_controls;
	std::map <std::string, Controls::Callback_Func> 
	m_function_map;
	bool m_special;

	enum Control_Type
	  {
		KEY,
		JOYSTICK_BUTTON,
		JOYSTICK_AXIS,
		MOUSE_BUTTON,
		MOUSE_MOTION
	  };

	enum Direction
	  {
		UP,
		DOWN
	  };

	Control_Type m_type;
	int m_control;
	Direction m_direction;
	std::string m_function;
	double m_factor;
	double m_offset;
	double m_minimum;
	double m_minimum_position;
	double m_maximum;
	double m_maximum_position;
	double m_time;
	double m_deadband;

  public:
	Controls_Reader (std::string file_name, Gl_World* world);
  };
}

#endif // not _GL_WORLD_
