#pragma once
#ifndef __OBSTACLEHPP__
#define __OBSTACLEHPP__

#pragma warning (push)
#include <map>
#include <algorithm>
#include <functional>
#include <vector>
#include <list>
#pragma warning (pop)
#include "mwobject.hpp"
#include "rail_move.hpp"

namespace MW4AI
{
	class Rect4DHashTable;

	const int Rect4DTimeDelta = 10;			// means 10th of a second
	extern Rect4DHashTable *g_Rect4DHash,*g_AirRect4DHash;
	extern Stuff::Scalar g_MaxBuildingHeight;					// above the ground
	extern Stuff::Scalar g_MaxAirHeight;							// above the ground, so stay above movers

	struct Rect4D
	{
		static Stuff::MemoryBlockOf<Rect4D> *m_EmptyRect;
		
		static Rect4D *GetBlankRect (void) { return (Rect4D *) m_EmptyRect->New (); }
		static void DoneRect (Rect4D *oldrect)  { if (oldrect) m_EmptyRect->Delete (oldrect); }
	
//		void *operator new (size_t size) {return gos_Malloc (sizeof (Rect4D)); }
//		void operator delete (void *value,size_t size) { gos_Free (value); }
		void *operator new (size_t size) {return GetBlankRect (); }
		void operator delete (void *value,size_t size) { DoneRect ((Rect4D *) value); }

		Stuff::Scalar left,right,top,bottom;
		int time;
		Stuff::Motion3D velocity;
		Rect4D *next;
		Rect4D *prev;
		Entity *who;
		void Save (MemoryStream *stream)
		{
			Verify (!next);				// cannot save member of chain
			Verify (!prev);
			Verify (!who);
			*stream << left;
			*stream << right;
			*stream << top;
			*stream << bottom;
			*stream << time;
			*stream << velocity;
		}
		void Load (MemoryStream *stream)
		{
			*stream >> left;
			*stream >> right;
			*stream >> top;
			*stream >> bottom;
			*stream >> time;
			*stream >> velocity;
		}
		Rect4D (void)
		{
			next = NULL;
			prev = NULL;
			who = NULL;
		}
		Rect4D (Stuff::Scalar p1,Stuff::Scalar p2,Stuff::Scalar p3,Stuff::Scalar p4,int p5,MWObject *p6,const Stuff::Motion3D& p7)
		{
			left = p1;
			top = p2;
			right = p3;
			bottom = p4;
			time = p5;
			next = NULL;
			prev = NULL;
			who = p6;
			velocity = p7;
			if (left > right)
			{
				Stuff::Scalar temp;
				temp = left;
				left = right;
				right = temp;
			}
			if (top > bottom)
			{
				Stuff::Scalar temp;
				temp = top;
				top = bottom;
				bottom = temp;
			}
		}
		bool operator== (const Rect4D& p1) const
		{
			if (left != p1.left)
				return false;
			if (right != p1.right)
				return false;
			if (top != p1.top)
				return false;
			if (bottom != p1.bottom)
				return false;
			if (time != p1.time)
				return false;
			return true;
		}
	};

	class Rect4DHashTable
	{
	private:
		Rect4D **m_List;
		Rect4D *m_PermList;
		int m_TopList;
		int m_CurrentTime;
		int m_ListSize;

		void KillHashLine (int i);
		int FindHashLine (const Rect4D& entry) const
		{
			Verify (entry.time >= m_CurrentTime);
			int deltatime,loc;

			deltatime = entry.time - m_CurrentTime;
			if (deltatime >= m_ListSize)
				return -1;
			Verify (deltatime < m_ListSize);
			loc = m_TopList + deltatime;
			if (loc >= m_ListSize)
				loc = deltatime - (m_ListSize - m_TopList);
			Verify (loc>=0);
			Verify (loc < m_ListSize);
			return loc;
		}

		void IncrementTime (void);
		void RemovePermRect (Rect4D *current)
		{
			Rect4D *temp;
			temp = current;
			current = current->next;
			if (m_PermList == temp)
				m_PermList = temp->next;
			if (temp->prev)
				temp->prev->next = temp->next;
			if (temp->next)
				temp->next->prev = temp->prev;
			delete temp;
		}

	public:
		void KillRectList (void);
		Rect4DHashTable (int size);
		~Rect4DHashTable (void);
		void AddPermRect (const Rect4D& p1)
		{
			Rect4D *arect;

//			Verify (p1.left >= 0);
//			Verify (p1.top >= 0);
//			Verify (p1.bottom < MaxZ);
//			Verify (p1.right < MaxX);
//			Verify (p1.left < p1.right);
//			Verify (p1.top < p1.bottom);
			arect = new Rect4D ();
			Verify (arect);

			*arect = p1;
			arect->prev = NULL;
			arect->next = m_PermList;
			if (m_PermList)
				m_PermList->prev = arect;
			m_PermList = arect;
		}

		void RemovePermRect (Entity *who);
		void RemovePermRect (const Rect4D& p1)
		{
			Rect4D *current,*temp;

			current = m_PermList;
			while (current)
			{
				if (current->who == p1.who)
				{
					temp = current;
					current = current->next;
					if (m_PermList == temp)
						m_PermList = temp->next;
					if (temp->prev)
						temp->prev->next = temp->next;
					if (temp->next)
						temp->next->prev = temp->prev;
					delete temp;
					return;
				}
				current = current->next;
			}
		}
		void AddRect (Rect4D* p1);
		void RemoveRect (Rect4D *p1)
		{
			int loc;

			if (p1->time < m_CurrentTime)
				return;
			Verify (p1);
			loc = FindHashLine (*p1);
			if (loc==-1)
				return;
			Verify (loc>=0);
			Verify (loc < m_ListSize);

			if (m_List[loc] == p1)
				m_List[loc] = p1->next;
			if (p1->prev)
				p1->prev->next = p1->next;
			if (p1->next)
				p1->next->prev = p1->prev;
			p1->next = p1->prev = NULL;
		//	delete p1;
		}

#ifdef _ARMOR
		void VerifyRectGone (Rect4D *p1);
#else
		void VerifyRectGone (Rect4D *p1)
		{}
#endif
		void UpdateTime (Stuff::Time curtime);
		Rect4D *Collide (const Rect4D& p1) const;
		Rect4D *Collide (const Rect4D* p1) const
		{ return Collide (*p1); }
	};
	

	struct LockKey
	{
		int locx,locz;
		LockKey (int p1,int p2)
		{
			locx = p1;
			locz = p2;
		}
		bool operator== (const LockKey& p1) const
		{
			return ((locx == p1.locx) && (locz == p1.locz));
		}
	};
	struct LockData
	{
		static Stuff::MemoryBlockOf<LockData> *m_EmptyData;
		
		static LockData *GetBlankData (void) { return (LockData *) m_EmptyData->New (); }
		static void DoneData (LockData *oldrect)  { if (oldrect) m_EmptyData->Delete (oldrect); }
	
		void *operator new (size_t size) {return GetBlankData (); }
		void operator delete (void *value,size_t size) { DoneData ((LockData *) value); }

		int locx,locz;
		unsigned short olddata;
		MWObject *who;
		LockData *next;
		LockData (void)
		{
			locx = -1;
			locz = -1;
			olddata = -1;
			next = NULL;
		}
		LockData (int p1,int p2,unsigned short p3)
		{
			locx = p1;
			locz = p2;
			olddata = p3;
			who = NULL;
			next = NULL;
		}
		LockData (int p1,int p2,MWObject *p3)
		{
			locx = p1;
			locz = p2;
			olddata = 0;
			who = p3;
			next = NULL;
		}
	};

	inline bool operator< (const LockKey& p1,const LockKey& p2)
	{
			if (p1.locx < p2.locx)
				return true;
			if (p1.locz < p2.locz)
				return true;
			return false;
	}

	struct obstacleptcmp : stlport::binary_function<LockKey,LockKey, bool>
	{
		bool operator()(const LockKey& _X, const LockKey& _Y) const
		{
			if (_X.locx < _Y.locx)
				return true;
			if (_X.locz < _Y.locz)
				return true;
			return false;
		}
	};

//	extern stlport::map<LockKey,MWObject *> *g_Obstacles;		// created in AI::Initilizeclass
//	extern stlport::vector<LockData> *g_Obstacles;		// created in AI::Initilizeclass

	MWObject *BlockerLocal (int x,int z);
	bool AddBlockLocal (int x,int z,MWObject *who);
	void RemoveBlockLocal (int x,int z,MWObject *who);

	void InitObstacle (void);
	void CleanObstacle (void);
	void SetupPermRects (void);
	void KillPermRects (void);


	inline MWObject *Blocker (const Point3D& pt)
	{
		int x,z;

    x = (int) (pt.x);
    z = (int) (pt.z);
		x = (int) (x/BLOCK_GRID_RES);
		z = (int) (z/BLOCK_GRID_RES);
		return BlockerLocal (x,z);
	}
	inline MWObject *Blocker (int x,int z)
	{
		x = (x/BLOCK_GRID_RES);
		z = (z/BLOCK_GRID_RES);
		return BlockerLocal (x,z);
	}
	inline bool AddBlock (const Point3D& pt,MWObject *who)
	{
		int x,z;

    x = (int) (pt.x);
    z = (int) (pt.z);
		x = (int) (x/BLOCK_GRID_RES);
		z = (int) (z/BLOCK_GRID_RES);
		return AddBlockLocal (x,z,who);
	}
	inline bool AddBlock (int x,int z,MWObject *who)
	{
		x = (x/BLOCK_GRID_RES);
		z = (z/BLOCK_GRID_RES);
		return AddBlockLocal (x,z,who);
	}
	inline void RemoveBlock (const Point3D& pt,MWObject *who)
	{
		int x,z;

    x = (int) (pt.x);
    z = (int) (pt.z);
		x = (int) (x/BLOCK_GRID_RES);
		z = (int) (z/BLOCK_GRID_RES);
		RemoveBlockLocal (x,z,who);
	}
	inline void RemoveBlock (int x,int z,MWObject *who)
	{
		x = (x/BLOCK_GRID_RES);
		z = (z/BLOCK_GRID_RES);
		RemoveBlockLocal (x,z,who);
	}


};


#endif
