#ifndef __gxframework_vector3_h__
	#define __gxframework_vector3_h__

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

//TODO: isNormalized

namespace Gx
{
	class Vector3
	{
	public:
		Vector3() { }
		Vector3(float a) : x(a), y(a), z(a) { }
		Vector3(float a[]) : x(a[0]), y(a[1]), z(a[2]) { }
		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) { }
		Vector3(const Vector3& v) { x = v.x; y = v.y; z= v.z; }
		Vector3& operator=(const Vector3& v) { x = v.x; y = v.y; z = v.z; return *this; }

		static Vector3 zero() { return Vector3(0, 0, 0); }

		float& operator[](unsigned int index) { ASSERT(index >= 0 && index <= 2); return (&x)[index]; }
		const float& operator[](unsigned int index) const { ASSERT(index >= 0 && index <= 2); return (&x)[index]; }

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

		bool isZero() const { return x == float(0) && y == float(0) && z == float(0); }
		bool isFinite() const { return Gx::isFinite(x) && Gx::isFinite(y) && Gx::isFinite(z); }

		float magnitudeSquared() const { return x * x + y * y + z * z;}
		float magnitude() const { return ::sqrt( this->magnitudeSquared() ); }
		float length() const { return magnitude(); }

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

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

		Vector3 operator+(const Vector3& v) const { return Vector3(x + v.x, y + v.y, z + v.z); }
		Vector3 operator-(const Vector3& v) const { return Vector3(x - v.x, y - v.y, z - v.z); }
		//Vector3 operator*(const Vector3& v) const { return Vector3(x * v.x, y * v.y); } // mnożenie wektorów

		Vector3 operator*(float scalar) const { return Vector3(x * scalar, y * scalar, z * scalar); }
		Vector3 operator/(float scalar) const { scalar = float(1) / scalar; return Vector3(x * scalar, y * scalar, z * scalar); }

		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) { scalar = float(1) / scalar; x *= scalar; y *= scalar; z *= scalar; return *this; }

		Vector3 getNormalized() const { const float m = this->magnitude(); return m > 0 ? (*this / m) : Vector3::zero(); }

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

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

		Vector3 minimum(const Vector3& v) const { return Vector3(std::min(x, v.x), std::min(y, v.y), std::min(z, v.z)); }
		Vector3 maximum(const Vector3& v) const { return Vector3(std::max(x, v.x), std::max(y, v.y), std::max(z, v.z)); }

		float minElement() const { return std::min(z, std::min(x, y)); }
		float maxElement() const { return std::max(z, std::max(x, y)); }

		float x, y, z;
	};

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

#endif // ~__gxframework_vector3_h__
