//  Tire.h - the tire for a wheel.
//
//  Copyright (C) 2001 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 _TIRE_H_
#define _TIRE_H_

#include <iostream>
using namespace std;

#include <vamos/geometry/Material.h>
#include <vamos/geometry/Three_Vector.h>
#include <vamos/body/Particle.h>
#include <vamos/geometry/Conversions.h>

#include <vector>

#define HAT_ITERATIONS 20
#define HAT_LOAD 0.5

namespace Vamos_Body
{
  //* The frictional properties of the tire.
  class Tire_Friction
  {
	// The parameters of the longitudinal magic equation.
	std::vector <double> m_longitudital_parameters;

	// The parameters of the transverse magic equation.
	std::vector <double> m_transverse_parameters;

	// The parameters of the magic equation for aligning torque.
	std::vector <double> m_aligning_parameters;

	// A parameter that is used to set the volume of the tire squeal
	// sound. 
	double m_slide;

  public:
	//** Constructor
	Tire_Friction (const std::vector <double>& long_parameters,
				   const std::vector <double>& trans_parameters,
				   const std::vector <double>& align_parameters);

	// Return the friction vector calculated from the magic formula.
	// HUB_VELOCITY is the velocity vector of the wheel's reference
	// frame.  PATCH_SPEED is the rearward speed of the contact pacth
	// with respect to the wheel's frame.
	Vamos_Geometry::Three_Vector 
	friction_forces (double normal_force, double friction_factor, 
					 const Vamos_Geometry::Three_Vector& hub_velocity, 
					 double patch_speed, double current_camber,
  					double sigma_hat, double alpha_hat);

	double Pacejka_Fx(double sigma, double Fz, double friction_factor);
	double Pacejka_Fy(double alpha, double Fz, double gamma, double friction_factor);
	double Pacejka_Mz(double sigma, double alpha, double Fz, double gamma, double friction_factor);
  
	// Return the value of the slide parameter.
	double slide () const { return m_slide; }
  };

  //* The tire for a wheel.
  class Tire : public Particle
  {
	// maximum grip sigma
	double sigma_hat[HAT_ITERATIONS];
	
	// maximum grip alpha
	double alpha_hat[HAT_ITERATIONS];
	  
	// calculate maximum grip sigma and alpha
	void FindSigmaHatAlphaHat(double load, double & sh, double & ah);
	  
	// The radius of the tire.
	double m_radius;

	// Linear rolling resistance on a hard surface.
	double m_rolling_resistance_1;

	// Quadratic rolling resistance on a hard surface.
	double m_rolling_resistance_2;

	Tire_Friction m_tire_friction;

	// The rotational inertia of the tire.
	double m_inertia;

	// The rotational speed of the tire in radians per second.
	double m_rotational_speed;
	double m_last_rotational_speed;
	double m_second_last_rotational_speed;

	// How fast the tire is sliding.
	double m_slide;

	//// Input paremeters provided by the wheel.

	// The velocity of the wheel relative to the road.
	Vamos_Geometry::Three_Vector m_velocity;

	// The angular velocity about the normal to the surface.
	double m_normal_ang_velocity;

	// The force perpendicular to the surface.
	double m_normal_force;

	// The current camber, supplied by the wheel.
	double m_camber;

	// The torque on the wheel due to acceleration or braking.
	double m_applied_torque;

	// True if the brake for this wheel is locked.
	bool m_is_locked;

	// The surface that the tire is currently on.
	Vamos_Geometry::Material_Handle m_material;

	// Orient the tire's z-axis with the normal force.
	void orient_frame_with_unit_vector (const Vamos_Geometry::
										Three_Vector& normal_unit_vector);
										
	double m_feedback;
	
	//1.0 equals a pure off-road tire, 0.0 is a pure road-tire
	double m_tread;
	
	//parameters of the surface we're driving on
	double m_friction1;
	double m_friction2;
	double m_rolling_resistance_factor;
	double m_rolling_drag;

  public:
	//** Constructor
	Tire (double radius, double rolling_resistance_1,
		  double rolling_resistance_2, const Tire_Friction& friction, 
		  double inertia, double tread);

	//** Destructor
	//virtual ~Tire ();

	// Called by the wheel to update the tire.
	void input (const Vamos_Geometry::Three_Vector& velocity, 
				double normal_ang_velocity,
				const Vamos_Geometry::Three_Vector& normal_force,
				double camber,
				double torque,
				bool is_locked,
				Vamos_Geometry::Material_Handle material);

	void find_forces ();

	// Advance the tire in time by TIME.
	void propagate (double time);

	void rewind ();

	// Return the radius of the tire
	double radius () const { return m_radius; }

	// Return the rotational speed of the tire.
	double rotational_speed () const { return m_rotational_speed; }
	double last_rotational_speed() const { return m_last_rotational_speed; }
	double second_last_rotational_speed() const { return m_second_last_rotational_speed; }
	
	void set_rotational_speed(double newrs) {m_rotational_speed = newrs; m_last_rotational_speed = newrs;}

	// Return the linear speed of the tread.
	double speed () const { return m_rotational_speed * m_radius; }
	double last_speed () const { return m_last_rotational_speed * m_radius; }

	// Return the linear sliding speed. 
	double slide () const { return m_slide; }

	// Return the position of the contact patch in the wheel's
	// coordinate system.
	Vamos_Geometry::Three_Vector contact_position () const;

	// Set the tire to its initial conditions.
	void reset ();
	
	double GetFeedback() {return m_feedback;}
	
	void SetSurfaceParams(double f1, double f2, double rr, double rd) {m_friction1 = f1; m_friction2 = f2; m_rolling_resistance_factor = rr; m_rolling_drag = rd;}
	
	void LookupSigmaHatAlphaHat(double normalforce, double & sh, double & ah);
	
	double GetSigmaHat() {double sh, ah; LookupSigmaHatAlphaHat(m_normal_force, sh, ah); return sh;}
	double GetFx(double sigma) {return m_tire_friction.Pacejka_Fx(sigma, m_normal_force/1000.0, 1.0);}
	double GetAlphaHat() {double sh, ah; LookupSigmaHatAlphaHat(m_normal_force, sh, ah); return ah;}
	double GetFy(double alpha) {return m_tire_friction.Pacejka_Fy(alpha, m_normal_force/1000.0, Vamos_Geometry::rad_to_deg(m_camber), 1.0);}
	double inertia() {return m_inertia;}

	// Return the tread value
	double tread () { return m_tread; }
  };
}

#endif // !_TIRE_H_
