//  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 "../body/Gl_Car.h"
#include "../geometry/Three_Vector.h"
#include "../media/XML_Parser.h"
#include "World.h"
#include "Controls.h"

class SDL_Surface;

namespace Vamos_World
{
  class Sounds;

  class Gl_Window : public Control_Handler
  {
	int m_width;
	int m_height;
	double m_aspect;
	int m_id;
    int m_video_flags;
	
  public:
	Gl_Window (int width, int height, const char* name, bool full_screen);
	~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
  {
	int m_timeout;
	double m_time_step;
	double m_max_time_step;
	int m_frames;
    bool m_is_paused;
    int m_start_time;

	void start ();

  public:
	Timer (double interval_to_average_over);

	void update ();
	void set_paused (bool is_paused);
	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 (); }
  };

  struct Can_Not_Intialize_SDL
  {
	std::string error;
	Can_Not_Intialize_SDL (std::string error_in) : error (error_in) {};
  };

  struct No_SDL_Screen
  {
    int width;
    int height;
    int depth;
    int video_flags;
    No_SDL_Screen (int w, int h, int d, int flags) 
      : width (w), height (h), depth (d), video_flags (flags) {};
  };

  class Gl_World : public World
  {
	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;
    Control m_keyboard;
    Control m_joystick;
    Control m_mouse;
	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 propagate_cars (double time_step);
    void update_car_timing ();
    void play_sounds ();

	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_attributes ();

  public:
	Gl_World (int argc, char** argv, 
			  Vamos_Track::Strip_Track* track, 
			  Atmosphere* atmosphere,
			  Sounds* sounds,
              bool full_screen);
	~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 = false, double = false); // 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_Media::XML_Parser
  {
	void on_start_tag (const Vamos_Media::XML_Tag& tag); 
	void on_end_tag (const Vamos_Media::XML_Tag& tag); 
	void on_data (std::string data_string);

	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_Media::XML_Parser
  {
	void on_start_tag (const Vamos_Media::XML_Tag& tag); 
	void on_end_tag (const Vamos_Media::XML_Tag& tag); 
	void on_data (std::string data_string);

	Gl_World* mp_world;
	Control* mp_keyboad;
    Control* mp_joystick;
    Control* mp_mouse;

	std::map <std::string, Callback_Function> 
	m_function_map;

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

	Control_Type m_type;
	int m_control;
    Vamos_Geometry::Direction m_direction;
	std::string m_function;
	double m_factor;
	double m_offset;
	double m_deadband;
	double m_upper_deadband;
	double m_time;

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

#endif // not _GL_WORLD_
