//  Strip_Track.h - a constant-width track.
//
//	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 _STRIP_TRACK_H_
#define _STRIP_TRACK_H_

#include <vamos/geometry/Material.h>
#include <vamos/geometry/Three_Matrix.h>
#include <vamos/geometry/Three_Vector.h>
#include <vamos/geometry/Two_Point.h>
#include <vamos/geometry/XML_Parser.h>
#include <vamos/track/Road_Segment.h>
#include <vamos/track/Track.h>

#include <string>
#include <map>
#include <vector>

class Vamos_Geometry::Spline;

namespace Vamos_Track
{
  // An exception thrown when the track can not be adjusted to make a circuit.
  class Can_Not_Close {};

  // Exception class
  class Segment_Not_Found
  {
	Vamos_Geometry::Three_Vector m_pos;
  public:
	Segment_Not_Found (const Vamos_Geometry::Three_Vector& world_pos) : 
	  m_pos (world_pos) {};
  };

  class Strip_Track : public Track
  {
	// This method builds the track and then adjusts the last curve
	// and the straights before and after it to make the track end
	// where it began.
	void build_circuit ();

    void set_skews ();

	// bounding dimensions in pixels.
	double m_min_x;
	double m_max_x;
	double m_min_y;
	double m_max_y;

	std::vector <double> m_timing_lines;
	double m_speed_trap_start;
	double m_speed_trap_end;

	std::string m_data_dir;
	std::string m_track_file;

	Vamos_Geometry::Material_Handle m_material;

	// Return WORLD_POS transformed to the track's coordinate system.
	// SEGMENT_INDEX will be modified if the position on another
	// segment.
	Vamos_Geometry::Three_Vector 
	track_coordinates (const Vamos_Geometry::Three_Vector& world_pos,
					   size_t& segment_index);

	Vamos_Geometry::Spline* mp_elevation;
	double m_length;

	GLuint m_sky_list_id;

  protected:
	std::vector <Road_Segment*> m_segments;

  public:
	Strip_Track ();
	virtual ~Strip_Track ();

	// Read the track definition file.
	virtual void read (std::string data_dir = "", std::string track_file = "");

	// Add a straight or curved segment of road to the track.
	void add_segment (Road_Segment* seg);

	// Draw the sky.
	void draw_sky (const Vamos_Geometry::Three_Vector& view) const;

	// Draw the track.
	void draw () const;

	// Make the track.
	void build (bool is_closed, double length);

	void build_sky_box (std::string sides_image, 
						std::string top_image,
						std::string bottom_image,
						bool smooth);

	// Add a sector timing line.
	void timing_line (double dist) { m_timing_lines.push_back (dist); }

	// Return the number of timing lines.
	size_t timing_lines () const { return m_timing_lines.size (); }

	// Add a speed trap.
	void speed_trap (double start, double end);

	// Return the bounds of the track.
	double min_x () const { return m_min_x; }
	double max_x () const { return m_max_x; }
	double min_y () const { return m_min_y; }
	double max_y () const { return m_max_y; }

	// Scale the track to a particular length.
	void set_length (double length);

	// Return the new position for a vehicle at POS when a reset is
	// performed. 
	Vamos_Geometry::Three_Vector 
	reset_position (const Vamos_Geometry::Three_Vector& pos,
					size_t& segment_index);

	// Return the new orientation for a vehicle at POS when a reset is
	// performed. 
	Vamos_Geometry::Three_Matrix
	reset_orientation (const Vamos_Geometry::Three_Vector& pos,
					   size_t& segment_index);

	// Return the elevation of the track at the x and y components of
	// POS.
	double elevation (const Vamos_Geometry::Three_Vector& pos,
					  double bump_parameter,
					  size_t & segment_index);

	Track_Contact_Info 
	test_for_contact (const Vamos_Geometry::Three_Vector& pos,
					  double bump_parameter,
					  size_t& segment_index);

	// Return the distance along the track for the given position.
	double distance (const Vamos_Geometry::Three_Vector& pos,
					 size_t& segment_index);

	// Return the timing sector at the given distance.
	int sector (double distance);

	const std::vector <Road_Segment*>& get_segments () const
	{ return m_segments; }
  };

  class Strip_Track_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;
	std::string m_name;

	bool m_first_road;

	std::vector <double> m_doubles;
	std::vector <bool> m_bools;
	std::vector <std::string> m_strings;
	std::vector <Vamos_Geometry::Two_Point> m_points;
	std::vector <Vamos_Geometry::Two_Point> m_elev_points;
	std::vector <Vamos_Geometry::Two_Point> m_point_vectors [4];
	std::vector <Vamos_Geometry::Two_Point> m_left_profile;
	std::vector <Vamos_Geometry::Two_Point> m_right_profile;
	std::vector <Braking_Marker*> m_braking_markers;

	// Materials indexed by name.
	std::map <std::string, Vamos_Geometry::Material_Handle> m_materials;

	std::vector <Vamos_Geometry::Material_Handle> m_segment_materials;

	// Groups of materials used on the segments, indexed by name.
	std::map <std::string, 
			  std::vector <Vamos_Geometry::Material_Handle> > m_segments;

	std::string m_data_dir;
	Vamos_Track::Strip_Track* mp_road;
	
	enum Classes
	  {
		NONE,
		MATERIAL,
		ROAD,
		SEGMENT
	  };
	
	Classes m_class;
	bool m_circuit;
    double m_length;

	Vamos_Geometry::Material::Material_Type m_material_type;

 	std::vector <Road_Segment::Model_Info> m_model_info;
 	Road_Segment::Model_Info m_current_model_info;

  public:
	Strip_Track_Reader (std::string data_dir, 
						std::string track_file, 
						Vamos_Track::Strip_Track* road);
  };
}

#endif
