//  Wheel.h - a wheel.
//
//  Copyright (C) 2001--2004 Sam Varner
//
//  This file is part of Vamos Automotive Simulator.
//
//  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 _WHEEL_H_
#define _WHEEL_H_

#include <vamos/body/Suspension.h>
#include <vamos/body/Tire.h>
#include <vamos/body/Brake.h>
#include <vamos/body/Contact_Point.h>
#include <vamos/geometry/Constants.h>
#include <vamos/geometry/Material.h>
#include <vamos/geometry/Three_Vector.h>
#include <vamos/geometry/Inertia_Tensor.h>

#include <GL/gl.h>

#include "model.h"

#include <string>

namespace Vamos_Body
{
  //* A wheel.  
  // Handles wheel geometry.  See Tire, Brake and Suspension for
  // related functionality.
  class Wheel : public Contact_Point
  {
  private:

	// The initial position of the wheel.
	Vamos_Geometry::Three_Vector m_original_position;

	double m_tire_offset;
	// The distance from the steering pivot to the effective center
	// of the contact patch.

	double m_roll_height;
	// How far off the road lateral forces are applied to the chassis.

	// The suspension that the wheel is attached to.
	Suspension* mp_suspension;

	// The tire that is attached to the wheel.
	Tire m_tire;

	// The brake for the wheel.
	Brake m_brake;

	// The velocity of the ground relative to the wheel.
	Vamos_Geometry::Three_Vector m_ground_velocity;

	// The current normal vector for the road.
	Vamos_Geometry::Three_Vector m_normal;

	// The angular velocity of the wheel.
 	Vamos_Geometry::Three_Vector m_ang_velocity;
	
	// The surface we're currently on
	Vamos_Geometry::Material_Handle m_material;

	// The torque applied to the tire.
	double m_tire_torque;

	double m_drive_torque;

	double m_braking_torque;

	// True if the wheel responds to steering. 
	bool m_steered;

	// True if the wheel is driven.
	bool m_driven;

	// The side of the car that the wheel is on.
	Vamos_Geometry::Side m_side;
	Vamos_Geometry::FrontBack m_frontback;

	// The ID of the display lists for the wheel.
	/*GLuint m_slow_wheel_list;
	GLuint m_fast_wheel_list;
	GLuint m_stator_list;*/

	// The speed where we start using m_fast_wheel_list.
	double m_transition_speed;

	// The current rotation angle about the axle.
	double m_rotation;
	
	JOEMODEL model;

	// Return the display list for the specified model file.
	/*GLuint make_model (std::string file, double scale,
					   const Vamos_Geometry::Three_Vector& translation,
					   const Vamos_Geometry::Three_Vector& rotation);*/

	// Do the transformation of the wheel model.
	void transform ();
	
	//true if ABS is currently reducing wheel braking (ABS is managed at Car level in Car::propagate)
	bool m_absactive;
	bool m_abslastactive;
	
	//true if TCS is currently applying wheel braking (TCS is managed at Car level in Car::propagate)
	bool m_tcsactive;
	bool m_tcslastactive;

  public:
	//** Constructor
	Wheel (double mass, 
		   Vamos_Geometry::Three_Vector position, 
		   double tire_offset,
		   double roll_height,
		   double restitution,
		   Suspension* suspension, 
		   const Tire& tire, 
		   const Brake& brake,
		   bool steered,
		   bool driven,
		   Vamos_Geometry::Side side,
			Vamos_Geometry::FrontBack frontback);

	// Find and store the forces and torques for the current
	// configuration.
	void find_forces ();

  	Suspension* suspension();
  	Vamos_Geometry::Three_Vector ang_velocity();
  	void set_ang_velocity(Vamos_Geometry::Three_Vector newvel) {m_ang_velocity = newvel;}
  
	// Advance the wheel forward in time by TIME.
	void propagate (double time);

	// Undo the last propagation.
	void rewind ();

 	// Handle collisions.  The return value is how much the wheel was
 	// displaced by the collision.
	double contact (const Vamos_Geometry::Three_Vector& position,
					const Vamos_Geometry::Inertia_Tensor& inertia,
					const Vamos_Geometry::Three_Vector& vecocity, 
					double distance,
					const Vamos_Geometry::Three_Vector& normal,
					const Vamos_Geometry::Three_Vector& ang_velocity,
					Vamos_Geometry::Material_Handle material);

	// Apply the torque TORQUE_IN to the wheel.  This torque results
	// from acceleration or braking.
	void drive_torque (double torque_in);

	double brake (double factor);
	double brake_torque_for_factor (double factor);
	double factor_for_brake_torque (double torque);
	
	double get_handbrake_multiplier() {return m_brake.handbrake();}

	double roll_height() { return m_roll_height; }
	// Set the steering angle.
	void steer (double degree_angle) { mp_suspension->steer (degree_angle); }


	// Return the speed of the contact patch of the tire with respect
	// to the ground.
	double slide () const { return m_tire.slide (); }

	// Return the wheel's rotational speed in radians per second.
	double rotational_speed () const { return m_tire.rotational_speed (); }
	double last_rotational_speed () const { return m_tire.last_rotational_speed (); }
	double second_last_rotational_speed () const { return m_tire.second_last_rotational_speed (); }
	
	void set_rotational_speed(double newrs) {m_tire.set_rotational_speed(newrs);}

	// Return the linear speed of the tire tread.  This is the speed
	// that a speedometer on this wheel would read.
	double speed () const { return m_tire.speed (); }
	double last_speed () const { return m_tire.last_speed (); }
	
	double GetFeedback() {return m_tire.GetFeedback();}

	// Return the position relative to the body where the wheel exerts
	// its force.
	Vamos_Geometry::Three_Vector force_position () const;

	// Return the position of the wheel relative to the body for the
	// purpose of detecting collisions.
	Vamos_Geometry::Three_Vector contact_position () const;
	
	Vamos_Geometry::Three_Vector position () const;

	// Return the slip ratio for the wheel;
	double slip () const;

	// Return the wheel to its initial conditions.
	void reset ();

	bool single_contact () const { return false; }

	bool steered () const { return m_steered; }
	bool driven () const { return m_driven; }

	Vamos_Geometry::Side side () const { return m_side; }
	Vamos_Geometry::FrontBack frontback () const { return m_frontback; }

	void set_models (std::string slow_file, 
					 std::string fast_file, 
					 double transition_speed,
					 std::string stator_file,
					 double stator_offset,
					 double scale, 
					 const Vamos_Geometry::Three_Vector& translation,
					 const Vamos_Geometry::Three_Vector& rotation);
	void draw ();
	
	void SetColParams(double f1, double f2, double rr, double rd) {m_tire.SetSurfaceParams(f1, f2, rr, rd);}
	
	Tire * GetTire() {return &m_tire;}
	
	void SetABSState(bool newabsstate) {m_absactive = newabsstate;}
	bool GetABSState() {return m_absactive;}
	void SetABSLastState(bool newabslstate) {m_abslastactive = newabslstate;}
	bool GetABSLastState() {return m_abslastactive;}
	
	void SetTCSState(bool newtcsstate) {m_tcsactive = newtcsstate;}
	bool GetTCSState() {return m_tcsactive;}
	void SetTCSLastState(bool newtcslstate) {m_tcslastactive = newtcslstate;}
	bool GetTCSLastState() {return m_tcslastactive;}
	
	void FenderTransform();
	
	double m_absdouble;
  };
}

#endif // !_WHEEL_H_
