//  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 _WORLD_H_
#define _WORLD_H_

#include <vamos/body/Car.h>
#include <vamos/geometry/Material.h>
#include <vamos/geometry/Three_Vector.h>
//#include <vamos/track/Track.h>
//#include <vamos/track/Strip_Track.h>
//#include <vamos/track/Road_Segment.h>

#include "track.h"

#include <vector>

namespace Vamos_World
{
  typedef Vamos_Geometry::Material::Material_Type Mat_Type;

  class Times
  {
	friend class Timing_Info;

	double m_current;
	double m_previous;
	double m_best;
	double m_difference;

  public:
	Times ();
	void update (double time_step);
	void finalize ();
	void reset ();
  };

  class Timing_Info
  {
	Times m_lap_times;
	std::vector <Times> ma_sector_times;

	int m_sector;
	int m_previous_sector;
	double m_distance;

	void update_sector_info (int sector);
	void update_times (int sector, double time_step);

  public:
	Timing_Info ();

	void reset ();
	void update (double time_step, double distance, int sector);

	int get_sector () const { return m_sector; }
	int get_previous_sector () const { return m_previous_sector; }
	double get_distance () const { return m_distance; }

	double get_lap_time () const { return m_lap_times.m_current; }
	double get_previous_lap_time () const { return m_lap_times.m_previous; }
	double get_best_lap_time () const { return m_lap_times.m_best; }
	double get_lap_time_difference () const { return m_lap_times.m_difference; }

	double get_sector_time () const
	{ return ma_sector_times [m_sector].m_current; }
	double get_previous_sector_time () const
	{ return ma_sector_times [m_previous_sector].m_previous; }
	double get_previous_sector_time_difference () const 
	{ return ma_sector_times [m_previous_sector].m_difference; }
	double get_best_sector_time (int sector) const
	{ return (size_t (sector) < ma_sector_times.size ()) 
		? ma_sector_times [sector].m_best : 0.0; }
  };

  struct Car_Information
  {
	Car_Information (Vamos_Body::Car* new_car) 
	  : segment_index (0), car (new_car), dist_from_start(0.0),
	    lap_number(0), race_position(0), end_race(false) {};

	Timing_Info timing;
	size_t segment_index;
	Vamos_Body::Car* car;
	float dist_from_start;
	int lap_number;
	int race_position;
	bool end_race;

	void reset ();
  };

  struct Contact_Info
  {
	Contact_Info (Vamos_Body::Car* car_in, 
				  Mat_Type car_material_type, 
				  Mat_Type track_material_type,
				  double par_speed, double perp_speed)
	  : car (car_in),
		car_material (car_material_type),
		track_material (track_material_type),
		parallel_speed (par_speed),
		perpendicular_speed (perp_speed)
	{};

	Vamos_Body::Car* car;
	Mat_Type car_material;
	Mat_Type track_material;
	double parallel_speed;
	double perpendicular_speed;
  };

  class World
  {
	size_t m_focused_car_index;

//  protected:
public:
	std::vector <Car_Information> m_cars;
	//Vamos_Track::Strip_Track* mp_track;
	TRACK * p_track;
//	Atmosphere* mp_atmosphere;

	double m_gravity;

	std::vector <Contact_Info> m_contact_info;

	void reset ();
	void reset (bool all);
	void reset (Car_Information * cartoreset, unsigned int index);
	void restart ();

	Car_Information* focused_car ();

//  public:
	World (TRACK * track);
	~World ();

	void interact (Vamos_Body::Car* car, size_t segment_index);
	void collide (Vamos_Body::Car* car1, Vamos_Body::Car* car2);
	void gravity (double g);
	void add_car (Vamos_Body::Car* car);
	void set_focused_car (size_t car_index);
	void focus_other_car (int delta);
	void update_race_position ();
  };
}

#endif // not _WORLD_H_
