#pragma once

#include "scalar.h"
#include <ostream>

template <class T> class Vector2DOf
{
public:
	#if defined(USE_SIGNATURE)
		friend int Is_Signature_Bad(const volatile Vector2DOf<T> *p);
	#endif

//		static const Vector2DOf<T>
//			identity;

	T x, y;

	Vector2DOf()	{}
	Vector2DOf(T x, T y) : x(x), y(y)	{}

	friend Logical Small_Enough(const Vector2DOf<T> &v, Scalar e);
	Logical operator!() const	{ return Small_Enough(*this,SMALL); }

	friend Logical Close_Enough(const Vector2DOf<T> &v1, const Vector2DOf<T> &v2, Scalar e);
	Logical operator==(const Vector2DOf<T>& v) const		{ return Close_Enough(*this,v,SMALL); }
	Logical operator!=(const Vector2DOf<T>& v) const		{ return !Close_Enough(*this,v,SMALL); }

	const T& operator[](size_t index) const
	{
		Check(this);
		Warn(index>Y_Axis);
		return (&x)[index];
	}
	T& operator[](size_t index)
	{
		Check(this);
		Warn(index>Y_Axis);
		return (&x)[index];
	}

	//
	//-----------------------------------------------------------------------
	// The following operators all assume that this points to the destination
	// of the operation results
	//-----------------------------------------------------------------------
	//
	Vector2DOf<T>& Negate(const Vector2DOf<T> &v);

	Vector2DOf<T>& Add(const Vector2DOf<T>& v1, const Vector2DOf<T>& v2);
	Vector2DOf<T>& operator+=(const Vector2DOf<T>& v)		{ return Add(*this,v); }

	Vector2DOf<T>& Subtract(const Vector2DOf<T>& v1, const Vector2DOf<T>& v2);
	Vector2DOf<T>& operator-=(const Vector2DOf<T>& v)		{ return Subtract(*this,v); }

	T operator*(const Vector2DOf<T>& v) const
	{
		Check(this);
		Check(&v);
		return x*v.x + y*v.y;
	}

	Vector2DOf<T>& Multiply(const Vector2DOf<T>& v, T Scale);
	Vector2DOf<T>& operator*=(T v)		{ return Multiply(*this,v); }

	Vector2DOf<T>& Multiply(const Vector2DOf<T>& v1, const Vector2DOf<T>& v2);
	Vector2DOf<T>& operator*=(const Vector2DOf<T> &v)		{ return Multiply(*this,v); }

	Vector2DOf<T>& Divide(const Vector2DOf<T>& v, T scale);
	Vector2DOf<T>& operator/=(T v)							{ return Divide(*this,v); }

	Vector2DOf<T>& Divide(const Vector2DOf<T>& v1, const Vector2DOf<T>& v2);
	Vector2DOf<T>& operator/=(const Vector2DOf<T> &v)		{ return Divide(*this,v); }

	T LengthSquared() const
	{
		Check(this);
		return operator*(*this);
	}
	T Length() const
	{
		Check(this);
		return (T)Sqrt(LengthSquared());
	}

	Vector2DOf<T>& Normalize(const Vector2DOf<T> &v);

#if 0
	Vector2DOf<T>& Combine(const Vector2DOf<T>& v1, Scalar t1, const Vector2DOf<T>& v2, Scalar t2);

	Vector2DOf<T>& Lerp(const Vector2DOf<T>& v1, const Vector2DOf<T>& v2, Scalar t)
	{
		return Combine(v1,1.0f-t,v2,t);
	}
#endif

	friend std::ostream& operator<<(std::ostream& stream, const Vector2DOf<T>& v);
	Logical TestInstance() const;
};

#if defined(USE_SIGNATURE)
	template <class T> int Is_Signature_Bad(const volatile Vector2DOf<T> *)
	{
		return False;
	}
#endif

//	template <class T> const Vector2DOf<T>
//		Vector2DOf<T>::identity(0.0f,0.0f);

template <class T> inline Logical
	Small_Enough(const Vector2DOf<T> &v,Scalar e)
{
	Check(&v);
	return Small_Enough(v.x,e) && Small_Enough(v.y,e);
}

//REMOVED: RB 1/15/07
//template <class T> Logical
//	Close_Enough(
//		const Vector2DOf<T> &v1,
//		const Vector2DOf<T> &v2,
//		Scalar e
//	)
//{
//	Check(&v1);
//	Check(&v2);
//	return Close_Enough(v1.x,v2.x,e) && Close_Enough(v1.y,v2.y,e);
//}

inline Logical Close_Enough(const Vector2DOf<int> &v1, const Vector2DOf<int> &v2, Scalar e)
{
	Check(&v1);
	Check(&v2);
	return Close_Enough(v1.x, v2.x,e) && Close_Enough(v1.y, v2.y,e);
}

inline Logical Close_Enough(const Vector2DOf<float> &v1, const Vector2DOf<float> &v2, Scalar e)
{
	Check(&v1);
	Check(&v2);
	return Close_Enough(v1.x, v2.x,e) && Close_Enough(v1.y, v2.y,e);
}

template <class T> inline Vector2DOf<T>&
	Vector2DOf<T>::Negate(const Vector2DOf<T> &v)
{
	Check(this);
	Check(&v);
	x = -v.x;
	y = -v.y;
	return *this;
}

template <class T> inline Vector2DOf<T>&
	Vector2DOf<T>::Add(
		const Vector2DOf<T>& v1,
		const Vector2DOf<T>& v2
	)
{
	Check(this);
	Check(&v1);
	Check(&v2);
	x = v1.x + v2.x;
	y = v1.y + v2.y;
	return *this;
}

template <class T> inline Vector2DOf<T>&
	Vector2DOf<T>::Subtract(
		const Vector2DOf<T>& v1,
		const Vector2DOf<T>& v2
	)
{
	Check(this);
	Check(&v1);
	Check(&v2);
	x = v1.x - v2.x;
	y = v1.y - v2.y;
	return *this;
}

template <class T> inline Vector2DOf<T>&
	Vector2DOf<T>::Multiply(
		const Vector2DOf<T>& v,
		T scale
	)
{
	Check(this);
	Check(&v);
	x = v.x * scale;
	y = v.y * scale;
	return *this;
}

template <class T> inline Vector2DOf<T>&
	Vector2DOf<T>::Multiply(
		const Vector2DOf<T>& v1,
		const Vector2DOf<T>& v2
	)
{
	Check(this);
	Check(&v1);
	Check(&v2);
	x = v1.x * v2.x;
	y = v1.y * v2.y;
	return *this;
}

template <class T> inline Vector2DOf<T>&
	Vector2DOf<T>::Divide(
		const Vector2DOf<T>& v,
		T scale
	)
{
	Check(this);
	Check(&v);
	Verify(!Small_Enough(scale));
	x = v.x / scale;
	y = v.y / scale;
	return *this;
}

template <class T> inline Vector2DOf<T>&
	Vector2DOf<T>::Divide(
		const Vector2DOf<T>& v1,
		const Vector2DOf<T>& v2
	)
{
	Check(this);
	Check(&v1);
	Check(&v2);
	Verify(!Small_Enough(v2.x));
	Verify(!Small_Enough(v2.y));
	x = v1.x / v2.x;
	y = v1.y / v2.y;
	return *this;
}

template <class T> inline Vector2DOf<T>& Vector2DOf<T>::Normalize(const Vector2DOf<T> &v)
{
	Check_Pointer(this);
	Check(&v);

	T len = v.Length();
	Verify(!Small_Enough(len));

	x = v.x/len;
	y = v.y/len;
	return *this;
}

//template <class T> std::ostream& operator<<(std::ostream& stream, const Vector2DOf<T>& v)
//{
//	Check(&v);
//	return stream << '<' << v.x << ',' << v.y << '>';
//}

template <class T> inline Logical Vector2DOf<T>::TestInstance() const
{
	return true;
}

inline std::ostream& operator<<(std::ostream& stream, const Vector2DOf<int>& v)
{
	Check(&v);
	return stream << '<' << v.x << ',' << v.y << '>';
}

#if 0
template <class T> Vector2DOf<T>&
	Combine(
		const Vector2DOf<T>& v1,
		Scalar t1,
		const Vector2DOf<T>& v2,
		Scalar t2
	)
{
	Check(this);
	Check(&v1);
	Check(&v2);

	x = v1.x*t1 + v2.x*t2;
	y = v1.y*t1 + v2.y*t2;
	return *this;
}
#endif