//-------------------------------------------------------------------
// SVector.h - A vector math library.
// By: Aaron Hilton (c) 1/21/98
//-------------------------------------------------------------------
#ifndef __SVECTOR_H_
#define __SVECTOR_H_

#include <math.h>

//-------------------------------------------------------------------
// Pseudo-constantes:
#define PI              3.14159265359f
#define EPSILON  	1e-8
#define ZERO  	 	EPSILON
#define M_1_MAXINT 	4.65661287524579692410E-10	//--->  1/(2^31-1) 1/meu maxint 32bits
#define M_1_255		(1.0 / 255.0)
#define M_1_65535   (1.0 / 65535.0)
#define M_1_65500   (1.0 / 65500.0)

// 3DFX Glide requires triangles to snap its coordinates to this precision.
#define SNAP_BIAS	(float)(1<<19)

//-------------------------------------------
// Forward declaration.

// Actual Vector struct.
class SVector3D {
      public:
	SVector3D () {
	};
	SVector3D (float tx, float ty, float tz) {
		x = tx;
		y = ty;
		z = tz;
		w = 1.f;
	}

	SVector3D (float tx, float ty, float tz, float tw) {
		x = tx;
		y = ty;
		z = tz;
		w = tw;
	}

	union {
		float x;	// Coorinate
		float nx;	// Normal
	};

	union {
		float y;
		float ny;
	};

	union {
		float z;
		float nz;
	};

	float w;		// This coordinate is only used for the final transformation process. Not used by any of the vector methods.

	//-------------------------------------------
	// General utility functions.
	void Print (char *s = 0);	// Prints the vector's coordinates to the "stderr" stream.
	void Rand ();		// Randomly generates a new vector.
	inline float DotProduct (const SVector3D & other) const;	// DotProduct of this vector into the other vector.
	inline void CrossProduct (const SVector3D & a, const SVector3D & b);	// Calculates the cross product of "a" and "b", then storing it in this vector.
	inline float Length ();	// Returns the length of this vector.
	inline float Length2 ();	// Returns the squared length of this vector.

	//-------------------------------------------
	// Results of these opperations are contained in "this" vector by default.
	inline float Normalize ();	// Normalizes this vector, and returns the scalar value used to normalize the vector.
	inline void Zero ();	// Reset variables to a normal zero constant. (w = 1.f)
	inline void Scale (float s);	// Scale this vector by "s".
	inline void Add (const SVector3D & other);	// Add the other vector to this vector.
	inline void Subtract (const SVector3D & other);	// Subtract the other vector from this vector. (result is contained in this vector.)
	inline void Combine (const SVector3D & other, float s = 1.f);	// Combine vectors with a scalar quantity.
	inline void Lerp (const SVector3D & a, const SVector3D & b, float fPercent);	// Linear interpolate
	inline void LerpW (const SVector3D & a, const SVector3D & b, float fPercent);	// Linear interpolate with W
	inline void RotV (const SVector3D & e, const SVector3D & p);	// Rotate this vector to the normal of e.
	inline void GetAngles (float &fPan, float &fPitch);	// Retrieve the angles of this vector.

	// Assignment opperator.
	const SVector3D & operator= (const SVector3D & point) {
		x = point.x;
		y = point.y;
		z = point.z;
		w = point.w;
		return *this;
	}

	// Math opperations.
	// Unary.
	friend SVector3D operator+ (const SVector3D & ptSrc1, const SVector3D & ptSrc2);
	friend SVector3D operator- (const SVector3D & ptSrc1, const SVector3D & ptSrc2);

	// Binary.
	const SVector3D & operator+= (const SVector3D & ptSrc) {
		Add (ptSrc);
		return *this;
	}

	const SVector3D & operator-= (const SVector3D & ptSrc) {
		Subtract (ptSrc);
		return *this;
	}

	//-------------------------------------------
	// Matrix transformation process.
//      void    Transform( const SVector3D &v, const SMatrix &a );
};

//------------------------------------------------------------------------
// Inline implementation.

//-------------------------------------------
// General utility functions.

// DotProduct of this vector into the other vector.
float SVector3D::DotProduct (const SVector3D & other) const
{
	return (x * other.x + y * other.y + z * other.z);
}

// Calculates the cross product of "a" and "b", then storing it in this vector.
void SVector3D::CrossProduct (const SVector3D & a, const SVector3D & b)
{
	x = a.y * b.z - b.y * a.z;
	y = b.x * a.z - a.x * b.z;
	z = a.x * b.y - b.x * a.y;
}

// Returns the length of this vector.
float SVector3D::Length ()
{
	return (float)sqrt (x * x + y * y + z * z);
}

// Returns the squared length of this vector.
float SVector3D::Length2 ()
{
	return (float)(x * x + y * y + z * z);
}

//------------------------------------------------------------------------
// Results of these opperations are contained in "this" vector by default.

//-------------------------------------------
// Normalizes this vector, and returns the scalar value used to normalize the vector.
float SVector3D::Normalize ()
{
	float n, nn;

	nn = x * x + y * y + z * z;
	if (nn < ZERO)
		return 0.0f;

	nn = (float)sqrt (nn);
	n = 1.0f / nn;
	x *= n;
	y *= n;
	z *= n;
	return nn;
}

void SVector3D::Zero ()
{
	x = y = z = 0.f;
	w = 1.f;
}

// Scale this vector by "s".
void SVector3D::Scale (float s)
{
	x *= s;
	y *= s;
	z *= s;
}

// Add the other vector to this vector.
void SVector3D::Add (const SVector3D & other)
{
	x += other.x;
	y += other.y;
	z += other.z;
}

// Subtract the other vector from this vector.
void SVector3D::Subtract (const SVector3D & other)
{
	x -= other.x;
	y -= other.y;
	z -= other.z;
}

// Combine vectors with a scalar quantity.
void SVector3D::Combine (const SVector3D & other, float s)
{
	x += s * other.x;
	y += s * other.y;
	z += s * other.z;
}

// Linear interpolate
void SVector3D::Lerp (const SVector3D & a, const SVector3D & b, float fPercent)
{
	x = a.x * (1.f - fPercent) + b.x * fPercent;
	y = a.y * (1.f - fPercent) + b.y * fPercent;
	z = a.z * (1.f - fPercent) + b.z * fPercent;
}

// Linear interpolate with W
void SVector3D::LerpW (const SVector3D & a, const SVector3D & b, float fPercent)
{
	x = a.x * (1.f - fPercent) + b.x * fPercent;
	y = a.y * (1.f - fPercent) + b.y * fPercent;
	z = a.z * (1.f - fPercent) + b.z * fPercent;
	w = a.w * (1.f - fPercent) + b.w * fPercent;
}

// Rotate a quaternion vector ("p") around a normal ("e").
void SVector3D::RotV (const SVector3D & e, const SVector3D & p)
{
	float m = (float)sqrt (p.y * p.y + p.z * p.z);
	float im = 1.0f / m;

	x = m * e.x + p.x * e.z;
	y = (-p.x * p.y * e.x + p.z * e.y) * im + p.y * e.z;
	z = (-p.x * p.z * e.x - p.y * e.y) * im + p.z * e.z;
}

// Retrieve the angles of this vector.
void SVector3D::GetAngles (float &fPan, float &fPitch)
{
//      fPan = -(float)atan2( x, z );
//      fPitch = 0;//(float)atan2( sin(fPan)*z + cos(fPan)*x, y);
	double dx, dy, dz;

	dx = (double)x;
	dy = (double)y;
	dz = (double)z;
	if (dz > 0.0001f) {
		fPan = (float)-atan (dx / dz);
		dz /= cos ((double)fPan);
		fPitch = (float)atan (dy / dz);
	} else if (dz < -0.0001f) {
		fPan = (float)(PI - atan (dx / dz));
		dz /= cos ((double)fPan - PI);
		fPitch = (float)-atan (dy / dz);
	} else {
		fPan = 0.0;
		fPitch = (float)-PI / 2.f;
	}
}

// Math opperations.
inline SVector3D operator+ (const SVector3D & ptSrc1, const SVector3D & ptSrc2)
{
	SVector3D ptTmp (ptSrc1);

	ptTmp.Add (ptSrc2);
	return ptTmp;
}

inline SVector3D operator- (const SVector3D & ptSrc1, const SVector3D & ptSrc2)
{
	SVector3D ptTmp (ptSrc1);

	ptTmp.Subtract (ptSrc2);
	return ptTmp;
}

//-------------------------------------------------------------------
// Extra tidbits...

//-------------------------------------------
// Calculates the equation of "b" to the power of "e".
int IPower (int b, int e);

inline float operator* (const SVector3D & a, const SVector3D & b)
{
	return a.DotProduct (b);
}

inline SVector3D operator* (const SVector3D & a, float b)
{
	return SVector3D (a.x * b, a.y * b, a.z * b);
}

inline SVector3D & operator*= (SVector3D & a, float b)
{
	a.Scale (b);
	return a;
}

inline SVector3D operator^ (const SVector3D & a, const SVector3D & b)
{
	SVector3D c;

	c.CrossProduct (a, b);
	return c;
}

class SMatrix3D {
      public:
	SVector3D x, y, z;

	SMatrix3D ():x (1, 0, 0), y (0, 1, 0), z (0, 0, 1) {
	} void FromAngle (float ax, float ay, float az) {
		float cx = (float)cos (ax);
		float sx = (float)sin (ax);
		float cy = (float)cos (ay);
		float sy = (float)sin (ay);
		float cz = (float)cos (az);
		float sz = (float)sin (az);

		// Calcul de la matrice de transformation 3D
		x.x = cy * cz;
		x.y = cx * sz * cy + sx * sy;
		x.z = sx * sz * cy - cx * sy;

		y.x = -sz;
		y.y = cx * cz;
		y.z = sx * cz;

		z.x = cz * sy;
		z.y = cx * sz * sy - sx * cy;
		z.z = sx * sz * sy + cx * cy;
	}

	void Apply (const SVector3D & v, SVector3D * out) {
		out->x = v * x;
		out->y = v * y;
		out->z = v * z;
	}

	void RApply (SVector3D const &v, SVector3D * out) {
		out->x = v.x * x.x + v.y * y.x + v.z * z.x;
		out->y = v.x * x.y + v.y * y.y + v.z * z.y;
		out->z = v.x * x.z + v.y * y.z + v.z * z.z;
	}

	void Apply (SVector3D * v) {
		SVector3D i (*v);

		Apply (i, v);
	}

	void RApply (SVector3D * v) {
		SVector3D i (*v);

		RApply (i, v);
	}
};

#endif // __SVECTOR_H_
