/*
/ TYPES.H
/ *******
/ Core type definion class.
/
/ Will Beddow 2010 - http://343gs.com/brainstem
/
/ This software is currently under development
/ and is subject to change and improvement. Some
/ aspects of the system are incomplete and/or 
/ unstable due to ongoing implementation.
/
*/

#ifndef __TYPES_H__
#define __TYPES_H__

#include <list>
#include <atlbase.h>
#include <iostream>
#include "Utils.h"

namespace Brainstem
{
	#define AUTO_OBJECT_SIZE unsigned long GetSize() { return sizeof(*this); }
	#define AUTO_OBJECT_NAME virtual const char* GetName(){ return "Unknown Object"; }

	#ifndef SAFE_DELETE
	#define SAFE_DELETE(p) { if (p) { delete (p); (p) = NULL; } }
	#endif    

	#ifndef SAFE_DELETE_ARRAY
	#define SAFE_DELETE_ARRAY(p) { if (p) { delete[] (p); (p) = NULL; } }
	#endif    

	#ifndef SAFE_RELEASE
	#define SAFE_RELEASE(p) { if (p) { (p)->Release(); (p) = NULL; } }
	#endif

	#ifndef WRITE
	#define WRITE(str) { std::cout << str; }
	#endif

	#ifndef WRITELN
	#define WRITELN(str) { WRITE(str << std::endl); }
	#endif

	#ifndef WRITE_WARN
	#define WRITE_WARN(p, str) { WRITELN("<BSTM_WARN | " << p->GetName() << "> " << str); }
	#endif

	#ifndef WRITE_ERROR
	#define WRITE_ERROR(p, str) { WRITELN("<BSTM_ERROR | " << p->GetName() << "> " << str); }
	#endif

	#ifndef WRITE_RELEASE
	#define WRITE_RELEASE(p) { WRITELN("<BSTM_REL | " << p->GetName() << "> "); }
	#endif

	#ifndef WRITE_MM
	#define WRITE_MM(p, str) { WRITELN("<BSTM_MM | " << p << "> " << str); }
	#endif

	class IObject
	{
		private:
			static IObject* LiveObjects;
			static IObject* DeadObjects;
			IObject* Next;
			IObject* Prev;
			long References;
			bool StackAllocated;
			static std::list<IObject*> HeapObjects;
			static unsigned long HeapUsage;
			unsigned long HeapAllocSize;

		protected:
			IObject();
			virtual ~IObject();

		public:

			void AddRef();
			void RemoveRef();
			static void CollectGarbage();
			static void CollectRemaining();
			virtual unsigned long GetSize() = 0;
			virtual const char* GetName() = 0;

			void *operator new(size_t size);
			void operator delete(void* obj);
	};

	template<class T>
	class IPtr
	{
	protected:
		T* obj;
	public:
		IPtr(){obj=0;}
		IPtr(T *o){obj=0; *this=o;}
		IPtr(const IPtr<T> &p){obj=0; *this=p;}

		~IPtr(){if(obj)obj->RemoveRef();}

		inline IPtr<T> &operator =(const IPtr<T> &p)
		{
			if(obj)obj->RemoveRef();
			obj=p.obj;
			if(obj)obj->AddRef();
			return *this;
		}
		inline IPtr<T> &operator =(T* o)
		{
			if(obj)obj->RemoveRef();
			obj=o;
			if(obj)obj->AddRef();
			return *this;
		}

		inline T& operator *() const { return *obj; }
		inline T* operator ->() const { return obj; }
		inline operator T*() const { return obj; }
		inline bool operator !(){ return !(obj); }
		inline bool isValid() const { return (obj != 0); }
		inline bool operator ==(const IPtr<T> &p) const { return (obj == p.obj); }
		inline bool operator ==(const T* o) const { return (obj == o); }
	};

	class ITask : public IObject
	{
	public:
		ITask() { Kill = false; }

		virtual bool Init() = 0;
		virtual void Update() = 0;
		virtual void Release() = 0;

		bool Kill;

		AUTO_OBJECT_SIZE;
	};

	template <class T>
	class Vector2
	{
	public:
		Vector2() : X(0), Y(0) {}
		Vector2(const T& x, const T& y)
			: X(x), Y(y) {}

		template <class U>
		explicit Vector2(const Vector2<U>& other) :
			X((T)other.X), Y((T)other.Y) { }

		template <class U>
		Vector2<T>& operator=(const Vector2<U>& other)
		{ 
			X = (T) other.X;
			Y = (T) other.Y;
			return *this;
		}

		T X;
		T Y;

		bool operator==(const Vector2<T>& other) const
		{
			return (X == other.X && Y == other.Y);
		}

		bool operator!=(const Vector2<T>& other) const
		{
			return ! (*this == other);
		}

		Vector2<T>& set(const T& x, const T& y)
		{
			X = x;
			Y = y;
			return *this;
		}

		Vector2<T>& operator/=(const T& scale)
		{
			X /= scale;
			Y /= scale;
			return *this;
		}

		Vector2<T> operator/(const T& scale) const
		{
			return Vector2<T>(X/scale, Y/scale);
		}

		Vector2<T>& operator*=(const T& scale)
		{
			X *= scale;
			Y *= scale;
			return *this;
		}

		Vector2<T> operator*(const T& scale) const
		{
			return Vector2<T>(X*scale, Y*scale);
		}

		Vector2<T>& operator+=(const Vector2<T>& other)
		{
			X += other.X;
			Y += other.Y;
			return *this;
		}

		Vector2<T>& operator-=(const Vector2<T>& other)
		{
			X -= other.X;
			Y -= other.Y;
			return *this;
		}

		Vector2<T> operator+(const Vector2<T>& other) const
		{
			return Vector2<T>(X+other.X, Y+other.Y);
		}
	};

	template <class T>
	class Vector3
	{
	public:
		Vector3() : X(0), Y(0), Z(0) {}
		Vector3(const T& x, const T& y, const T& z)
			: X(x), Y(y), Z(z) {}

		template <class U>
		explicit Vector3(const Vector3<U>& other) :
			X((T)other.X), Y((T)other.Y), Z((T)other.Z) { }

		template <class U>
		Vector3<T>& operator=(const Vector3<U>& other)
		{ 
			X = (T) other.X;
			Y = (T) other.Y;
			Z = (T) other.Z;
			return *this;
		}

		T X;
		T Y;
		T Z;

		bool operator==(const Vector3<T>& other) const
		{
			return (X == other.X && Y == other.Y && Z == other.Z);
		}

		bool operator!=(const Vector3<T>& other) const
		{
			return ! (*this == other);
		}

		Vector3<T>& set(const T& x, const T& y, const T& z)
		{
			X = x;
			Y = y;
			Z = z;
			return *this;
		}

		Vector3<T>& operator/=(const T& scale)
		{
			X /= scale;
			Y /= scale;
			Z /= scale;
			return *this;
		}

		Vector3<T> operator/(const T& scale) const
		{
			return Vector3<T>(X/scale, Y/scale, Z/scale);
		}

		Vector3<T>& operator*=(const T& scale)
		{
			X *= scale;
			Y *= scale;
			Z *= scale;
			return *this;
		}

		Vector3<T> operator*(const T& scale) const
		{
			return Vector3<T>(X*scale, Y*scale, Z*scale);
		}

		Vector3<T>& operator+=(const Vector3<T>& other)
		{
			X += other.X;
			Y += other.Y;
			Z += other.Z;
			return *this;
		}

		Vector3<T>& operator-=(const Vector3<T>& other)
		{
			X -= other.X;
			Y -= other.Y;
			Z -= other.Z;
			return *this;
		}

		Vector3<T>& operator*=(const Vector3<T>& other)
		{
			X *= other.X;
			Y *= other.Y;
			Z *= other.Z;
			return *this;
		}

		Vector3<T>& operator*(const Vector3<T>& other) const
		{
			return Vector3<T>(X * other.X, Y * other.Y, Z * other.Z);
		}


		Vector3<T> operator+(const Vector3<T>& other) const
		{
			return Vector3<T>(X+other.X, Y+other.Y, Z+other.Z);
		}

		Vector3<T> operator-(const Vector3<T>& other) const
		{
			return Vector3<T>(X-other.X, Y-other.Y, Z-other.Z);
		}

		T DotProduct(const Vector3<T>& other) const
		{
			return X*other.X + Y*other.Y + Z*other.Z;
		}

		T Length() const { return sqrt( X*X + Y*Y + Z*Z ); }

		Vector3<T> CrossProduct(const Vector3<T>& p) const
		{
			return Vector3<T>(Y * p.Z - Z * p.Y, Z * p.X - X * p.Z, X * p.Y - Y * p.X);
		}

		Vector3<T>& Normalize()
		{
			float length = X*X + Y*Y + Z*Z;
			length = sqrtf(length);

			X = (T)(X * length);
			Y = (T)(Y * length);
			Z = (T)(Z * length);
			return *this;
		}

		Vector3<T>& Invert()
		{
			X *= -1;
			Y *= -1;
			Z *= -1;
			return *this;
		}
	};

	template <class T>
	class Dimension2
	{
	public:
		Dimension2() : Width(0), Height(0) {}
		Dimension2(const T& width, const T& height)
			: Width(width), Height(height) {}

		template <class U>
		explicit Dimension2(const Dimension2<U>& other) :
			Width((T)other.Width), Height((T)other.Height) { }

		template <class U>
		Dimension2<T>& operator=(const Dimension2<U>& other)
		{ 
			Width = (T) other.Width;
			Height = (T) other.Height;
			return *this;
		}

		T Width;
		T Height;

		bool operator==(const Dimension2<T>& other) const
		{
			return (Width == other.Width && Height == other.Height);
		}

		bool operator!=(const Dimension2<T>& other) const
		{
			return ! (*this == other);
		}

		Dimension2<T>& set(const T& width, const T& height)
		{
			Width = width;
			Height = height;
			return *this;
		}

		Dimension2<T>& operator/=(const T& scale)
		{
			Width /= scale;
			Height /= scale;
			return *this;
		}

		Dimension2<T> operator/(const T& scale) const
		{
			return Dimension2<T>(Width/scale, Height/scale);
		}

		Dimension2<T>& operator*=(const T& scale)
		{
			Width *= scale;
			Height *= scale;
			return *this;
		}

		Dimension2<T> operator*(const T& scale) const
		{
			return Dimension2<T>(Width*scale, Height*scale);
		}

		Dimension2<T>& operator+=(const Dimension2<T>& other)
		{
			Width += other.Width;
			Height += other.Height;
			return *this;
		}

		Dimension2<T>& operator-=(const Dimension2<T>& other)
		{
			Width -= other.Width;
			Height -= other.Height;
			return *this;
		}

		Dimension2<T> operator+(const Dimension2<T>& other) const
		{
			return Dimension2<T>(Width+other.Width, Height+other.Height);
		}
	};

	class Color
	{
	public:
		Color() : R(0.0f), G(0.0f), B(0.0f), A(1.0f) {}
		Color(float r, float g, float b, float a) : R(r), G(g), B(b), A(a) { }

		float R;
		float G;
		float B;
		float A;
	};

	struct Plane4
	{
		Plane4() :
			A(0.0f), B(0.0f), C(0.0f), D(0.0f)
		{ }

		Plane4(const Plane4& other) {*this = other;}

		Plane4& operator=(const Plane4& other)
		{
			A = other.A;
			B = other.B;
			C = other.C;
			D = other.D;
			return *this;
		}

		void Normalize()
		{
			float norm = sqrt(A * A + B * B + C * C);

		    if (norm)
		    {
				A /= norm;
				B /= norm;
				C /= norm;
				D /= norm;
		    }
		    else
		    {
				A = 0.0f;
				B = 0.0f;
				C = 0.0f;
				D = 0.0f;
		    }
		}

		float DotCoord(const Vector3<float>& vector)
		{
			return (A * vector.X + B * vector.Y + C * vector.Z + D);
		}

		float A;
		float B;
		float C;
		float D;
	};

	enum LIGHT_TYPE
	{
		DIR_LIGHT = 0,
		POINT_LIGHT,
		SPOT_LIGHT
	};

	class ITexture : public IObject
	{
	public:
		virtual void Release() = 0;

		AUTO_OBJECT_SIZE;
		virtual const char* GetName() { return "Unknown Texture"; }
	};

	class ILight
	{
	public:

		LIGHT_TYPE Type;

		bool Enabled;
		Vector3<float> Position;
		Color Ambient;
		Color Diffuse;
		Color Specular;

		AUTO_OBJECT_SIZE;
		virtual const char* GetName() { return "Unknown Light"; }
	};

	class DirectionalLight : public ILight
	{
	public:
		DirectionalLight() { Enabled = true; Type = DIR_LIGHT; }
		~DirectionalLight() { } 

		Vector3<float> Direction;

		AUTO_OBJECT_SIZE;
		virtual const char* GetName() { return "Directional Light"; }
	};

	class PointLight : public ILight
	{
	public:
		PointLight() { Enabled = true; Type = POINT_LIGHT; }
		~PointLight() { } 

		float Radius;

		AUTO_OBJECT_SIZE;
		virtual const char* GetName() { return "Point Light"; }
	};

	class SpotLight : public ILight
	{
	public:
		SpotLight() { Enabled = true; Type = SPOT_LIGHT; }
		~SpotLight() { } 

		Vector3<float> Direction;
		float InnerCone;
		float OuterCone;
		float Radius;

		AUTO_OBJECT_SIZE;
		virtual const char* GetName() { return "Spot Light"; }
	};
}

#endif