#include "ge_math.h" // place before #ifdef

#ifndef __GEVECTOR_H
#define __GEVECTOR_H

struct LVector; 

struct SVector
{
	SReal x, y, z;

	SVector() : x(0.0f), y(0.0f), z(0.0f) { }
	SVector(SReal in) : x(in), y(in), z(in) { }
	SVector(SReal ix, SReal iy, SReal iz)  : x(ix), y(iy), z(iz) { }
	SVector(_DONTCONSTRUCT v){}

	// dot product: this . v2
	SReal	Dot( const SVector &v2 ) const
	{
		return ( x * v2.x + y * v2.y + z * v2.z );
	}

	// cross product: this x v2
	SVector	Cross( const SVector &v2 ) const
	{
		return SVector( y * v2.z - z * v2.y,
										z * v2.x - x * v2.z,
										x * v2.y - y * v2.x );
	}

	// return the length of the vector
	SReal	GetLength( void ) const
	{
		return Sqrt( x*x + y*y + z*z );
	}

	// return the length of the vector
	SReal	GetLengthSquared( void ) const
	{
		return x*x + y*y + z*z;
	}

	// return a normalized vector
	SVector GetNormalized( void ) const
	{
		SReal l = GetLength();

		if ( l == 0.0f )
			return SVector( 0.0f, 0.0f, 0.0f );

		l = 1.0f / l;
		return SVector( x*l, y*l, z*l );
	}

	// normalize this
	void Normalize(void)
	{
		SReal l=Sqrt(x*x+y*y+z*z);
		if (l!=0.0f)
		{
			l = 1.0f/l;
			x*=l;
			y*=l;
			z*=l;
		}
	}


	const SVector & operator += (SReal s)
	{
		x += s;
		y += s;
		z += s;
		return *this;
	}

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

	const SVector & operator -= (SReal s)
	{
		x -= s;
		y -= s;
		z -= s;
		return *this;
	}

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

	// multiply each vector component by a scalar
	const SVector & operator *= (SReal s)
	{
		x *= s;
		y *= s;
		z *= s;

		return *this;
	}
	
	// divide each vector component by a scalar
	const SVector & operator /= (SReal s)
	{
		if (s)
		{
			s = 1.0f/s;
			x *= s;
			y *= s;
			z *= s;
		}
		else 
			x = y = z = 0.0f;

		return *this;
	}

	// multiply vectors componentwise
	const SVector & operator ^= (const SVector &v)
	{
		x*=v.x; y*=v.y; z*=v.z;
		return *this;
	}

	// multiply vector by a matrix
	inline const SVector & operator *= (const SMatrix &m);

	// multiply vector by a matrix
	// This does not include translation from the matrix and is a rotation and scaling transformation
	inline const SVector & operator ^= (const SMatrix &m);

	inline const LVector ToLV() const;
	inline const SVector &ToSV() const { return *this; }

	#ifdef __SINGLEPRECISION
		inline const SVector &ToRV() const { return *this; }
	#else
		inline const LVector ToRV() const;
	#endif



	// dot product: v1 . v2
	friend SReal Dot( const SVector &v1, const SVector &v2 )
	{
		return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
	}

	// dot product: v1 . v2
	friend SReal operator * (const SVector &v1, const SVector &v2)
	{
		return (v1.x*v2.x + v1.y*v2.y + v1.z*v2.z);
	}

	// cross product: v1 x v2
	friend const SVector Cross( const SVector &v1, const SVector &v2 )
	{
		return SVector(v1.y*v2.z - v1.z*v2.y,
		               v1.z*v2.x - v1.x*v2.z,
		               v1.x*v2.y - v1.y*v2.x);
	}

	// cross product: v1 x v2
	friend const SVector operator % (const SVector &v1, const SVector &v2)
	{
		return SVector(v1.y*v2.z - v1.z*v2.y,
		               v1.z*v2.x - v1.x*v2.z,
		               v1.x*v2.y - v1.y*v2.x);
	}

	// GetLength()
	friend SReal Len(const SVector &v)
	{
		return Sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
	}

	// normalized operator (GetNormalized)
	friend const SVector operator ! (const SVector &v)
	{
		SReal l=Sqrt(v.x*v.x+v.y*v.y+v.z*v.z);
		if (l==0.0f) return 0.0f;
		l = 1.0f/l;
		return SVector(v.x*l,v.y*l,v.z*l);
	}

	// multiply each vector component by a scalar
	friend const SVector operator * (SReal s, const SVector &v)
	{
		return SVector(v.x*s, v.y*s, v.z*s);
	}

	// multiply each vector component by a scalar
	friend const SVector operator * (const SVector &v, SReal s)
	{
		return SVector(v.x*s, v.y*s, v.z*s);
	}

	// divide each vector component by a scalar
	friend const SVector operator / (const SVector &v, SReal s)
	{
		if (s)
		{
			s = 1.0f/s;
			return SVector(v.x * s, v.y * s, v.z * s);
		}
		return 0.0f;
	}

	// multiply vectors componentwise
	friend const SVector operator ^ (const SVector &v1, const SVector &v2)
	{
		return SVector(v1.x*v2.x, v1.y*v2.y, v1.z*v2.z);
	}

	friend const SVector operator + (SReal s, const SVector &v)
	{
		return SVector(v.x + s, v.y + s, v.z + s);
	}

	friend const SVector operator + (const SVector &v, SReal s)
	{
		return SVector(v.x + s, v.y + s, v.z + s);
	}

	friend const SVector operator + (const SVector &v1, const SVector &v2)
	{
		return SVector(v1.x + v2.x, v1.y + v2.y, v1.z + v2.z);
	}

	friend const SVector operator - (SReal s, const SVector &v)
	{
		return SVector(s -v.x, s - v.y, s - v.z);
	}

	friend const SVector operator - (const SVector &v, SReal s)
	{
		return SVector(v.x -s, v.y - s, v.z - s);
	}

	friend const SVector operator - (const SVector &v1, const SVector &v2)
	{
		return SVector(v1.x -v2.x, v1.y - v2.y, v1.z - v2.z);
	}

	friend const SVector operator - (const SVector &v)
	{
		return SVector( -v.x, -v.y, -v.z);
	}

	friend Bool operator == (const SVector &v1, const SVector &v2)
	{
		return ((v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z));
	}

	friend Bool operator == (const SVector &v1, SReal r)
	{
		return ((v1.x == r) && (v1.y == r) && (v1.z == r));
	}

	friend Bool operator != (const SVector &v1, SReal r)
	{
		return ((v1.x != r) || (v1.y != r) || (v1.z != r));
	}

	friend Bool operator != (const SVector &v1, const SVector &v2)
	{
		return ((v1.x != v2.x) || (v1.y != v2.y) || (v1.z != v2.z));
	}
};

#endif
