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

#ifndef __GELVECTOR_H
#define __GELVECTOR_H

struct LVector
{
	LReal x, y, z;

	LVector() : x(0.0), y(0.0), z(0.0) { }
	LVector(LReal in) : x(in), y(in), z(in) { }
	LVector(LReal ix, LReal iy, LReal iz)  : x(ix), y(iy), z(iz) { }
	LVector(_DONTCONSTRUCT v) {}

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

	LReal	Dot( const SVector &v ) const
	{
		return x * LReal( v.x ) + y * LReal( v.y ) + z * LReal( v.z );
	}

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

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

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

	// return a normalized vector
	LVector GetNormalized( void ) const
	{
		LReal l = GetLength();

		if ( l == 0.0 )
			return LVector( 0.0, 0.0, 0.0 );

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

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


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

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

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

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

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

		return *this;
	}

	// divide each vector component by a scalar
	const LVector & operator /= (LReal s)
	{
		if (s)
		{
			s = 1.0/s;
			x *= s;
			y *= s;
			z *= s;
		}
		else 
			x = y = z = 0.0;

		return *this;
	}

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

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

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

	inline const SVector ToSV() const {return SVector((SReal)x,(SReal)y,(SReal)z);}
	inline const LVector &ToLV() const { return *this; }

	#ifdef __SINGLEPRECISION
		inline const SVector ToRV() const { return SVector((SReal)x,(SReal)y,(SReal)z); }
	#else
		inline const LVector &ToRV() const { return *this; }
	#endif


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

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

	// cross product: v1 x v2
	friend const LVector Cross( const LVector &v1, const LVector &v2 )
	{
		return LVector(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 LVector operator % (const LVector &v1, const LVector &v2)
	{
		return LVector(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 LReal Len(const LVector &v)
	{
		return Sqrt(v.x*v.x + v.y*v.y + v.z*v.z);
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	/// access vector component: index 0 is 'x', index 1 is 'y', index 2 is 'z'. All other values must not be used and will crash
	LReal& operator [](LONG l)
	{
		return (&x)[l];
	}

	/// access vector component: index 0 is 'x', index 1 is 'y', index 2 is 'z'.  All other values must not be used and will crash
	const LReal& operator [](LONG l) const
	{
		return (&x)[l];
	}
};

inline const LVector SVector::ToLV() const { return LVector((LReal)x,(LReal)y,(LReal)z);}

#ifndef __SINGLEPRECISION
	inline const LVector SVector::ToRV() const { return LVector((LReal)x,(LReal)y,(LReal)z);}
#endif

#endif
