#ifndef __gxframework_vector4_h__
	#define __gxframework_vector4_h__

#include "shared.h"
#include "gxmath.h"
#include "vector3.h"

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

		static unsigned int getCount() { return 4; }

		static const Vector4 Zero;
		static const Vector4 One;

		static const Vector4 UnitX;
		static const Vector4 UnitY;
		static const Vector4 UnitZ;
		static const Vector4 UnitW;

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

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

		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]; }

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

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

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

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

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

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

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

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


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

			return m;
		}

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

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

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

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

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

		float x, y, z, w;
	};

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

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

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

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

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

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

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

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

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

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

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

#endif // ~__gxframework_Vector4_h__
