//-----------------------------------------------------------------------------
//
//  $Logfile:: /Quake 2 Engine/Sin/code/game/vector.h                         $
// $Revision:: 17                                                             $
//   $Author:: Markd                                                          $
//     $Date:: 9/25/98 4:42p                                                  $
//
// Copyright (C) 1997 by Ritual Entertainment, Inc.
// All rights reserved.
//
// This source is may not be distributed and/or modified without
// expressly written permission by Ritual Entertainment, Inc.
//
// $Log:: /Quake 2 Engine/Sin/code/game/vector.h                              $
//
// 17    9/25/98 4:42p Markd
// Fixed a local reference return problem in Quat
//
// 16    9/05/98 6:57p Markd
// took out int type casts in toAngles
//
// 15    7/21/98 4:15p Jimdose
// Disabled the - (negate) operator since VC++ generates incorrect code for it
//
// 14    7/08/98 12:43p Markd
// Added quaternion support
//
// 13    7/02/98 7:57p Markd
// Added MaxValue function
//
// 12    7/01/98 8:15p Markd
// Added LerpVector
//
// 11    5/24/98 2:46p Markd
// Made char *'s into const char *'s
//
// 10    5/20/98 6:38p Jimdose
// Added toPitch
//
// 9     5/03/98 4:42p Jimdose
// Simplified class to get rid of bugs when passing into functions
// Replaced vec and vector with x, y, and z
// got rid of pointsTo and Init
// got rid of setX, setY, and setZ.  Direct modifaction of x, y, z is permitted
// got rid of x(), y(), z().  Direct inspection of x,y,z is permitted.
//
// 8     2/06/98 5:50p Jimdose
// Replace getValue and setValue with [] override to allow random access to
// vector parameters.
// Fixed the -(negate) operator
//
// 7     2/03/98 11:01a Jimdose
// Added pitch, yaw, roll, value inspection and manipulation.
// Added Init which essential resets the vector to the startup state, for when
// the vector isn't initialized or is memset.
//
// 5     11/20/97 4:36p Jimdose
// Made multiply work for double * vector as well as vector * double.
//
// 4     10/27/97 4:22p Jimdose
// Added includes for math.h and stdio.h
//
// 3     10/27/97 2:59p Jimdose
// Removed dependency on quakedef.h
//
// 2     9/26/97 6:14p Jimdose
// Added standard Ritual headers
//
// DESCRIPTION:
// C++ implemention of a Vector object.  Handles standard vector operations
// such as addition, subtraction, normalization, scaling, dot product,
// cross product, length, and decomposition into Euler angles.
//

#ifndef __VECTOR_H__
#define __VECTOR_H__

#include "g_local.h"
#include <math.h>
#include <stdio.h>

class EXPORT_FROM_DLL Vector
{
public:
	float x;
	float y;
	float z;

	Vector();
	Vector( vec3_t src );
	Vector( double x, double y, double z );
	Vector( const char *text );

	float       *vec3( void );
	float       pitch( void );
	float       yaw( void );
	float       roll( void );
	float operator[]( int index ) const;
	float&  operator[]( int index );
	void        copyTo( vec3_t vec );
	void        setPitch( double x );
	void        setYaw( double y );
	void        setRoll( double z );
	void        setXYZ( double x, double y, double z );
	void operator=( Vector a );
	void operator=( vec3_t a );
	friend  Vector operator+( Vector a, Vector b );
	Vector& operator+=( Vector a );
	friend  Vector operator-( Vector a, Vector b );
	Vector& operator-=( Vector a );
	friend  Vector operator*( Vector a, double b );
	friend  Vector operator*( double a, Vector b );
	friend  float operator*( Vector a, Vector b );
	Vector& operator*=( double a );
	Vector& operator/=( double a );                 //###
	friend  int operator==( Vector a, Vector b );
	friend  int operator!=( Vector a, Vector b );
	Vector& CrossProduct( Vector a, Vector b );
	float       length( void );
	Vector& normalize( void );
	float   normalize2( void );                 //### returns the length of the original vector
#if 0
//FIXME
// Some kind of compiler bug in VC++ prevents this from working.
// Returns result of ( -x, -y, -x ) for some reason
	Vector& operator-();
#endif
	friend  Vector  fabs( Vector a );
	float       toYaw( void );
	float       toPitch( void );
	Vector  toAngles( void );
	void        AngleVectors( Vector *forward, Vector *right, Vector *up );
	friend  Vector  LerpVector( Vector w1, Vector w2, float t );
	friend  float       MaxValue( Vector a );
};

inline float Vector::pitch( void )
{
	return x;
}
inline float Vector::yaw( void )
{
	return y;
}
inline float Vector::roll( void )
{
	return z;
}
inline void Vector::setPitch( double pitch )
{
	x = ( float )pitch;
}
inline void Vector::setYaw( double yaw )
{
	y = ( float )yaw;
}
inline void Vector::setRoll( double roll )
{
	z = ( float )roll;
}

inline void Vector::copyTo( vec3_t vec )
{
	vec[ 0 ] = x;
	vec[ 1 ] = y;
	vec[ 2 ] = z;
}

inline float Vector::operator[]( int index ) const
{
	assert( ( index >= 0 ) && ( index < 3 ) );
	return ( &x )[ index ];
}

inline float& Vector::operator[]( int index )
{
	assert( ( index >= 0 ) && ( index < 3 ) );
	return ( &x )[ index ];
}

inline void Vector::setXYZ( double x,double y,double z )
{
	this->x = x;
	this->y = y;
	this->z = z;
}

inline Vector::Vector()
{
	x = 0;
	y = 0;
	z = 0;
}

inline Vector::Vector( vec3_t src )
{
	x = src[ 0 ];
	y = src[ 1 ];
	z = src[ 2 ];
}

inline Vector::Vector( double x,double y,double z )
{
	this->x = x;
	this->y = y;
	this->z = z;
}

inline Vector::Vector( const char *text )
{
	if ( text )
	{
		sscanf( text, "%f %f %f", &x, &y, &z );
	}
	else
	{
		x = 0;
		y = 0;
		z = 0;
	}
}

inline float *Vector::vec3( void )
{
	return &x;
}

inline void Vector::operator=( Vector a )
{
	x = a.x;
	y = a.y;
	z = a.z;
}

inline void Vector::operator=( vec3_t a )
{
	x = a[ 0 ];
	y = a[ 1 ];
	z = a[ 2 ];
}

inline Vector operator+( Vector a,Vector b )
{
	return Vector( a.x + b.x, a.y + b.y, a.z + b.z );
}

inline Vector& Vector::operator+=( Vector a )
{
	x += a.x;
	y += a.y;
	z += a.z;

	return *this;
}

inline Vector operator-( Vector a,Vector b )
{
	return Vector( a.x - b.x, a.y - b.y, a.z - b.z );
}

inline Vector& Vector::operator-=( Vector a )
{
	x -= a.x;
	y -= a.y;
	z -= a.z;

	return *this;
}

inline Vector operator*( Vector a,double b )
{
	return Vector( a.x * b, a.y * b, a.z * b );
}

inline Vector operator*( double a,Vector b )
{
	return b * a;
}

inline float operator*( Vector a,Vector b )
{
	return a.x * b.x + a.y * b.y + a.z * b.z;
}

inline Vector& Vector::operator*=( double a )
{
	x *= a;
	y *= a;
	z *= a;

	return *this;
}

//###
inline Vector& Vector::operator/=( double a )
{
	x /= a;
	y /= a;
	z /= a;

	return *this;
}
//###

inline int operator==( Vector a,Vector b )
{
	return ( ( a.x == b.x ) && ( a.y == b.y ) && ( a.z == b.z ) );
}

inline int operator!=( Vector a,Vector b )
{
	return ( ( a.x != b.x ) || ( a.y != b.y ) || ( a.z != b.z ) );
}

inline Vector& Vector::CrossProduct( Vector a,Vector b )
{
	x = a.y * b.z - a.z * b.y;
	y = a.z * b.x - a.x * b.z;
	z = a.x * b.y - a.y * b.x;

	return *this;
}

inline float Vector::length( void )
{
	float length;

	length = x * x + y * y + z * z;
	return ( float )sqrt( length );
}

inline Vector& Vector::normalize( void )
{
	float length, ilength;

	length = this->length();
	if ( length )
	{
		ilength = 1 / length;
		x *= ilength;
		y *= ilength;
		z *= ilength;
	}

	return *this;
}

//###
// normalize function that returns the length of the original vector
inline float Vector::normalize2( void )
{
	float length, ilength;

	length = this->length();
	if ( length )
	{
		ilength = 1 / length;
		x *= ilength;
		y *= ilength;
		z *= ilength;
	}

	return length;
}
//###

#if 0
//FIXME
// Some kind of compiler bug in VC++ prevents this from working.
// Returns result of ( -x, -y, -x ) for some reason
inline Vector& Vector::operator-()
{
	return Vector( -x, -y, -z );
}
#endif

inline Vector fabs( Vector a )
{
	return Vector( fabs( a.x ), fabs( a.y ), fabs( a.z ) );
}

inline float MaxValue( Vector a )
{
	float maxy;
	float maxz;
	float max;

	max = fabs( a.x );
	maxy = fabs( a.y );
	maxz = fabs( a.z );
	if ( maxy > max )
		max = maxy;
	if ( maxz > max )
		max = maxz;
	return max;
}

inline float Vector::toYaw( void )
{
	float yaw;

	if ( ( y == 0 ) && ( x == 0 ) )
	{
		yaw = 0;
	}
	else
	{
		yaw = ( float )( ( int )( atan2( y, x ) * 180 / M_PI ) );
		if ( yaw < 0 )
		{
			yaw += 360;
		}
	}

	return yaw;
}

inline float Vector::toPitch( void )
{
	float forward;
	float pitch;

	if ( ( x == 0 ) && ( y == 0 ) )
	{
		if ( z > 0 )
		{
			pitch = 90;
		}
		else
		{
			pitch = 270;
		}
	}
	else
	{
		forward = ( float )sqrt( x * x + y * y );
		pitch = ( float )( ( int )( atan2( z, forward ) * 180 / M_PI ) );
		if ( pitch < 0 )
		{
			pitch += 360;
		}
	}

	return pitch;
}

inline Vector Vector::toAngles( void )
{
	float forward;
	float yaw, pitch;

	if ( ( x == 0 ) && ( y == 0 ) )
	{
		yaw = 0;
		if ( z > 0 )
		{
			pitch = 90;
		}
		else
		{
			pitch = 270;
		}
	}
	else
	{
		yaw = atan2( y, x ) * 180 / M_PI;
		if ( yaw < 0 )
		{
			yaw += 360;
		}

		forward = ( float )sqrt( x * x + y * y );
		pitch = atan2( z, forward ) * 180 / M_PI;
		if ( pitch < 0 )
		{
			pitch += 360;
		}
	}

	return Vector( pitch, yaw, 0 );
}

inline void Vector::AngleVectors( Vector *forward,Vector *right,Vector *up )
{
	float angle;
	static float sr, sp, sy, cr, cp, cy;    // static to help MS compiler fp bugs

	angle = yaw() * ( M_PI * 2 / 360 );
	sy = sin( angle );
	cy = cos( angle );

	angle = pitch() * ( M_PI * 2 / 360 );
	sp = sin( angle );
	cp = cos( angle );

	angle = roll() * ( M_PI * 2 / 360 );
	sr = sin( angle );
	cr = cos( angle );

	if ( forward )
	{
		forward->setXYZ( cp * cy, cp * sy, -sp );
	}

	if ( right )
	{
		right->setXYZ( -1 * sr * sp * cy + -1 * cr * -sy, -1 * sr * sp * sy + -1 * cr * cy, -1 * sr * cp );
	}

	if ( up )
	{
		up->setXYZ( cr * sp * cy + -sr * -sy, cr * sp * sy + -sr * cy, cr * cp );
	}
}


#define LERP_DELTA 1e-6
inline Vector LerpVector( Vector w1,Vector w2,float t )
{
	double omega, cosom, sinom, scale0, scale1;

	w1.normalize();
	w2.normalize();

	cosom = w1 * w2;
	if ( ( 1.0 - cosom ) > LERP_DELTA )
	{
		omega = acos( cosom );
		sinom = sin( omega );
		scale0 = sin( ( 1.0 - t ) * omega ) / sinom;
		scale1 = sin( t * omega ) / sinom;
	}
	else
	{
		scale0 = 1.0 - t;
		scale1 = t;
	}

	return ( w1 * scale0 + w2 * scale1 );
}

class EXPORT_FROM_DLL Quat
{
public:
	float x;
	float y;
	float z;
	float w;

	Quat();
	Quat( Vector angles );
	Quat( float scrMatrix[ 3 ][ 3 ] );
	Quat( double x, double y, double z, double w );

	float       *vec4( void );
	float operator[]( int index ) const;
	float&  operator[]( int index );
	void        set( double x, double y, double z, double w );
	void operator=( Quat a );
	friend  Quat operator+( Quat a, Quat b );
	Quat&      operator+=( Quat a );
	friend  Quat operator-( Quat a, Quat b );
	Quat&      operator-=( Quat a );
	friend  Quat operator*( Quat a, double b );
	friend  Quat operator*( double a, Quat b );
	Quat&      operator*=( double a );
	friend  int operator==( Quat a, Quat b );
	friend  int operator!=( Quat a, Quat b );
	float       length( void );
	Quat&      normalize( void );
	Quat operator-();
	Vector   toAngles( void );
};

inline float Quat::operator[]( int index ) const
{
	assert( ( index >= 0 ) && ( index < 4 ) );
	return ( &x )[ index ];
}

inline float& Quat::operator[]( int index )
{
	assert( ( index >= 0 ) && ( index < 4 ) );
	return ( &x )[ index ];
}

inline float *Quat::vec4( void )
{
	return &x;
}

inline void Quat::set( double x,double y,double z,double w )
{
	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}

inline Quat::Quat()
{
	x = 0;
	y = 0;
	z = 0;
	w = 0;
}

inline Quat::Quat( Vector Angles )
{
	EulerToQuat( Angles.vec3(), this->vec4() );
}

inline Quat::Quat( float srcMatrix[ 3 ][ 3 ] )
{
	MatToQuat( srcMatrix, this->vec4() );
}

inline Quat::Quat( double x,double y,double z,double w )
{
	this->x = x;
	this->y = y;
	this->z = z;
	this->w = w;
}

inline void Quat::operator=( Quat a )
{
	x = a.x;
	y = a.y;
	z = a.z;
	w = a.w;
}

inline Quat operator+( Quat a,Quat b )
{
	return Quat( a.x + b.x, a.y + b.y, a.z + b.z, a.w + b.w );
}

inline Quat& Quat::operator+=( Quat a )
{
	x += a.x;
	y += a.y;
	z += a.z;
	w += a.w;

	return *this;
}

inline Quat operator-( Quat a,Quat b )
{
	return Quat( a.x - b.x, a.y - b.y, a.z - b.z, a.w - b.w );
}

inline Quat& Quat::operator-=( Quat a )
{
	x -= a.x;
	y -= a.y;
	z -= a.z;
	w -= a.w;

	return *this;
}

inline Quat operator*( Quat a,double b )
{
	return Quat( a.x * b, a.y * b, a.z * b, a.w * b );
}

inline Quat operator*( double a,Quat b )
{
	return b * a;
}

inline Quat& Quat::operator*=( double a )
{
	x *= a;
	y *= a;
	z *= a;
	w *= a;

	return *this;
}

inline int operator==( Quat a,Quat b )
{
	return ( ( a.x == b.x ) && ( a.y == b.y ) && ( a.z == b.z ) && ( a.w == b.w ) );
}

inline int operator!=( Quat a,Quat b )
{
	return ( ( a.x != b.x ) || ( a.y != b.y ) || ( a.z != b.z ) && ( a.w != b.w ) );
}

inline float Quat::length( void )
{
	float length;

	length = x * x + y * y + z * z + w * w;
	return ( float )sqrt( length );
}

inline Quat& Quat::normalize( void )
{
	float length, ilength;

	length = this->length();
	if ( length )
	{
		ilength = 1 / length;
		x *= ilength;
		y *= ilength;
		z *= ilength;
		w *= ilength;
	}

	return *this;
}

inline Quat Quat::operator-()
{
	return Quat( -x, -y, -z, -w );
}

inline Vector Quat::toAngles( void )
{
	float m[ 3 ][ 3 ];
	vec3_t angles;

	QuatToMat( this->vec4(), m );
	MatrixToEulerAngles( m, angles );
	return Vector( angles );
}


#endif /* Vector.h */
