//Copyright (C) 2007 SiegeLord
//See ReadMe.txt and License.txt for copying information

#pragma once

#include "engine.h"
#include <math.h>

//returns 0 to 1
inline float Rand()
{
	return (rand() % 10000) / 10000.0f;
}

//returns min to max
inline float Rand(float _min, float _max)
{
	return (_max - _min) * Rand() + _min;
}

inline bool Feq(float a, float b)
{
	const float epsilon = 0.0001;
	return fabs(a - b) < epsilon;
}

//changes a variable so that it reaches target with a certain increment
template <typename T>
inline void ChangeVar(T& var, T target, T increment)
{
	T diff = var - target;
	if(fabs(diff) < increment)
	{
		var = target;
		return;
	}
	var += var > target ? -increment : increment;
}

//snaps a variable to a certain range
template <typename T>
inline void SnapVar(T& var, T _min, T _max)
{
	var = var < _min ? var = _min : (var > _max ? var = _max : var);
}

//snaps a variable to a certain range, returns true if changed
template <typename T>
inline bool SnapVarTest(T& var, T _min, T _max)
{
	if(var < _min)
	{
		var = _min;
		return true;
	}
	else if(var > _max)
	{
		var = _max;
		return true;
	}
	else
	{
		return false;
	}
	return false;
}

struct vector2d
{
	union
	{
		float vals[2];
		struct {float x; float y;};
	};
	//float x;
	//float y;
	vector2d()
	{
		x = y = 0;
	}
	vector2d(float X, float Y)
	{
		Set(X, Y);
	}
	//has restrict stuff
	vector2d(const vector2d& restrict pt1, const vector2d& restrict pt2)
	{
		Set(pt1, pt2);
	}
	//undefined if vectors are the same, or one of the vectors is this one
	inline void Set(const vector2d& restrict pt1, const vector2d& restrict pt2)
	{
		Set(pt2.x - pt1.x, pt2.y - pt1.y);
	}
	inline void Set(float X, float Y)
	{
		x = X;
		y = Y;
	}
	inline void Rotate(float cosine, float sine)
	{
		float t = x * cosine - y * sine;
		y  = x * sine + y * cosine;
		x  = t;
	}
	inline void Rotate(float theta)
	{
		Rotate(cosf(theta), sinf(theta));
	}
	//rotates this vector by pi/2
	inline void MakeNormal()
	{
		float t = y;
		y = x;
		x = -t;
	}
	inline float DotProduct(const vector2d& other) const
	{
		return x * other.x + y * other.y;
	}
	inline float CrossProduct(const vector2d& other) const
	{
		return x * other.y - y * other.x;
	}
	inline float GetLength() const
	{
		return hypotf(x, y);
	}
	inline float GetLengthSQ() const
	{
		return x * x + y * y;
	}
	inline vector2d operator+ (const vector2d &other) const
	{
		return vector2d(x + other.x, y + other.y);
	}
	inline vector2d operator- (const vector2d &other) const
	{
		return vector2d(x - other.x, y - other.y);
	}
	inline vector2d operator* (float n) const
	{
		return vector2d(x * n, y * n);
	}
	inline vector2d operator/ (float n) const
	{
		return vector2d(x / n, y / n);
	}
	inline void operator+= (const vector2d &other)
	{
		x += other.x;
		y += other.y;
	}
	inline void operator-= (const vector2d &other)
	{
		x -= other.x;
		y -= other.y;
	}
	inline void operator*= (float n)
	{
		x *= n;
		y *= n;
	}
	inline void operator/= (float n)
	{
		x /= n;
		y /= n;
	}
	inline bool operator== (const vector2d &other) const
	{
		return Feq(x, other.x) && Feq(y, other.y);
	}

	inline bool operator!= (const vector2d &other) const
	{
		return !Feq(x, other.x) || !Feq(y, other.y);
	}

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

	inline void Normalize()
	{
		*this /= GetLength();
	}
};

const vector2d g_vecZero = vector2d(0, 0);
const vector2d g_vecI = vector2d(1, 0);
const vector2d g_vecJ = vector2d(0, 1);




//inline vector2d AddVectors(const vector2d* vec, const vector2d* other)
//{
//	vector2d ret = *vec;
//	//VecAssign(&ret, vec);
//	VecAdd(&ret, vec);
//	return ret;
//}
//
//inline vector2d SubtractVectors(const vector2d* vec, const vector2d* other)
//{
//	vector2d ret = *vec;
//	//VecAssign(&ret, vec);
//	VecSubtract(&ret, vec);
//	return ret;
//}

///// Transformations

struct transform2d
{
	float vals[2][3];
	transform2d()
	{
		MakeIdentity();
	}
	//row column notation
	transform2d(float _00, float _01, float _02, float _10, float _11, float _12)
	{
		Set(_00, _01, _02, _10, _11, _12);
	}
	inline void Set(float _00, float _01, float _02, float _10, float _11, float _12)
	{
		vals[0][0] = _00;
		vals[0][1] = _01;
		vals[0][2] = _02;
		vals[1][0] = _10;
		vals[1][1] = _11;
		vals[1][2] = _12;
	}
	inline void MakeIdentity();
	inline void SetTranslation(float x, float y)
	{
		vals[0][2] = x;
		vals[1][2] = y;
	}
	//note that this erases any scale set on this matrix
	inline void SetRotation(float cosine, float sine)
	{
		vals[0][0] = cosine;
		vals[0][1] = -sine;
		vals[1][0] = sine;
		vals[1][1] = cosine;
	}
	inline void SetRotation(float theta)
	{
		SetRotation(cosf(theta), sinf(theta));
	}
	//note that this erases any rotation set on this matrix
	inline void SetScale(float x, float y)
	{
		vals[0][0] = x;
		vals[0][1] = 0;
		vals[1][0] = 0;
		vals[1][1] = y;
	}
	inline void TransformVector(vector2d* vec) restrict const
	{
		float t1;
		t1 =	 vec->x * vals[0][0] + vec->y * vals[0][1] + vals[0][2];
		vec->y = vec->x * vals[1][0] + vec->y * vals[1][1] + vals[1][2];
		vec->x = t1;
	}
	inline void RotateVector(vector2d* vec) restrict const
	{
		float t1;
		t1 =	 vec->x * vals[0][0] + vec->y * vals[0][1];
		vec->y = vec->x * vals[1][0] + vec->y * vals[1][1];
		vec->x = t1;
	}
	inline void TranslateVector(vector2d* vec) restrict const
	{
		vec->x = vals[0][2];
		vec->y = vals[1][2];
	}
	//undefined if called on self
	inline void TransformTransform(transform2d * restrict other) restrict const
	{
		float t1, t2, t3;
		t1 = vals[0][0] * other->vals[0][0] + vals[0][1] * other->vals[1][0];
		t2 = vals[0][0] * other->vals[0][1] + vals[0][1] * other->vals[1][1];
		t3 = vals[0][0] * other->vals[0][2] + vals[0][1] * other->vals[1][2] + vals[0][2];

		other->vals[1][0] = vals[1][0] * other->vals[0][0] + vals[1][1] * other->vals[1][0];
		other->vals[1][1] = vals[1][0] * other->vals[0][1] + vals[1][1] * other->vals[1][1];
		other->vals[1][2] = vals[1][0] * other->vals[0][2] + vals[1][1] * other->vals[1][2] + vals[1][2];

		other->vals[0][0] = t1;
		other->vals[0][1] = t2;
		other->vals[0][2] = t3;
	}
};

const transform2d g_trIdentity = transform2d(1, 0, 0,
								   			 0, 1, 0);

inline void transform2d::MakeIdentity()
{
	*this = g_trIdentity;
}

struct SRect
{
	SRect()
	{

	}

	SRect(float min_x, float min_y, float max_x, float max_y)
	{
		Set(min_x, min_y, max_x, max_y);
	}

	inline void Set(float min_x, float min_y, float max_x, float max_y)
	{
		Min.Set(min_x, min_y);
		Max.Set(max_x, max_y);
	}

	vector2d Max;
	vector2d Min;
	inline void Fix()
	{
		float t;
		if(Max.x < Min.x)
		{
			t = Max.x;
			Max.x = Min.x;
			Min.x = t;
		}
		if(Max.y < Min.y)
		{
			t = Max.y;
			Max.y = Min.y;
			Min.y = t;
		}
	}
	//sees if the two rectangles intersect
	inline bool IntersectTest(const SRect& other) const
	{
		return Max.x > other.Min.x && Max.y > other.Min.y && Min.x < other.Max.x && Min.y < other.Max.y;
	}
	//sees if the passed rectangle is entirely inside this rectangle
	inline bool InsideTest(const SRect& other) const
	{
		return Min.x < other.Min.x && Min.y < other.Min.y && Max.x > other.Max.x && Max.y > other.Max.y;
	}
	//sees if the passed point is inside the rectangle
	inline bool PointTest(const vector2d& vec) const
	{
		return vec.x > Min.x && vec.x < Max.x && vec.y > Min.y && vec.y < Max.y;
	}
};

//forces the angle to be between 0 and 2 PI
inline float FixAngle(float angle)
{
	if(angle < 0 || angle > 2 * AL_PI)
	{
		return angle - floorf(angle / (2 * AL_PI)) * 2 * AL_PI;
	}
	return angle;
}

//subtracts angle2 from angle1 and returns the real distance between them
inline float SubtractAngles(float angle1, float angle2)
{
	//return //-(FixAngle(angle1 - angle2) - AL_PI);
	return FixAngle(angle1 + AL_PI - angle2) - AL_PI;
}
//inline transform2d TransformTransforms(const transform2d* trans, const transform2d* other)
//{
//	transform2d ret = *other;
//	TransTransformTransform(trans, &ret);
//	return ret;
//}

//Again, not mine... I picked this one up from somewhere... at least it has the credits
/*
Fast Circle-Rectangle Intersection Checking
by Clifford A. Shaffer
from "Graphics Gems", Academic Press, 1990
*/

//bool CircleTest(R, C, Rad)
//
///* Return TRUE iff rectangle R intersects circle with centerpoint C and
//   radius Rad. */
// Box2 *R;
// Point2 *C;
// double Rad;
//{
// double Rad2;
//
// Rad2 = Rad * Rad;
// /* Translate coordinates, placing C at the origin. */
// R->max.x -= C->x;  R->max.y -= C->y;
// R->min.x -= C->x;  R->min.y -= C->y;
//
// if (R->max.x < 0) 			/* R to left of circle center */
//   	if (R->max.y < 0) 		/* R in lower left corner */
//     		return ((R->max.x * R->max.x + R->max.y * R->max.y) < Rad2);
//   	else if (R->min.y > 0) 	/* R in upper left corner */
//     		return ((R->max.x * R->max.x + R->min.y * R->min.y) < Rad2);
//   	else 					/* R due West of circle */
//     		return(ABS(R->max.x) < Rad);
// 	else if (R->min.x > 0)  	/* R to right of circle center */
//   		if (R->max.y < 0) 	/* R in lower right corner */
//     			return ((R->min.x * R->min.x) < Rad2);
//   	else if (R->min.y > 0)  	/* R in upper right corner */
//     		return ((R->min.x * R->min.x + R->min.y + R->min.y) < Rad2);
//   	else 				/* R due East of circle */
//     		return (R->min.x < Rad);
// 	else				/* R on circle vertical centerline */
//   		if (R->max.y < 0) 	/* R due South of circle */
//     		return (ABS(R->max.y) < Rad);
//   	else if (R->min.y > 0)  	/* R due North of circle */
//     		return (R->min.y < Rad);
//   	else 				/* R contains circle centerpoint */
//     		return(TRUE);
//}
