/*
	Twilight Prophecy 3D/Multimedia SDK
	A multi-platform development system for virtual reality and multimedia.

	Copyright (C) 1997-2001 by Twilight 3D Finland Oy Ltd.
*/
#ifndef PRMATH_VECTOR3_HPP
#define PRMATH_VECTOR3_HPP



namespace prmath
{
	
	struct Vector3
	{
		// members
		union {	float x, r; };
		union {	float y, g; };
		union {	float z, b; };
		
		// constructors
inline	Vector3() {}
inline	Vector3(float x, float y, float z);
inline	Vector3(const float v[]);
inline	Vector3(const Vector3& v);

		// operators
inline	Vector3			operator +  () const;
inline	Vector3			operator -  () const;
inline	Vector3			operator +  (const Vector3& v) const;
inline	Vector3			operator -  (const Vector3& v) const;
inline	Vector3			operator *  (const Vector3& v) const;
inline	Vector3			operator *  (const float& s) const;
inline	Vector3			operator /  (const float& s) const;
inline	Vector3&		operator += (const Vector3& v);
inline	Vector3&		operator -= (const Vector3& v);
inline	Vector3&		operator *= (const Vector3& v);
inline	Vector3&		operator *= (const float& s);
inline	Vector3&		operator /= (const float& s);
inline	void			operator  = (const Vector3& v);
inline	bool			operator == (const Vector3& v) const;
inline	bool			operator != (const Vector3& v) const;
inline	const float&	operator []	(int index) const;
inline	float&			operator []	(int index);
inline					operator const float* () const;
inline					operator float* ();

inline	void			Normalize();
	};


	// functions
	
inline	float		LengthSquared(const Vector3& v);
inline	float		Length(const Vector3& v);
inline	float		DotProduct(const Vector3& a, const Vector3& b);
inline	Vector3		CrossProduct(const Vector3& a, const Vector3& b);
inline	Vector3		Normalize(const Vector3& v);
inline	Vector3		Reflect(const Vector3& v, const Vector3& normal);
inline	Vector3		Lerp(const Vector3& a, const Vector3& b, float t);
inline	Vector3		operator * (float s, const Vector3& v);


	// implementation

inline Vector3::Vector3(float vx, float vy, float vz)
: x(vx), y(vy), z(vz)
{
}

inline Vector3::Vector3(const float v[])
: x(v[0]), y(v[1]), z(v[2])
{
}

inline Vector3::Vector3(const Vector3& v)
: x(v.x), y(v.y), z(v.z)
{
}

inline Vector3 Vector3::operator + () const
{
	return *this;
}

inline Vector3 Vector3::operator - () const
{
	return Vector3(
		-x,
		-y,
		-z );
}

inline Vector3 Vector3::operator + (const Vector3& v) const
{
	return Vector3(
		x + v.x, 
		y + v.y, 
		z + v.z );
}

inline Vector3 Vector3::operator - (const Vector3& v) const
{
	return Vector3(
		x - v.x,
		y - v.y,
		z - v.z );
}

inline Vector3 Vector3::operator * (const Vector3& v) const
{
	return Vector3(
		x * v.x,
		y * v.y,
		z * v.z );
}

inline Vector3 Vector3::operator * (const float& s) const
{
	return Vector3(
		x * s,
		y * s,
		z * s );
}

inline Vector3 Vector3::operator / (const float& s) const
{
	assert( s );
	float is = 1 / s;
	return Vector3(
		x * is,
		y * is,
		z * is );
}

inline Vector3& Vector3::operator += (const Vector3& v)
{
	x += v.x;
	y += v.y;
	z += v.z;
	return *this;
}

inline Vector3& Vector3::operator -= (const Vector3& v)
{
	x -= v.x;
	y -= v.y;
	z -= v.z;
	return *this;
}

inline Vector3& Vector3::operator *= (const Vector3& v)
{
	x *= v.x;
	y *= v.y;
	z *= v.z;
	return *this;
}

inline Vector3& Vector3::operator *= (const float& s)
{
	x *= s;
	y *= s;
	z *= s;
	return *this;
}

inline Vector3& Vector3::operator /= (const float& s)
{
	assert( s );
	float is = 1 / s;
	x *= is;
	y *= is;
	z *= is;
	return *this;
}

inline void Vector3::operator = (const Vector3& v)
{
	x = v.x;
	y = v.y;
	z = v.z;
}

inline bool Vector3::operator == (const Vector3& v) const
{
	return 
		x == v.x &&
		y == v.y &&
		z == v.z;
}

inline bool Vector3::operator != (const Vector3& v) const
{
	return 
		x != v.x ||
		y != v.y ||
		z != v.z;
}

inline const float& Vector3::operator [] (int index) const
{
	assert( index >= 0 && index <= 2 );
	return reinterpret_cast<const float*>(this)[index];
}

inline float& Vector3::operator [] (int index)
{
	assert( index >= 0 && index <= 2 );
	return reinterpret_cast<float*>(this)[index];
}

inline Vector3::operator const float* () const
{
	return reinterpret_cast<const float*>(this);
}

inline Vector3::operator float* ()
{
	return reinterpret_cast<float*>(this);
}

inline float LengthSquared(const Vector3& v)
{
	return v.x*v.x + v.y*v.y + v.z*v.z;
}

inline float Length(const Vector3& v)
{
	return (float)sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
}

inline void Vector3::Normalize()
{
	float s = (float)sqrt(x*x + y*y + z*z);
	if ( s )
		s = 1 / s;

	x *= s;
	y *= s;
	z *= s;
}

inline float DotProduct(const Vector3& a, const Vector3& b)
{
	return 
		a.x * b.x +
		a.y * b.y +
		a.z * b.z;
}

inline Vector3 CrossProduct(const Vector3& a, const Vector3& b)
{
	return Vector3(
		(a.y*b.z) - (a.z*b.y),
		(a.z*b.x) - (a.x*b.z),
		(a.x*b.y) - (a.y*b.x) );
}

inline Vector3 Normalize(const Vector3& v)
{
	float s = (float)sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
	if ( s )
		s = 1 / s;
		
	return Vector3(
		v.x * s,
		v.y * s,
		v.z * s );
}

inline Vector3 Reflect(const Vector3& v, const Vector3& normal)
{
	float s = DotProduct(v,normal) * 2;
	return Vector3(
		v.x + normal.x * s,
		v.y + normal.y * s,
		v.z + normal.z * s );
}

inline Vector3 Lerp(const Vector3& a, const Vector3& b, float t)
{
	return Vector3(
		a.x + (b.x - a.x) * t,
		a.y + (b.y - a.y) * t,
		a.z + (b.z - a.z) * t );
}

inline Vector3 operator * (float s, const Vector3& v)
{
	return Vector3(
		v.x * s,
		v.y * s,
		v.z * s );
}

} // namespace prmath



#endif