//	Vamos Automotive Simulator
//  Copyright (C) 2001--2003 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 _ROAD_SEGMENT_H_
#define _ROAD_SEGMENT_H_

#include <vamos/geometry/Conversions.h>
#include <vamos/geometry/Linear_Interpolator.h>
#include <vamos/geometry/Material.h>
#include <vamos/geometry/Spline.h>
#include <vamos/geometry/Three_Vector.h>
#include <vamos/geometry/Two_Vector.h>

#include <string>
#include <vector>

namespace Vamos_Track
{
  class Model_Info;

  class Kerb
  {
	std::vector <Vamos_Geometry::Two_Vector> m_points;
	Vamos_Geometry::Linear_Interpolator m_profile;
	double m_start;
	double m_start_transition_length;
	double m_start_transition_width;
	double m_end;
	double m_end_transition_length;
	double m_end_transition_width;
	bool m_full_length;

  public:
	Kerb (const std::vector <Vamos_Geometry::Two_Vector>& profile,
		  double start, 
		  double start_transition_length,
		  double start_transition_width,
		  double end, 
		  double end_transition_length,
		  double end_transition_width);

	void length (double length);

	double transition_start () const { return m_start; }
	double transition_end () const { return m_end; }
	double start () const { return m_start + m_start_transition_length; }
	double end () const { return m_end - m_end_transition_length; }
	double start_transition_width () const { return m_start_transition_width; }
	double end_transition_width () const { return m_end_transition_width; }
	double width () const;
	bool on_kerb (double dist) const;
	double elevation (double along, double from_inside);
	double angle (double along, double from_inside);
	const Vamos_Geometry::Two_Vector& point (size_t substrip);
	size_t substrips () const { return m_points.size () - 1; }
  };

  //===========================================================================
  class Road_Segment
  {
	Road_Segment (const Road_Segment& seg);

    void scale_widths (double factor);

    double skew_slope () const;
    double skew_offset (double x, double y) const;
    double skewed_radius (double x, double y, double offset) const;

  protected:

	// The length of the centerline of the segment.
	double m_length;
	// The radius of curvature; 0.0 for a straight segment. 
	double m_radius;

    double m_start_skew;
    double m_end_skew;

	Vamos_Geometry::Three_Vector m_center;

	// The distance from the center line to the walls.
	Vamos_Geometry::Linear_Interpolator* mp_left_width;
	Vamos_Geometry::Linear_Interpolator* mp_right_width;

	// The distance from the center line to the edges of the track.
	Vamos_Geometry::Linear_Interpolator* mp_left_road_width;
	Vamos_Geometry::Linear_Interpolator* mp_right_road_width;

	Kerb* mp_left_kerb;
	Kerb* mp_right_kerb;
	
	// The heights of the walls.
	double m_left_wall_height;
	double m_right_wall_height;

	std::vector <Vamos_Geometry::Two_Vector> m_elevation_points;
	Vamos_Geometry::Spline* mp_elevation_curve;

	std::vector <Vamos_Geometry::Two_Vector> m_bank_points;
	double m_start_bank;
	double m_end_bank;
	double m_bank_pivot;
	Vamos_Geometry::Spline* mp_bank_curve;

	std::vector <Vamos_Geometry::Material> m_materials;

	double m_start_distance;

	// The endpoints of the segment.
	Vamos_Geometry::Three_Vector m_start_coords;
	Vamos_Geometry::Three_Vector m_end_coords;
	double m_start_angle;

	// True if this is the last segment of a circuit.
	bool m_last_segment;

	double bank_height (double along, double from_center) const;


  public:
    Road_Segment (double length,
                  double radius,
                  double left_width,
                  double right_width,
                  double left_road_width,
                  double right_road_width);

	virtual ~Road_Segment ();

    void set_skew (double skew);
    void set_width_point (double distance, 
                          double width, 
                          Vamos_Geometry::Side side);
    void set_road_width_point (double distance, 
                               double width, 
                               Vamos_Geometry::Side side);
    void set_kerb (Kerb* kerb, Vamos_Geometry::Side side);
    void set_wall_height (double left_height, double right_height);
    void set_elevation_point (double distance, double elevation);
    void set_bank_point (double distance, double bank);
    void set_end_bank (double bank) { m_end_bank = bank; }
    void set_bank_pivot (double pivot) { m_bank_pivot = pivot; }
    void set_materials (const std::vector <Vamos_Geometry::Material>& 
                        materials,
                        double resolution);

	double length () const { return m_length; }
	void set_length (double new_length);

	double radius () const { return m_radius; }
	void set_radius (double new_radius) { m_radius = new_radius; }

    double arc () const 
    { return (m_radius == 0.0) ? 0.0 : m_length / m_radius; }
    void set_arc (double new_arc) { set_length (m_radius * new_arc); }

	void scale (double factor);


	virtual void set_start (const Vamos_Geometry::Three_Vector& start_coords, 
                            double start_distance,
                            double start_angle, 
                            double start_bank,
                            const std::vector <double>& texture_offsets);

	double end_angle () const { return m_start_angle + arc (); }
	double coordinates (const Vamos_Geometry::Three_Vector& world_pos,
						Vamos_Geometry::Three_Vector& track_pos);
    Vamos_Geometry::Three_Vector position (double along, 
                                           double from_center) const;

	Vamos_Geometry::Three_Vector end_coords () const { return m_end_coords; } 
	Vamos_Geometry::Three_Vector start_coords () const 
	{ return m_start_coords; } 

	double end_bank () const { return m_end_bank; }

	double elevation (double along, double from_center) const;
	double angle (double along) const;
	double bank (double along) const;
	Vamos_Geometry::Three_Vector normal (double along, double from_center) const;
	Vamos_Geometry::Three_Vector barrier_normal (double along, 
												 double from_center) const;

	void build_elevation (Vamos_Geometry::Spline* elevation,
						  double start_distance);

	const std::vector <Vamos_Geometry::Material>& materials () const 
	{ return m_materials; }

	double left_width (double dist) const;
	double right_width (double dist) const;
	double left_road_width (double dist) const;
	double right_road_width (double dist) const;
	void last_segment (bool last) { m_last_segment = last; }
	double start_distance () const { return m_start_distance; }

    void set_start_skew (double skew) { m_start_skew = skew; }
    void set_end_skew (double skew) { m_end_skew = skew; }
    double skew () const { return m_start_skew; }

	const Vamos_Geometry::Material& left_material () const 
	{ return *(m_materials.begin ()); }
	const Vamos_Geometry::Material& right_material () const 
	{ return *(m_materials.end () - 1); }
	const Vamos_Geometry::Material& 
	material_at (double along, double from_center);



	virtual void build () {};
	virtual void draw () const {};

	virtual void add_model_info (const Model_Info&) {};

	virtual std::vector <double> texture_offsets () const
    { return std::vector <double> (); }

	virtual double min_x () const { return 0.0; }
	virtual double max_x () const { return 100.0; }
	virtual double min_y () const { return 0.0; }
	virtual double max_y () const { return 100.0; }
  };
}

#endif // not _ROAD_SEGMENT_H_
