#pragma once

//namespace BS3
//{

#define SQUARE(x) ((x)*(x))

struct Vector3
{
	// members
	float	x;
	float	y;
	float	z;
		
	// constructors
	inline	Vector3() {}
	inline	Vector3(float x, float y, float z);
	inline	Vector3(const float v[]);
	inline	Vector3(const Vector3& v);

	// operators
	inline bool operator < (const Vector3 &v) const 
	{
		return v.y!=y ? y<v.y : v.x!=x ? x<v.x : z<v.z ;
	}
	inline	Vector3		operator +  () const;
	inline	Vector3		operator -  () const;
	inline	Vector3		operator +  (const Vector3& v) const;
	inline	Vector3		operator -  (const Vector3& v) const;
	inline	Vector3		operator *  (const Vector3& v) const;
	inline	Vector3		operator *  (const float& s) const;
	inline	Vector3		operator /  (const float& s) const;
	inline	Vector3&		operator += (const Vector3& v);
	inline	Vector3&		operator -= (const Vector3& v);
	inline	Vector3&		operator *= (const Vector3& v);
	inline	Vector3&		operator *= (const float& s);
	inline	Vector3&		operator /= (const float& s);
	inline	void			operator  = (const Vector3& v);
	inline	bool			operator == (const Vector3& v) const;
	inline	bool			operator != (const Vector3& v) const;
	inline	const float&	operator []	(int index) const;
	inline	float&			operator []	(int index);
	inline					operator const float* () const;
	inline					operator float* ();

	inline	void			Normalize();

	//------------------------------------------------------------------------------------------------------------------------
	inline	void			SetNull(void){x=y=z=0.0f;};

	//------------------------------------------------------------------------------------------------------------------------
	inline	float			LengthSquared()	const {return SQUARE(x)+SQUARE(y)+SQUARE(z);};
	inline	float			Length()		const {return sqrtf(SQUARE(x)+SQUARE(y)+SQUARE(z));};

	//------------------------------------------------------------------------------------------------------------------------
	inline  float			Mag() const { return (float) sqrtf(SQUARE(x) + SQUARE(y) + SQUARE(z) ); };
	inline  void			SetMagnitude(float mag) { Normalize(); Scale(mag); }
	inline  void			Scale(float s) { x = x*s; y = y*s; z = z*s; }

	//------------------------------------------------------------------------------------------------------------------------
	inline	float			DotProduct(const Vector3& v){return x*v.x+y*v.y+z*v.z;};
	inline	void			SetCrossProduct(const Vector3& a, const Vector3& b){x=(a.y*b.z)-(a.z*b.y);y=(a.z*b.x)-(a.x*b.z);z=(a.x*b.y)-(a.y*b.x);};	
	//------------------------------------------------------------------------------------------------------------------------
	inline	float GetMax(void) const 
	{
		if (x>y)
		{
			if (x>z) return x;
			else return z;
		} else
		{
			if (y>z) return y;
			else return z;
		}
	};

	//------------------------------------------------------------------------------------------------------------------------
	float GetMin(void) const 
	{
		if (x<y)
		{
			if (x<z) return x;
			else return z;
		} else
		{
			if (y<z) return y;
			else return z;
		}
	};

	//------------------------------------------------------------------------------------------------------------------------
	void Set(float ix, float iy, float iz){x=ix; y=iy; z=iz;};

	//------------------------------------------------------------------------------------------------------------------------
};

typedef Vector3 LPoint;

inline	float		LengthSquared(const Vector3& v);
inline	float		Length(const Vector3& v);
inline	float		DotProduct(const Vector3& a, const Vector3& b);
inline	Vector3	CrossProduct(const Vector3& a, const Vector3& b);
inline	Vector3	Normalize(const Vector3& v);
inline	Vector3	Reflect(const Vector3& v, const Vector3& normal);
inline	Vector3	Lerp(const Vector3& a, const Vector3& b, float t);
inline	Vector3	operator * (float s, const Vector3& v);


inline Vector3::Vector3(float vx, float vy, float vz) : x(vx), y(vy), z(vz)
{
}

inline Vector3::Vector3(const float v[]) : x(v[0]), y(v[1]), z(v[2])
{
}

inline Vector3::Vector3(const Vector3& v) : x(v.x), y(v.y), z(v.z)
{
}

inline Vector3 Vector3::operator + () const
{
	return *this;
}

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

inline Vector3 Vector3::operator + (const Vector3& v) const
{
	return Vector3(x+v.x,y+v.y,z+v.z);
}

inline Vector3 Vector3::operator - (const Vector3& v) const
{
	return Vector3(x-v.x,y-v.y,z-v.z);
}

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

inline Vector3 Vector3::operator * (const float& s) const
{
	return Vector3(x*s,y*s,z*s);
}

inline Vector3 Vector3::operator / (const float& s) const
{
	float is=1/s;
	return Vector3(x*is,y*is,z*is);
}

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

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

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

inline Vector3& Vector3::operator *= (const float& s)
{
	x*=s;
	y*=s;
	z*=s;
	return *this;
}

inline Vector3& Vector3::operator /= (const float& s)
{
	float is=1/s;
	x*=is;
	y*=is;
	z*=is;
	return *this;
}

inline void Vector3::operator = (const Vector3& v)
{
	x=v.x;
	y=v.y;
	z=v.z;
}

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

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

inline const float& Vector3::operator [] (int index) const
{
	return reinterpret_cast<const float*>(this)[index];
}

inline float& Vector3::operator [] (int index)
{
	return reinterpret_cast<float*>(this)[index];
}

inline Vector3::operator const float* () const
{
	return reinterpret_cast<const float*>(this);
}

inline Vector3::operator float* ()
{
	return reinterpret_cast<float*>(this);
}

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

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

inline void Vector3::Normalize()
{
	float s=(float)sqrtf(x*x+y*y+z*z);
	if(s) s=1/s;
	x*=s;
	y*=s;
	z*=s;
}

inline float DotProduct(const Vector3& a, const Vector3& b)
{
	return a.x*b.x+a.y*b.y+a.z*b.z;
}

inline Vector3 CrossProduct(const Vector3& a, const Vector3& b)
{
	return Vector3((a.y*b.z)-(a.z*b.y),(a.z*b.x)-(a.x*b.z),(a.x*b.y)-(a.y*b.x));
}

inline Vector3 Normalize(const Vector3& v)
{
	float s=(float)sqrtf(v.x*v.x+v.y*v.y+v.z*v.z);
	if(s) s=1/s;
	return Vector3(v.x*s,v.y*s,v.z*s);
}

inline Vector3 Reflect(const Vector3& v, const Vector3& normal)
{
	float s=DotProduct(v,normal)*2;
	return Vector3(v.x+normal.x*s,v.y+normal.y*s,v.z+normal.z*s);
}

inline Vector3 Lerp(const Vector3& a, const Vector3& b, float t)
{
	return Vector3(a.x+(b.x-a.x)*t,a.y+(b.y-a.y)*t,a.z+(b.z-a.z)*t);
}

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

//};


