//	Vamos Automotive Simulator
//  Copyright (C) 2001--2002 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_CAR_WORLD_H_
#define _GL_CAR_WORLD_H_

#include <vamos/body/Gl_Car.h>
#include <vamos/geometry/Three_Vector.h>
#include <vamos/world/Car_World.h>

#include <sigc++/signal_system.h>

class slScheduler;
class slSample;
class slEnvelope;
class jsJoystick;
class Vamos_Track::Track;
class Vamos_Track::Track;


namespace Vamos_World
{
  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 animate_proxy ();
  void timer_proxy (int value);

  class Timing_Info
  {
	Vamos_Track::Track* mp_track;

	double m_elapsed_time;

	double m_lap_time;
	double m_prev_lap_time;
	double m_lap_diff;
	double m_best_lap_time;

	int m_sector;
	int m_prev_sector;
	double m_sector_time;
	double m_prev_sector_time;
	double m_prev_sector_diff;
	std::vector <double> m_best_sector_times;

	std::string format (double time);
	std::string format_diff (double time);

	double m_distance;

  public:
	Timing_Info (Vamos_Track::Track* track);

	void update (double time_step, const Vamos_Geometry::Three_Vector& pos);
	std::string lap_time () { return format (m_lap_time); }
	std::string prev_lap_time () { return format (m_prev_lap_time); }
	std::string best_lap_time () { return format (m_best_lap_time); }
	std::string lap_diff () { return format_diff (m_lap_diff); }
	int sector () const { return m_sector; }
	int prev_sector () const { return m_prev_sector; }
	std::string sector_time () { return format (m_sector_time); }
	std::string prev_sector_time () { return format (m_prev_sector_time); }
	std::string prev_sector_diff () 
	{ return format_diff (m_prev_sector_diff); }
	std::string best_sector_time (int sector)
	{ return format (m_best_sector_times [sector]); }
	double distance () const { return m_distance; }
  };

  class Missing_Sound_File
  {
  private:
	std::string m_file;
  public:
	Missing_Sound_File (std::string file) : m_file (file) {};
	std::string message () { return "Can't find the sound file \"" 
							   + m_file + '\"'; }
  };

  class Sounds
  {
  private:
	slScheduler* mp_scheduler;

	slSample* mp_engine_sound;
	// The sound for tires slipping on a hard surface.
	slSample* mp_tire_squeal_sound;
	// The sound for driving or sliding on grass.
	slSample* mp_grass_sound;
	// The sound for driving or sliding on gravel.
	slSample* mp_gravel_sound;
	// The sound for hard surfaces sliding.
	slSample* mp_scrape_sound;
	// Wind
	slSample* mp_wind_sound;
	// The sound for a crash into a soft surface.
	slSample* mp_soft_crash_sound;
	// The sound for a crash into a hard surface.
	slSample* mp_hard_crash_sound;

	slEnvelope* mp_engine_pitch;
	slEnvelope* mp_engine_volume;

	slEnvelope* mp_tire_squeal_pitch;
	slEnvelope* mp_tire_squeal_volume;

	slEnvelope* mp_grass_volume;
	slEnvelope* mp_gravel_volume;
	slEnvelope* mp_scrape_volume;
	slEnvelope* mp_wind_volume;

	double clip (double number);

  public:
	Sounds (slSample* engine_sound,
			std::string tire_squeal_sound, 
			std::string grass_sound,
			std::string gravel_sound, 
			std::string scrape_sound,
			std::string wind_sound,
			std::string soft_crash_sound,
			std::string hard_crash_sound);

	virtual ~Sounds ();

	// The engine pitch and volume are returned by the car.  They
	// should not be modified.
	void play_engine_sound (double pitch, double volume);

	// The other sounds are adjusted by these virtual functions.
	// Derived classes can make different adjustments by overiding
	// them. 
	virtual void play_tire_squeal_sound (double slide);
	
	virtual void play_grass_sound (double speed);
	virtual void play_gravel_sound (double speed);
	virtual void play_scrape_sound (double speed);
	virtual void play_wind_sound (double speed);
	virtual void play_hard_crash_sound (double speed);
	virtual void play_soft_crash_sound (double speed);

	void update () { mp_scheduler->update (); }
  };

  class Gl_Car_World : public Car_World, public SigC::Object
  {
	Sounds* mp_sounds;

	GLuint m_font;
	int m_window;

	int m_window_width;
	int m_window_height;
	double m_aspect;

	double m_steer_exponent;

	jsJoystick* mp_js_joystick;
	float* mp_js_axes;
	bool m_joystick_is_present;

	static const int m_shift_up_button = 1;  // first bit
	static const int m_shift_down_button = 2;  // second bit
	static const int m_clutch_button = 4; // third bit
	static const int m_steer_axis = 0;
	static const int m_accel_axis = 1;

	int m_last_buttons;

	enum View
	{
	  BODY_VIEW,
	  MAP_VIEW,
	  WORLD_VIEW
	};

	View m_view;

	double m_time_step;
	int m_frames;

	double m_steer_time;
	double m_steer_return_time;
	double m_clutch_time;
	double m_gas_time;
	double m_brake_time;
	double m_first_clutch_time;
	double m_shift_delay;
	double m_max_time_step;

	bool m_paused;
	
	Timing_Info* mp_timing;

	void key_press (unsigned char key, int x, int y);
	void key_release (unsigned char key, int x, int y);
	void special_key_press (int key, int x, int y);
	void special_key_release (int key, int x, int y);
	void animate ();
	void draw_dashboard ();
	void timer (int value);

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

  public:
	Gl_Car_World (int argc, char** argv, double gravity, 
				  Vamos_Body::Gl_Car* car, 
				  Vamos_Track::Track* track, 
				  Atmosphere* atmosphere,
				  Sounds* sounds);

	virtual ~Gl_Car_World ();

	void start ();

	void max_time_step (double step) { m_max_time_step = step; }

	// Methods for setting the ramp rates for controls.
	void steer_time (double time) { m_steer_time = time; }
	void steer_return_time (double time) { m_steer_return_time = time; }
	void clutch_time (double time) { m_clutch_time = time; }
	void gas_time (double time) { m_gas_time = time; }
	void brake_time (double time) { m_brake_time = time; }
	void first_clutch_time (double time) { m_first_clutch_time = time; }
	void shift_delay (double time) { m_shift_delay = time; }
	void steer_exponent (double exponent) { m_steer_exponent = exponent; }
  };
}

#endif
