//  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 "../body/Car.h"
#include "../geometry/Circular_Buffer.h"
#include "../geometry/Material.h"
#include "../geometry/Three_Vector.h"
#include "../track/Strip_Track.h"
#include "Atmosphere.h"
#include "../track/Road_Segment.h"

#include <vector>

namespace Vamos_World
{
  class Driver;

  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* car_in, Driver* driver_in);

    Timing_Info timing;
    size_t road_index;
    size_t segment_index;
    Vamos_Body::Car* car;
    Driver* driver;
    Vamos_Geometry::Circular_Buffer <Vamos_Geometry::Three_Vector> position_history;
    double history_time;

	void reset ();
    void propagate (double time_step, 
                    const Vamos_Geometry::Three_Vector& track_position);
  };

  struct Interaction_Info
  {
    typedef Vamos_Geometry::Material::Material_Type Mat_Type;

	Interaction_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
  {
  public:
	World (Vamos_Track::Strip_Track* track, Atmosphere* atmosphere);
	virtual ~World ();

	void interact (Vamos_Body::Car* car, 
                   size_t road_index,
                   size_t segment_index);
	void collide (Car_Information* car1_info, Car_Information* car2_info);
	void gravity (double g);
	virtual void add_car (Vamos_Body::Car* car, Driver* driver);
	virtual void set_focused_car (size_t car_index);
	virtual void set_controlled_car (size_t car_index);
	void focus_other_car (int delta);
    void propagate_cars (double time_step);

  protected:
	std::vector <Car_Information> m_cars;
	Vamos_Track::Strip_Track* mp_track;
	Atmosphere* mp_atmosphere;

	double m_gravity;

	std::vector <Interaction_Info> m_interaction_info;

	void reset ();
	void restart ();

	Car_Information* focused_car ();
    Car_Information* controlled_car ();

  private:
	size_t m_focused_car_index;
	bool m_has_controlled_car;
	size_t m_controlled_car_index;

    double car_reset_elevation (Vamos_Body::Car* car,
                                size_t& segment_index,
                                size_t& road_index);

    double slipstream_air_density (double air_density,
                                   Car_Information& car1,
                                   Car_Information& car2);
  };
}

#endif // not _WORLD_H_
