#ifndef __gxframework_vector3_h__
	#define __gxframework_vector3_h__

#include "shared.h"
#include "gxmath.h"
#include "vector2.h"

namespace Gx
{
	class Vector3
	{
	public:
		Vector3() { }
		Vector3(float a) : x(a), y(a), z(a) { }
		Vector3(const Vector2& v, float nz) : x(v.x), y(v.y), z(nz) { }
		Vector3(float nx, float ny, float nz) : x(nx), y(ny), z(nz) { }

		static unsigned int getCount() { return 3; }

		static const Vector3 Zero;
		static const Vector3 One;

		static const Vector3 UnitX;
		static const Vector3 UnitY;
		static const Vector3 UnitZ;

		static const Vector3 Left;
		static const Vector3 Right;

		static const Vector3 Up;
		static const Vector3 Down;

		static const Vector3 Backward;
		static const Vector3 Forward;

		bool isZero() const { return x == 0.0f && y == 0.0f && z == 0.0f; }

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

		float operator[](unsigned int i) const { ASSERT(i < getCount()); return (&x)[i]; }
		float& operator[](unsigned int i) { ASSERT(i < getCount()); return (&x)[i]; }

		float operator()(unsigned int i) const { ASSERT(i < getCount()); return (&x)[i]; }
		float& operator()(unsigned int i) { ASSERT(i < getCount()); return (&x)[i]; }

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

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

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

		Vector3& operator*=(float scalar) {
			x *= scalar; y *= scalar; z *= scalar;
			return *this;
		}

		Vector3& operator/=(float scalar) {
			ASSERT(scalar != 0.0f);
			*this *= (1.0f / scalar);
			return *this;
		}

		Vector3& multiply(const Vector3& v) { // component product
			x *= v.x; y *= v.y; z *= v.z;
			return *this;
		}

		Vector3 getNormalized() const {
			Vector3 v(*this);
			v.normalize();
			return v;
		}

		Vector3 cross(const Vector3& v) const {
			return Vector3(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x);
		}

		float normalize () {
			const float m = norm();
			if ( m > 0)
			{
				*this /= m;
			}

			return m;
		}

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

		float magnitudeSquared() const {
			return x * x + y * y + z * z;
		}

		float magnitude() const {
			return std::sqrt( magnitudeSquared() );
		}

		float norm() const {
			return magnitude();
		}

		float length() const {
			return magnitude();
		}

		float x, y, z;
	};

	__declspec(selectany) const Vector3 Vector3::Zero = Vector3(0.0f, 0.0f, 0.0f);
	__declspec(selectany) const Vector3 Vector3::One = Vector3(1.0f, 1.0f, 1.0f);

	__declspec(selectany) const Vector3 Vector3::UnitX = Vector3(1.0f, 0.0f, 0.0f);
	__declspec(selectany) const Vector3 Vector3::UnitY = Vector3(0.0f, 1.0f, 0.0f);
	__declspec(selectany) const Vector3 Vector3::UnitZ = Vector3(0.0f, 0.0f, 1.0f);

	__declspec(selectany) const Vector3 Vector3::Left = Vector3(-1.0f, 0.0f, 0.0f);
	__declspec(selectany) const Vector3 Vector3::Right = Vector3(1.0f, 0.0f, 0.0f); // == UnitX

	__declspec(selectany) const Vector3 Vector3::Down = Vector3(0.0f, -1.0f, 0.0f);
	__declspec(selectany) const Vector3 Vector3::Up = Vector3(0.0f, 1.0f, 0.0f); // == UnitY

	__declspec(selectany) const Vector3 Vector3::Forward = Vector3(0.0f, 0.0f, -1.0f);
	__declspec(selectany) const Vector3 Vector3::Backward = Vector3(0.0f, 0.0f, 1.0f); // == UnitZ

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

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

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

	static Vector3 operator*(float scalar, const Vector3& v)
	{
		return v * scalar;
	}

	static Vector3 operator/(const Vector3& v, float scalar)
	{
		return v * (1.0f / scalar);
	}

	static Vector3 cross(const Vector3& v1, const Vector3& v2)
	{
		return Vector3(v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x);
	}

	static Vector3 operator%(const Vector3& v1, const Vector3& v2)
	{
		return cross(v1, v2);
	}

	static Vector3 multiply(const Vector3& v1, const Vector3& v2)
	{
		return Vector3(v1.x * v2.x, v1.y * v2.y, v1.z * v2.z);
	}

	static float dot(const Vector3& v1, const Vector3& v2)
	{
		return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
	}

	static float operator*(const Vector3& v1, const Vector3& v2)
	{
		return dot(v1, v2);
	}

	static std::ostream& operator<<(std::ostream& stream, const Vector3& v)
	{
		stream << "{" << v.x << " " << v.y << " " << v.z << "}";
		return stream;
	}
}

#endif // ~__gxframework_vector3_h__
