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

#ifndef __GEVECTOR_H
	#define __GEVECTOR_H

struct Vector64;

struct Vector32
{
	Float32 x, y, z;

	Vector32() : x(0.0f), y(0.0f), z(0.0f) { }
	explicit Vector32(Float32 ix, Float32 iy, Float32 iz)  : x(ix), y(iy), z(iz) { }
	explicit Vector32(_DONTCONSTRUCT v){ }
	explicit Vector32(const Vector64& v);

#ifndef __LEGACY_API
	explicit Vector32(Float32 in) : x(in), y(in), z(in) { }
#else
	Vector32(Float32 in) : x(in), y(in), z(in) { }
	Float32								 Dot(const Vector32& v2) const { return x * v2.x + y * v2.y + z * v2.z; }
	Vector32							 Cross(const Vector32& v2) const { return Vector32(y * v2.z - z * v2.y, z * v2.x - x * v2.z, x * v2.y - y * v2.x); }
	inline const Vector64 ToLV() const;
	inline const Vector32& ToSV() const { return *this; }
	inline const Vector64 ToRV() const;
	friend Float32 operator * (const Vector32& v1, const Vector32& v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; }
	friend const Vector32 operator % (const Vector32& v1, const Vector32& v2) { return Vector32(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x); }
	inline const Vector32& operator *= (const Matrix32& m);
	inline const Vector32& operator ^= (const Matrix32& m);
	const Vector32&				 operator ^= (const Vector32& v) { x *= v.x; y *= v.y; z *= v.z; return *this; }
	friend const Vector32 operator ^ (const Vector32& v1, const Vector32& v2) { return Vector32(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z); }
#endif

	/// Checks if each component is zero.
	Bool IsZero() const
	{
		return (x == 0.0f) && (y == 0.0f) && (z == 0.0f);
	}

	/// Checks if one of the components is not zero.
	Bool IsNotZero() const
	{
		return (x != 0.0f) || (y != 0.0f) || (z != 0.0f);
	}

	/// Sets all components to zero.
	void SetZero()
	{
		x = 0.0f;
		y = 0.0f;
		z = 0.0f;
	}

	/// Tests component-wise if the difference is no bigger than 'epsilon'
	Bool IsEqual(const Vector32& v2, const Float32& epsilon = 0.01f) const
	{
		return Abs(x - v2.x) < epsilon && Abs(y - v2.y) < epsilon && Abs(z - v2.z) < epsilon;
	}

	/// Calculates the average value of 'x', 'y' and 'z'
	Float32 GetAverage() const
	{
		return (x + y + z) * Float32(1.0 / 3.0);
	}

	/// Calculates the sum of 'x', 'y' and 'z'
	Float32 GetSum() const
	{
		return x + y + z;
	}

	/// Returns the minimum of 'x', 'y' and 'z'
	Float32 GetMin() const
	{
		if (x < y)
		{
			return (z < x) ? z : x;
		}
		else
		{
			return (z < y) ? z : y;
		}
	}

	/// Returns the maximum of 'x', 'y' and 'z'
	Float32 GetMax() const
	{
		if (x > y)
		{
			return (z > x) ? z : x;
		}
		else
		{
			return (z > y) ? z : y;
		}
	}

	/// Returns a vector that is clamped to the range [0.0 .. 1.0]
	Vector32 Clamp01() const
	{
		return Vector32(::Clamp01(x), ::Clamp01(y), ::Clamp01(z));
	}

	/// Calculates angle (in radians) between v1 and v2
	friend Float32 GetAngle(const Vector32& v1, const Vector32& v2)
	{
		Float32 il = Inverse(v1.GetSquaredLength() * v2.GetSquaredLength());
		return ACos((v1.x * v2.x + v1.y * v2.y + v1.z * v2.z) * Sqrt(il));
	}

	/// Calculates dot product of v1 and v2
	friend Float32 Dot(const Vector32& v1, const Vector32& v2)
	{
		return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
	}

	/// Calculates cross product of v1 and v2
	friend Vector32	Cross(const Vector32& v1, const Vector32& v2)
	{
		return Vector32(v1.y * v2.z - v1.z * v2.y,
						 v1.z * v2.x - v1.x * v2.z,
						 v1.x * v2.y - v1.y * v2.x);
	}

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

	/// Returns the squared length of the vector
	Float32	GetSquaredLength(void) const
	{
		return x * x + y * y + z * z;
	}

	/// Returns a normalized vector, so that GetLength(vector)==1.0
	Vector32 GetNormalized(void) const
	{
		Float32 l = GetLength();

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

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

	/// Normalizes this vector, so that GetLength()==1.0
	void Normalize(void)
	{
		Float32 l = Sqrt(x * x + y * y + z * z);
		if (l != 0.0f)
		{
			l	 = 1.0f / l;
			x *= l;
			y *= l;
			z *= l;
		}
	}


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

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

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

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

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

		return *this;
	}

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

		return *this;
	}

#ifndef __LEGACY_API
	// multiply vectors componentwise
	const Vector32& operator *= (const Vector32& v)
	{
		x *= v.x; y *= v.y; z *= v.z;
		return *this;
	}
#endif

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

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

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

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

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

#ifndef __LEGACY_API
	// multiply vectors componentwise
	friend const Vector32 operator * (const Vector32& v1, const Vector32& v2)
	{
		return Vector32(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);
	}
#endif

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

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

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

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

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

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

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

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

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

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

	friend Bool operator != (const Vector32& v1, const Vector32& 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
	Float32& operator [](Int 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 Float32& operator [](Int l) const
	{
		return (&x)[l];
	}
};

#endif
