//  Strip_Track.cc - a 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

#include <vamos/track/Strip_Track.h>
#include <vamos/geometry/Spline.h>
#include <vamos/geometry/Gl_Texture_Image.h>

#include <GL/glu.h>

#include <cmath>
#include <sstream>
#include <cassert>

using namespace Vamos_Geometry;

//* Class Strip_Track

Vamos_Track::
Strip_Track::Strip_Track () :
  m_min_x (0.0),
  m_max_x (0.0),
  m_min_y (0.0),
  m_max_y (0.0),
  //mp_elevation (new Spline (0.0, 0.0)),
  m_length (0.0),
  m_sky_list_id (0)
{
  m_timing_lines.clear ();
}

extern bool verbose_output;

Vamos_Track::
Strip_Track::~Strip_Track ()
{
	if (verbose_output)
		std::cout << "track deinit" << std::endl;
  for (std::vector <Road_Segment*>::iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
	  delete (*it);
	}
  //delete mp_elevation;
	
	if (verbose_output)
		std::cout << "track deinit done" << std::endl;
}

// Read the track definition file.
void Vamos_Track::
Strip_Track::read (std::string data_dir, std::string track_file)
{
  // Remember the file name for re-reading.
  if ((data_dir != "") && (track_file != ""))
	{
	  m_data_dir = data_dir;
	  m_track_file = track_file;
	}

  m_min_x = 0.0;
  m_max_x = 0.0;
  m_min_y = 0.0;
  m_max_y = 0.0;

  for (std::vector <Road_Segment*>::iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
	  delete (*it);
	}
  m_segments.clear ();
  m_timing_lines.clear ();

  Strip_Track_Reader reader (m_data_dir, m_track_file, this);
}

void Vamos_Track::
Strip_Track::build_sky_box (std::string sides_image, 
							std::string top_image,
							std::string bottom_image,
							bool smooth)
{
  if (m_sky_list_id != 0)
	{
	  glDeleteLists (m_sky_list_id, 1);
	}

  double height = 400.0;
  double length = 400.0;
  double width = 400.0;

  double x = -length / 2.0;
  double y = -width / 2.0;
  double z = -height / 2.0;

  Gl_Texture_Image sky_sides (sides_image, smooth);
  Gl_Texture_Image sky_top (top_image, smooth);
  Gl_Texture_Image sky_bottom (bottom_image, smooth);

  // Clamp the textures to aviod showing seams.
  sky_sides.clamp_to_edge ();
  sky_top.clamp_to_edge ();
  sky_bottom.clamp_to_edge ();

  m_sky_list_id = glGenLists (1);
  glNewList (m_sky_list_id, GL_COMPILE);

  glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);

  sky_sides.activate ();

  // front
  glBegin (GL_QUAD_STRIP);
  glTexCoord2d (0.0, 0.0); 
  glVertex3d (x + length, y + width, z + height);
  glTexCoord2d (0.0, 1.0); 
  glVertex3d (x + length, y + width, z);
  glTexCoord2d (0.25, 0.0); 
  glVertex3d (x + length, y, z + height); 
  glTexCoord2d (0.25, 1.0); 
  glVertex3d (x + length, y, z);

  // right
  glTexCoord2d (0.25, 0.0); 
  glVertex3d (x + length, y, z + height);
  glTexCoord2d (0.25, 1.0); 
  glVertex3d (x + length, y, z);
  glTexCoord2d (0.5, 0.0); 
  glVertex3d (x, y, z + height); 
  glTexCoord2d (0.5, 1.0); 
  glVertex3d (x, y, z);

  // back
  glTexCoord2d (0.5, 0.0); 
  glVertex3d (x, y, z + height);
  glTexCoord2d (0.5, 1.0); 
  glVertex3d (x, y, z);
  glTexCoord2d (0.75, 0.0); 
  glVertex3d (x, y + width, z + height); 
  glTexCoord2d (0.75, 1.0); 
  glVertex3d (x, y + width, z);

  // left
  glTexCoord2d (0.75, 0.0); 
  glVertex3d (x, y + width, z + height);
  glTexCoord2d (0.75, 1.0); 
  glVertex3d (x, y + width, z);
  glTexCoord2d (1.0, 0.0); 
  glVertex3d (x + length, y + width, z + height); 
  glTexCoord2d (1.0, 1.0); 
  glVertex3d (x + length, y + width, z);
  glEnd();

  // top
  sky_top.activate ();

  glBegin (GL_QUADS);		
  glTexCoord2d (0.0, 0.0); 
  glVertex3d (x, y + width, z + height);
  glTexCoord2d (0.0, 1.0); 
  glVertex3d (x + length, y + width, z + height); 
  glTexCoord2d (1.0, 1.0); 
  glVertex3d (x + length, y, z + height);
  glTexCoord2d (1.0, 0.0); 
  glVertex3d (x, y, z + height);
  glEnd();

  // bottom
  sky_bottom.activate ();

  glBegin (GL_QUADS);		
  glTexCoord2d (0.0, 0.0); 
  glVertex3d (x, y + width, z);
  glTexCoord2d (0.0, 1.0); 
  glVertex3d (x, y + width, z);
  glTexCoord2d (1.0, 1.0); 
  glVertex3d (x + length, y, z); 
  glTexCoord2d (1.0, 0.0); 
  glVertex3d (x + length, y, z);
  glEnd();

  glFlush ();
  glEndList ();
}

// Add a straight or curved segment of road to the track.
void Vamos_Track::
Strip_Track::add_segment (Road_Segment* seg)
{
  m_segments.push_back (seg);
}

// Make the track.
void Vamos_Track::
Strip_Track::build (bool is_closed, double track_length)
{
  set_skews ();
  if (is_closed)
    {
      build_circuit ();
    }
  if (track_length != 0.0)
    {
      set_length (track_length);
    }

  m_length = 0.0;
  
  //mp_elevation->clear ();
  //mp_elevation->load (Two_Point (0.0, 0.0));

  Three_Vector start_coords (0.0, 0.0, 0.0);
  double start_angle = 0.0;
  double start_bank = 0.0;

  for (std::vector <Road_Segment*>::iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
	  //(*it)->build_elevation (mp_elevation, m_length);
	  m_length += (*it)->length ();
	}

  size_t strips = (*(m_segments.begin ()))->materials ().size ();
  std::vector <double> texture_offsets (strips);

	//enable detail texture
	utility.SelectTU(1);
	glEnable(GL_TEXTURE_2D);
	detailtex = utility.TexLoad("track/track2.png", GL_RGB, true);
	glBindTexture(GL_TEXTURE_2D, detailtex);
	utility.SelectTU(0);
	
  m_length = 0.0;
  for (std::vector <Road_Segment*>::iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
	  (*it)->build (m_length, start_coords, start_angle, start_bank,
					texture_offsets, detailtex);
	  texture_offsets = (*it)->texture_offsets ();

	  // Update the bounding dimensions.
	  if ((*it)->min_x () < m_min_x)
		m_min_x = (*it)->min_x ();
	  if ((*it)->max_x () > m_max_x)
		m_max_x = (*it)->max_x ();
	  if ((*it)->min_y () < m_min_y)
		m_min_y = (*it)->min_y ();
	  if ((*it)->max_y () > m_max_y)
		m_max_y = (*it)->max_y ();

	  m_length += (*it)->length ();
	  start_coords = (*it)->end_coords ();
	  start_angle = (*it)->end_angle ();
	  start_bank = (*it)->end_bank ();
	}
}

void Vamos_Track::
Strip_Track::set_skews ()
{
  for (std::vector <Road_Segment*>::iterator it = m_segments.begin () + 1;
	   it != m_segments.end ();
	   it++)
	{
      double skew = (*it)->skew ();
      if ((skew != 0.0) && ((*it)->arc () != 0.0))
        {
          if ((*(it - 1))->arc () == 0.0)
            (*(it - 1))->set_end_skew (skew);
          if ((*(it + 1))->arc () == 0.0)
            (*(it + 1))->set_start_skew (-skew);
        }
    }
}

void Vamos_Track::
Strip_Track::build_circuit ()
{
  Road_Segment* last_straight = *(m_segments.end () - 1);
  Road_Segment* last_curve = *(m_segments.end () - 2);
  Road_Segment* other_straight = *(m_segments.end () - 3);

  if ((last_straight->radius () != 0.0) 
	  || (last_curve->radius () == 0.0) 
	  || (other_straight->radius () != 0.0))
	{
	  throw Can_Not_Close ();
	}

  double end_x = 0.0;
  double end_y = 0.0;
  double end_angle = 0.0;
  double straight_angle = 0.0;
  double center_x = 0.0;
  double center_y = 0.0;
  for (std::vector <Road_Segment*>::iterator it = m_segments.begin ();
	   it != m_segments.end () - 1;
	   it++)
	{
      if ((*it)->radius () == 0.0)
        {
          end_x += (*it)->length () * cos (end_angle);
          end_y += (*it)->length () * sin (end_angle);
          straight_angle = end_angle;
        }
      else
        {
          center_x = end_x - (*it)->radius () * sin (end_angle);
          center_y = end_y + (*it)->radius () * cos (end_angle);
          end_angle += (*it)->arc ();
          end_x = center_x + (*it)->radius () * sin (end_angle);
          end_y = center_y - (*it)->radius () * cos (end_angle);
        }
    }

  // Change the length, but not the radius of the last curve to make the last
  // straight and parallel with the beginning of the first segment.
  end_angle -= last_curve->arc ();
  if (end_angle > pi)
    {
      end_angle = 2.0 * pi - end_angle;
    }
  else if (end_angle < -pi)
    {
      end_angle = -2.0 * pi - end_angle;
    }
  else
    {
      end_angle = -end_angle;
    }

  last_curve->set_arc (end_angle);

  double delta_length = 
    -(center_y - last_curve->radius ()) / sin (straight_angle);
  other_straight->set_length (other_straight->length () + delta_length);

  last_straight->set_length (-center_x 
                             - delta_length * cos (straight_angle)
                             + 0.5);

  // Force the segment to end at 0, 0.
  last_straight->last_segment (true);
}

void Vamos_Track::
Strip_Track::draw_sky (const Vamos_Geometry::Three_Vector& view) const
{
  glLoadIdentity ();
  glTranslatef (view [0], view [1], view [2]);

  // Draw the sky and then clear the depth buffer to keep it behind
  // everything else.
  glCallList (m_sky_list_id);
  glClear (GL_DEPTH_BUFFER_BIT);
}

void Vamos_Track::
Strip_Track::draw () const
{
  //glLoadIdentity ();
  for (std::vector <Road_Segment*>::const_iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
	  (*it)->draw ();
	}
}

void Vamos_Track::Strip_Track::
speed_trap (double start, double end)
{
  m_speed_trap_start = start;
  m_speed_trap_end = end;
}

// Scale the track to a particular length.
void Vamos_Track::
Strip_Track::set_length (double length)
{
  assert (m_segments.size () != 0);

  // Find the current length.
  double old_length = 0.0;
  for (std::vector <Road_Segment*>::iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
	  old_length += (*it)->length ();
	}

  assert (old_length != 0.0);
  double factor = length / old_length;

  // Adjust the segments.
  for (std::vector <Road_Segment*>::iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
	  (*it)->scale (factor);
	}
}

//!! New Interface Methods

// Return the new position for a vehicle at POS when a reset is
// performed.
Three_Vector Vamos_Track::
Strip_Track::reset_position (const Three_Vector& pos,
							 size_t& segment_index)
{
  const Three_Vector& track_pos = track_coordinates (pos, segment_index);

  const Road_Segment* segment = m_segments [segment_index];
  const double along = track_pos [0] - segment->start_distance ();
  Three_Vector direction = segment->barrier_normal (along, -1.0);
  direction [2] = -sin (segment->bank (along));

  const double across = track_pos [1];
  return pos - across * direction;
}

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

  // Align the car's up direction with the normal.
  const Three_Vector& track_pos = track_coordinates (pos, segment_index);

  const Road_Segment* segment = m_segments [segment_index];
  const double along = track_pos [0] - segment->start_distance ();
  const double across = track_pos [1];
  Three_Vector normal = segment->normal (along, across); 
  orientation.rotate (Three_Vector (-asin (normal [1]), 
									asin (normal [0]),
									segment->angle (along)));
  return orientation;
}

// Return the elevation of the track at the x and y components of POS.
double Vamos_Track::
Strip_Track::elevation (const Three_Vector& pos, 
						double bump_parameter,
						size_t& segment_index)
{
  //return track_coordinates (pos, segment_index)[2] 
	//+ m_material->bump (bump_parameter);

	track_coordinates(pos, segment_index);
	
	return m_segments[segment_index]->elevationxy(pos[0], pos[1])
	+ m_material->bump (bump_parameter);
}

bool Vamos_Track::Strip_Track::ontrack(const Vamos_Geometry::Three_Vector& world_pos)
{
	size_t segment_index = 0;
	int i = 0;
	
	for (i = 0; i < (int) m_segments.size(); i++)
	{
		bool found = false;
		segment_index = i;
		Three_Vector track_pos = segment_coordinates (world_pos, segment_index, found);
		//double w = 20.0;
		//if ((track_pos[1] >= 0 && track_pos[1] < w) || (track_pos[1] < 0 && track_pos[1] > -w))
		if (found)
		{
			if ((track_pos[1] >= 0 && track_pos[1] < m_segments[segment_index]->left_width(track_pos[0])) || (track_pos[1] < 0 && track_pos[1] > -m_segments[segment_index]->right_width(track_pos[0])))
			{
				//cout << track_pos[1] << " ";
				//cout << i << "/" << m_segments.size() << " ";
				return true;
			}
		}
	}

	return false;
}

size_t Vamos_Track::Strip_Track::getsegment(const Vamos_Geometry::Three_Vector& pos)
{
	/*size_t insegment = 0;
	
	track_coordinates(pos, insegment);
	
	return insegment;*/
	
	size_t segment_index = 0;
	int i = 0;
	
	size_t insegment = 0;
	
	for (i = 0; i < (int) m_segments.size(); i++)
	{
		segment_index = i;
		//Three_Vector track_pos = track_coordinates (pos, segment_index);
		Three_Vector track_pos;
		double innit = m_segments[segment_index]->coordinates (pos, track_pos);
		
		//double w = 20.0;
		//if ((track_pos[1] >= 0 && track_pos[1] < w) || (track_pos[1] < 0 && track_pos[1] > -w))
		if (innit == 0.0f && ((track_pos[1] >= 0 && track_pos[1] < m_segments[segment_index]->left_width(track_pos[0])) || (track_pos[1] < 0 && track_pos[1] > -m_segments[segment_index]->right_width(track_pos[0]))))
		{
			//cout << track_pos[1] << " ";
			//cout << i << "/" << m_segments.size() << " ";
			//return true;
			insegment = i;			
		}
	}

	return insegment;
}

/*bool Vamos_Track::Strip_Track::ontrack(const Vamos_Geometry::Three_Vector& world_pos)
{
	Three_Vector track_pos;
	unsigned int segment_index = 0;
	assert (segment_index < m_segments.size ());
	Road_Segment* segment = m_segments [segment_index];

	double off = 0;
	
	size_t i = 0;
	while (i < m_segments.size ())
	{
	  off = segment->coordinates (world_pos, track_pos); 
	  if (off == 0.0)
		{
		  break;
		}

	  if (off > 0.0)
		{
		  segment_index++;
		  if (segment_index == m_segments.size ())
			{
			  segment_index = 0;
			}
		}
	  else
		{
		  if (segment_index == 0)
			{
			  segment_index = m_segments.size ();
			}
		  segment_index--;
		}
	  segment = m_segments [segment_index];
	  i++;
	}

	// Throw an exception if a segment could not be found.
	if (i >= m_segments.size ())
	{
		//return true;
		return false;
	}
	else
	{
		//cout << track_pos[0] << "," << track_pos[1] << "," << track_pos[2] << ":" << segment->start_distance() << ":" << segment->left_road_width(track_pos[0]) << endl;
		*//*if (track_pos[1] >= 0)
		{
			double w = segment->left_width(track_pos[0]);
			if (track_pos[1] < w)
				return true;
		}
		else
		{
			double w = segment->right_width(track_pos[0]+segment->start_distance());
			if (track_pos[1] > w)
				return true;
		}*/
		/*
		//double w = segment->left_road_width(track_pos[0]);
		//if (track_pos[1] > w)
		double w = 20.0;
		if ((track_pos[1] >= 0 && track_pos[1] < w) || (track_pos[1] < 0 && track_pos[1] > -w))
		{
			//cout << track_pos[1] << " ";
			//cout << i << "/" << m_segments.size() << " ";
			return true;
		}
		
		return false;
	}
}*/

// Return WORLD_POS transformed to the track's coordinate system.
// SEGMENT_INDEX will be modified if the position on another
// segment.
Three_Vector Vamos_Track::
Strip_Track::track_coordinates (const Three_Vector& world_pos,
								size_t& segment_index)
{
  // Find the distance along the track, distance from center, and elevation
  // for the world coordinates `world_pos [0]' and `world_pos [1]'.

  Three_Vector track_pos;
  assert (segment_index < m_segments.size ());
  Road_Segment* segment = m_segments [segment_index];

	/*if (segment_index == 0)
		cout << "bingo" << endl;*/
	
	double off = -1337;
	
  size_t i = 0;
	//cout << "begin" << endl;
  while (i < m_segments.size ())
	{
		//cout << i << "!!!" << endl;
	  off = segment->coordinates (world_pos, track_pos); 
		//cout << off << endl;
	  if (off == 0.0)
		{
			//cout << "fuk" << endl;
		  break;
		}

	  if (off > 0.0)
		{
		  segment_index++;
		  if (segment_index == m_segments.size ())
			{
			  segment_index = 0;
			}
		}
	  else
		{
		  if (segment_index == 0)
			{
			  segment_index = m_segments.size ();
			}
		  segment_index--;
		}
	  segment = m_segments [segment_index];
	  i++;
	}

  // Throw an exception if a segment could not be found.
  if (i == m_segments.size ())
	{
	  std::cout << "road segment not found" << std::endl;
		//cout << off << endl;
	  throw Segment_Not_Found (world_pos);
	}
	
  assert (segment_index < m_segments.size ());
  m_material = segment->material_at (track_pos [0], track_pos [1]);
  track_pos[0] += segment->start_distance ();
  return track_pos;
}

Three_Vector Vamos_Track::
Strip_Track::segment_coordinates (const Three_Vector& world_pos,
								size_t& segment_index, bool &found)
{
  // Find the distance along the track, distance from center, and elevation
  // for the world coordinates `world_pos [0]' and `world_pos [1]'.

  Three_Vector track_pos;
  assert (segment_index < m_segments.size ());
  Road_Segment* segment = m_segments [segment_index];

	/*if (segment_index == 0)
		cout << "bingo" << endl;*/
	
	double off = -1337;
	
  size_t i = 0;
	//cout << "begin" << endl;
  while (i < m_segments.size ())
	{
		//cout << i << "!!!" << endl;
	  off = segment->coordinates (world_pos, track_pos); 
		//cout << off << endl;
	  if (off == 0.0)
		{
		  break;
		}

	  if (off > 0.0)
		{
		  segment_index++;
		  if (segment_index == m_segments.size ())
			{
			  segment_index = 0;
			}
		}
	  else
		{
		  if (segment_index == 0)
			{
			  segment_index = m_segments.size ();
			}
		  segment_index--;
		}
	  segment = m_segments [segment_index];
	  i++;
	}

  // Throw an exception if a segment could not be found.
  if (i == m_segments.size ())
	{
	  //std::cout << "road segment not found" << std::endl;
		//cout << off << endl;
	  found = false;
		return track_pos;
	}

	//cout << off << endl;
	//cout << i << endl;

	found = true;
  assert (segment_index < m_segments.size ());
  m_material = segment->material_at (track_pos [0], track_pos [1]);
  track_pos [0] += segment->start_distance ();
  return track_pos;
}

Vamos_Track::Track_Contact_Info Vamos_Track::
Strip_Track::test_for_contact (const Three_Vector& pos, 
							   double bump_parameter,
							   size_t& segment_index)
{
  Three_Vector track_pos = track_coordinates (pos, segment_index);
  const Road_Segment* segment = m_segments [segment_index];
  track_pos [0] -= segment->start_distance ();

  bool contact = false;
  Three_Vector normal;

  // Test for contact with the road.
  double diff = elevation (pos, bump_parameter, segment_index) - pos [2];
  if (diff >= 0.0)
	{
	  contact = true;
	  normal = segment->normal (track_pos [0], track_pos [1]);
	}

  // Test for contact with the walls.
  // Left
  if (!contact)
	{
	  const Material_Handle material = segment->left_material ();
	  double bump = material->bump (bump_parameter);
	  diff = track_pos [1] - (segment->left_width (track_pos [0]) + bump); 
	  if (diff >= 0.0)
		{
		  contact = true;
		  normal = segment->barrier_normal (track_pos [0], track_pos [1]);
		}
	}

  // Right
  if (!contact)
	{
	  const Material_Handle material = segment->right_material ();
	  double bump = material->bump (bump_parameter);
	  diff = -track_pos [1] - (segment->right_width (track_pos [0]) + bump); 
	  if (diff >= 0.0)
		{
		  contact = true;
		  normal = segment->barrier_normal (track_pos [0], track_pos [1]);
		}
	}

  return Track_Contact_Info (contact, diff, normal, m_material);
}

// Return the distance along the track for the given position.
double Vamos_Track::
Strip_Track::distance (const Three_Vector& pos, size_t& segment_index)
{	
  return track_coordinates (pos, segment_index)[0];
}

Three_Vector Vamos_Track::
Strip_Track::position (double along, double from_center) const
{
  assert ((along >= 0.0) && (along <= m_length));
  double distance = 0.0;
  for (std::vector <Road_Segment*>::const_iterator it = m_segments.begin ();
	   it != m_segments.end ();
	   it++)
	{
      if (distance + (*it)->length () >= along)
        {
          return (*it)->position (along - distance, from_center);
        }
      distance += (*it)->length ();
	}
  assert (false);
  return Three_Vector (0.0, 0.0, 0.0);
}

// Return the timing sector at the given distance.
int Vamos_Track::
Strip_Track::sector (double distance)
{
  for (size_t i = 0; i < m_timing_lines.size (); i++)
	{
	  if (m_timing_lines [i] > distance)
		return i - 1;
	}
  return m_timing_lines.size () - 1;
}

//* Class Track_Reader

//** Constructor
Vamos_Track::
Strip_Track_Reader::Strip_Track_Reader (std::string data_dir, 
										std::string track_file, 
										Strip_Track* road) 
  : m_first_road (true),
	m_data_dir (data_dir),
	mp_road (road),
    m_circuit (false),
    m_length (0.0)
{
  read (data_dir + track_file);
}

void Vamos_Track::
Strip_Track_Reader::on_start_tag (const Vamos_Geometry::XML_Tag& tag)
{
  //  std::cout << "start " << tag.get_label () << std::endl;
  m_tag = tag.get_label ();
  m_path = m_path + '/' + m_tag;

  const Vamos_Geometry::XML_Tag::Attribute_List& 
	attribs = tag.get_attributes ();

  if (m_tag == "sky")
	{
	  m_strings.clear ();
	  m_strings.resize (3);
	  m_bools.clear ();
	  m_bools.resize (1);
	}
  else if (m_tag == "material")
	{
	  m_name = attribs [0].value;
	  std::string type = attribs [1].value;
	  if (type == "rubber")
		m_material_type = Material::RUBBER;
	  else if (type == "metal")
		m_material_type = Material::METAL;
	  else if (type == "asphalt")
		m_material_type = Material::ASPHALT;
	  else if (type == "concrete")
		m_material_type = Material::CONCRETE;
	  else if (type == "grass")
		m_material_type = Material::GRASS;
	  else if (type == "gravel")
		m_material_type = Material::GRAVEL;
	  else if (type == "dirt")
		m_material_type = Material::DIRT;
	  else
		{
		  std::cerr << "Strip_Track_Reader: Warning: Unknown material \"" 
					<< attribs [1].value << '\"' << std::endl;
		  m_material_type = Material::UNKNOWN;
		}
			
	  m_bools.clear ();
	  m_bools.resize (2);
	  m_doubles.clear ();
	  m_doubles.resize (8);
	  m_strings.clear ();
	}
  else if (m_tag == "segment")
	{
	  m_name = attribs [0].value;
	  m_strings.resize (7);
	}
  else if (m_tag == "road")
	{
	  if (m_first_road)
		{
		  // Clear the vector so that it gets filled with 0.0 when
		  // it's resized.
		  m_doubles.clear ();
		  m_doubles.resize (21);
		  m_bools.clear ();
		  m_bools.resize (6);
		  m_points.clear ();
		  m_points.resize (2);
		  m_left_profile.clear ();
		  m_right_profile.clear ();

		  m_first_road = false;
		}

	  m_doubles [3] = 0.0; // left kerb start
	  m_doubles [6] = 0.0; // left kerb end
	  m_doubles [9] = 0.0; // right kerb start
	  m_doubles [12] = 0.0; // right kerb end
      m_doubles [20] = 0.0; // skew
	  m_strings.clear ();
	  m_strings.resize (2);
	  m_strings [0] = attribs [0].value;
	  m_bools [0] = false;
	  m_bools [1] = false;
	  m_bools [2] = false;
	  m_bools [3] = false;
	  m_bools [4] = false;
	  m_elev_points.clear ();

	  for (size_t i = 0; i < 4; i++)
		{
		  if (m_point_vectors [i].size () > 0)
			{
			  Two_Point last_point = *(m_point_vectors [i].end () - 1);
			  last_point.x = 0.0;
			  m_point_vectors [i].clear ();
			  m_point_vectors [i].push_back (last_point);
			}
		}

	  m_braking_markers.clear ();
	  m_model_info.clear ();
	}
  else if (m_tag == "left-kerb")
	{
	  m_doubles [6] = -1.0; // end
	}
  else if (m_tag == "right-kerb")
	{
	  m_doubles [12] = -1.0; // end
	}
  else if (m_tag == "circuit")
	{
	  m_circuit = true;
	}
  else if (m_tag == "timing-line")
	{
	  m_doubles.clear ();
	}
  else if (m_tag == "track-length")
	{
	  m_doubles.clear ();
	}

  if (m_path == "/track/road/left-kerb/start/transition")
	{
	  m_bools [1] = true;
	}
  else if (m_path == "/track/road/left-kerb/end/transition")
	{
	  m_bools [2] = true;
	}
  else if (m_path == "/track/road/right-kerb/start/transition")
	{
	  m_bools [3] = true;
	}
  else if (m_path == "/track/road/right-kerb/end/transition")
	{
	  m_bools [4] = true;
	}
}

void Vamos_Track::
Strip_Track_Reader::on_end_tag (const Vamos_Geometry::XML_Tag& tag)
{
  //  std::cout << "end " << tag.get_label () << std::endl;
  m_tag = tag.get_label ();
  m_path = m_path.substr (0, m_path.find_last_of ("/"));

  if (m_tag == "sky")
	{
	  mp_road->build_sky_box (m_data_dir + m_strings [0], 
							  m_data_dir + m_strings [1],
							  m_data_dir + m_strings [2],
							  m_bools [0]);
	}
  else if (m_tag == "material")
	{
	  m_materials [m_name] = 
		Material_Handle (new Material 
						 (m_material_type,
						  m_doubles [0], m_doubles [1],
						  m_doubles [2], m_doubles [3],
						  m_doubles [4], m_doubles [5], 
						  new Gl_Texture_Image (m_data_dir + m_strings [0],
												m_bools [0],
												m_bools [1],
												m_doubles [6],
												m_doubles [7])));
	}
  else if (m_tag == "smooth")
	{
	  m_bools [0] = true;
	}
  else if (m_tag == "mipmap")
	{
	  m_bools [1] = true;
	}
  else if (m_tag == "segment")
	{
	  std::vector <Material_Handle> mat;
	  mat.resize (7);
	  mat [0] = m_materials [m_strings [0]];
	  mat [1] = m_materials [m_strings [1]];
	  mat [2] = m_materials [m_strings [2]];
	  mat [3] = m_materials [m_strings [3]];
	  mat [4] = m_materials [m_strings [4]];
	  mat [5] = m_materials [m_strings [5]];
	  mat [6] = m_materials [m_strings [6]];
	  m_segments [m_name] = mat;
	}
  else if (m_tag == "braking-marker")
	{
	  Side side = m_bools [5] ? RIGHT : LEFT;
	  m_braking_markers.
		push_back (new Braking_Marker (m_doubles [19],
									   m_points [0],
									   m_points [1],
									   side,
									   m_data_dir + m_strings [1]));
	}
  else if (m_tag == "model")
	 {
 	  m_model_info.push_back (m_current_model_info);
 	}
  else if (m_tag == "road")
	{
	  double left_start_trans = m_bools [1] ? m_doubles [4] : 0.0;
	  double left_end_trans = m_bools [2] ? m_doubles [7] : 0.0;
	  double right_start_trans = m_bools [3] ? m_doubles [10] : 0.0;
	  double right_end_trans = m_bools [4] ? m_doubles [13] : 0.0;

	  std::vector <Two_Point> vec;

	  if (!m_bools [0])
		{
		  m_doubles [2] = 0.0;
		}
	  Road_Segment* segment = new Road_Segment (m_doubles [0], 
												m_doubles [1], 
												m_doubles [2],
                                                m_doubles [20],
												m_point_vectors [0], 
												m_point_vectors [1],
												m_point_vectors [2], 
												m_point_vectors [3],
												new Kerb (m_left_profile, 
														  m_doubles [3],
														  left_start_trans, 
														  m_doubles [5],
														  m_doubles [6],
														  left_end_trans,
														  m_doubles [8]),
												new Kerb (m_right_profile,
														  m_doubles [9],
														  right_start_trans,
														  m_doubles [11],
														  m_doubles [12],
														  right_end_trans,
														  m_doubles [14]),
												m_doubles [15], 
												m_doubles [16],
												//m_elev_points, 
												vec,
												m_doubles [17], 
												m_doubles [18],
												m_segments [m_strings [0]],
												m_braking_markers);
	  mp_road->add_segment (segment);
	  for (std::vector <Road_Segment::Model_Info>::iterator
			 it = m_model_info.begin ();
		   it !=  m_model_info.end ();
		   it++)
		{
		  segment->add_model_info (*it);
		}
	}
  else if (m_tag == "timing-line")
	{
	  mp_road->timing_line (m_doubles [0]);
	}
  else if (m_tag == "track")
    {
      mp_road->build (m_circuit, m_length);
    }
}

void Vamos_Track::
Strip_Track_Reader::on_data (std::string data_string)
{
  std::string data = remove_leading_space (data_string);
  if (data.size () == 0)
	{
	  return;
	}
  std::istringstream is (data.c_str ());

  char delim;

  if (m_path == "/track/sky/sides")
	{
	  m_strings [0] = data;
	}
  else if (m_path == "/track/sky/top")
	{
	  m_strings [1] = data;
	}
  else if (m_path == "/track/sky/bottom")
	{
	  m_strings [2] = data;
	}
  else if (m_path == "/track/material/friction")
	{
	  is >> m_doubles [0];
	}
  else if (m_path == "/track/material/restitution")
	{
	  is >> m_doubles [1];
	}
  else if (m_path == "/track/material/rolling")
	{
	  is >> m_doubles [2];
	}
  else if (m_path == "/track/material/drag")
	{
	  is >> m_doubles [3];
	}
  else if (m_path == "/track/material/bump-amplitude")
	{
	  is >> m_doubles [4];
	}
  else if (m_path == "/track/material/bump-wavelength")
	{
	  is >> m_doubles [5];
	}
  else if (m_path == "/track/material/texture/width")
	{
	  is >> m_doubles [6];
	}
  else if (m_path == "/track/material/texture/length")
	{
	  is >> m_doubles [7];
	}
  else if (m_path == "/track/material/texture/file")
	{
	  m_strings.push_back (data);
	}
  else if (m_path == "/track/segment")
	{
	  is >> delim >> m_strings [0] 
		 >> m_strings [1] 
		 >> m_strings [2] 
		 >> m_strings [3] 
		 >> m_strings [4]
		 >> m_strings [5]
		 >> m_strings [6];
	}
  else if (m_path == "/track/road/resolution")
	{
	  is >> m_doubles [0];
	}
  else if (m_path == "/track/road/length")
	{
	  is >> m_doubles [1];
	}
  else if (m_path == "/track/road/radius")
	{
	  m_bools [0] = true;
	  is >> m_doubles [2];
	}
  else if (m_path == "/track/road/skew")
	{
	  is >> m_doubles [20];
	}
  else if (m_path == "/track/road/left-width")
	{
	  Two_Point point;
	  is >> point;
	  m_point_vectors [0].push_back (point);
	}
  else if (m_path == "/track/road/right-width")
	{
	  Two_Point point;
	  is >> point;
	  m_point_vectors [1].push_back (point);
	}
  else if (m_path == "/track/road/left-road-width")
	{
	  Two_Point point;
	  is >> point;
	  m_point_vectors [2].push_back (point);
	}
  else if (m_path == "/track/road/right-road-width")
	{
	  Two_Point point;
	  is >> point;
	  m_point_vectors [3].push_back (point);
	}
  // Left Kerb
  else if (m_path == "/track/road/left-kerb/start/distance")
	{
	  is >> m_doubles [3];
	}
  else if (m_path == "/track/road/left-kerb/start/transition/length")
	{
	  is >> m_doubles [4];
	}
  else if (m_path == "/track/road/left-kerb/start/transition/width")
	{
	  is >> m_doubles [5];
	}
  else if (m_path == "/track/road/left-kerb/end/distance")
	{
	  is >> m_doubles [6];
	}
  else if (m_path == "/track/road/left-kerb/end/transition/length")
	{
	  is >> m_doubles [7];
	}
  else if (m_path == "/track/road/left-kerb/end/transition/width")
	{
	  is >> m_doubles [8];
	}
  else if (m_path == "/track/road/left-kerb/profile")
	{
	  Two_Point point;
	  m_left_profile.clear ();
	  m_left_profile.push_back (point);
	  while (is >> point)
		{
		  m_left_profile.push_back (point);
		}
	}
  // Right Kerb
  else if (m_path == "/track/road/right-kerb/start/distance")
	{
	  is >> m_doubles [9];
	}
  else if (m_path == "/track/road/right-kerb/start/transition/length")
	{
	  is >> m_doubles [10];
	}
  else if (m_path == "/track/road/right-kerb/start/transition/width")
	{
	  is >> m_doubles [11];
	}
  else if (m_path == "/track/road/right-kerb/end/distance")
	{
	  is >> m_doubles [12];
	}
  else if (m_path == "/track/road/right-kerb/end/transition/length")
	{
	  is >> m_doubles [13];
	}
  else if (m_path == "/track/road/right-kerb/end/transition/width")
	{
	  is >> m_doubles [14];
	}
  else if (m_path == "/track/road/right-kerb/profile")
	{
	  Two_Point point;
	  m_right_profile.clear ();
	  m_right_profile.push_back (point);
	  while (is >> point)
		{
		  m_right_profile.push_back (point);
		}
	}

  else if (m_path == "/track/road/left-wall-height")
	{
	  is >> m_doubles [15];
	}
  else if (m_path == "/track/road/right-wall-height")
	{
	  is >> m_doubles [16];
	}
  else if (m_path == "/track/road/elevation")
	{
	  char delim;
	  double dist;
	  double elev;
	  is >> delim >> dist >> delim >> elev;
	  //m_elev_points.push_back (Two_Point (dist, elev));
	}
  else if (m_path == "/track/road/bank")
	{
	  is >> m_doubles [17];
	}
  else if (m_path == "/track/road/bank-pivot")
	{
	  is >> m_doubles [18];
	}
  else if (m_path == "/track/road/braking-marker/file")
	{
	  m_strings [1] = data;
	}
  else if (m_path == "/track/road/braking-marker/distance")
	{
	  is >> m_doubles [19];
	}
  else if (m_path == "/track/road/braking-marker/size")
	{
	  is >> m_points [0];
	}
  else if (m_path == "/track/road/braking-marker/offset")
	{
	  is >> m_points [1];
	}
  else if (m_path == "/track/road/braking-marker/side")
	{
	  m_bools [5] = (data == "right");
	}
  else if (m_path == "/track/road/model/file")
	{
	  std::string file;
	  is >> file;
	  m_current_model_info.file = m_data_dir + "tracks/" + file;
	}
  else if (m_path == "/track/road/model/scale")
	{
	  is >> m_current_model_info.scale;
	}
  else if (m_path == "/track/road/model/translate")
	{
	  is >> m_current_model_info.translation;
	}
  else if (m_path == "/track/road/model/rotate")
	{
	  is >> m_current_model_info.rotation;
	}
  else if (m_path == "/track/track-length")
    {
      is >> m_length;
    }
  else
	{
	  double arg;
	  is >> arg;
	  m_doubles.push_back (arg);
	}
}
