#ifndef __gxframework_vector2_h__
	#define __gxframework_vector2_h__

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

#include <ostream>

namespace Gx
{
	class Vector2
	{
	public:
		Vector2() { }
		Vector2(float a) : x(a), y(a) { }
		Vector2(float nx, float ny) : x(nx), y(ny) { }

		static unsigned int getCount() { return 2; }

		static const Vector2 Zero;
		static const Vector2 One;
		static const Vector2 UnitX;
		static const Vector2 UnitY;

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

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

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

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

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

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

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

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

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

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

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

			return m;
		}

		float dot(const Vector2& v) const { // dot product
			return x * v.x + y * v.y;
		}

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

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

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

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

		float x, y;
	};

	__declspec(selectany) const Vector2 Vector2::Zero = Vector2(0.0f, 0.0f);
	__declspec(selectany) const Vector2 Vector2::One = Vector2(1.0f, 1.0f);
	__declspec(selectany) const Vector2 Vector2::UnitX = Vector2(1.0f, 0.0f);
	__declspec(selectany) const Vector2 Vector2::UnitY = Vector2(0.0f, 1.0f);

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

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

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

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

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

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

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

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

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

#endif // ~__gxframework_vector2_h__
