//  Suspension.h - the suspension component for a wheel.
//
//  Copyright (C) 2001--2002 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 _SUSPENSION_H_
#define _SUSPENSION_H_

#include "Particle.h"
#include "../geometry/Constants.h"
#include "../geometry/Three_Vector.h"

#include <GL/gl.h>

#include <vector>
#include <string>

namespace Vamos_Body
{
  struct Suspension_Model;

  class Hinge : public Particle
  {
  public:
	Hinge (const Vamos_Geometry::Three_Vector& position);

	void input (const Vamos_Geometry::Three_Vector& torque, 
				const Vamos_Geometry::Three_Vector& radius);
  };

  //* The suspension component for a wheel.
  class Suspension : public Particle
  {
	//** Static Member

	// The axis of rotation for steering.
	const static Vamos_Geometry::Three_Vector STEER_AXIS;

	Hinge* mp_hinge;
	Vamos_Geometry::Three_Vector m_radius;
	Vamos_Geometry::Three_Vector m_tangent;
	double m_radius_magnitude;
	double m_angle;
	double m_initial_z;

	// The spring constant.
	double m_spring_constant;

	// Damping for compression.
	double m_bounce;

	// Damping for decompression.
	double m_rebound;

	// How far the suspension can be displaced before bottoming out.
	double m_travel;

	// The current displacement of the suspension.  A positive
	// displacement means compression.
	double m_displacement;

	// The size of the last time step handled by propagate().
	double m_time_step;

	// How fast the suspension is being compressed.
	double m_compression_velocity;

	// How fast the suspension can be compressed before the damper
	// locks up.
	double m_max_compression_velocity;

	// true if the displacement has exceeded `m_travel', false
	// otherwise.
	bool m_bottomed_out;

	// The spring constant for the anti-roll bar that connects this
	// suspension component with another.
	double m_anti_roll_k;

	// The suspension component that this one is connected to with an
	// anti-roll bar.
	Suspension* m_anti_roll_suspension;

	Vamos_Geometry::Three_Vector m_wheel_force;

	// The deflection of the tire in radians due to steering.
	double m_steer_angle;

	// The static camber angle in radians.  Positive camber is a
	// rotation that moves the tops of the tires away from the center
	// of the car.  So the direction of the rotation depends on the
	// value of m_side.
	double m_camber;

	// The caster angle in radians.
	double m_caster;

	// The toe angle in radians.  Positive is toe-in.
	double m_toe;

	// The side, RIGHT or LEFT, that the suspension is on.  This is
	// used to choose the right direction for camber, caster and toe
	// adjustments.
	Vamos_Geometry::Direction m_side;

	// The orientation of the wheel in the absence of steering and
	// displacement.
	Vamos_Geometry::Three_Matrix m_static_orientation;

	Vamos_Geometry::Three_Vector m_normal;

	std::vector <Suspension_Model*> m_models;

  public:
	Suspension (const Vamos_Geometry::Three_Vector& position,
				const Vamos_Geometry::Three_Vector& center_of_translation,
				Vamos_Geometry::Direction side_of_car, double spring_constant, 
				double bounce, double rebound, double travel,
				double max_compression_velocity);

	~Suspension ();

	//** Geometry-setting methods.  The arguments are in degrees.

	// Set the steering angle.
	void steer (double degree_angle);

	// Set the camber angle.
	void camber (double degree_angle);

	// Set the caster angle.
	void caster (double degree_angle);

	// Set the toe angle.
	void toe (double degree_angle);

	Hinge* hinge () const { return mp_hinge; }

	Vamos_Geometry::Three_Vector force () const { return Particle::force (); }
	Vamos_Geometry::Three_Vector torque () const 
	{ return Particle::torque (); }

	void input (const Vamos_Geometry::Three_Vector& wheel_force,
				const Vamos_Geometry::Three_Vector& normal);

	void torque (double wheel_torque);

	// Calculate the force exerted by the suspension in its current state.
	void find_forces ();

	// Advance this suspension component forward in time by TIME.
	void propagate (double time);

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

	// Specify the suspension component that is attached to this one
	// with an anti-roll bar.  The anti-roll bar will have a spring
	// constant of SPRING_CONSTANT.
	void anti_roll (Suspension* other, double spring_constant);

	// Displace this suspension component by DISTANCE.  A positive
	// DISTANCE means compression.
	void displace (double distance);

	// Return the current displacement.
	double displacement () const { return m_displacement; }

	// Return true if the suspension is displaced as much as it can
	// be, false otherwise.
	bool bottomed_out () const { return m_bottomed_out; }

	// Return the camber angle in radians for a suspension
	// displacement of DISPLACEMENT.
	double camber_function (double displacement) const;

	double current_camber (double normal_y) const;

	// Return this suspension component to equilibrium.
	void reset ();

	void set_model (std::string file_name, 
					double scale, 
					const Vamos_Geometry::Three_Vector& translation, 
					const Vamos_Geometry::Three_Vector& rotation);

	void draw ();
  };
}

#endif // !_SUSPENSION_H_
