#include "MW4Headers.hpp"
#include "Adept\AdeptHeaders.hpp"
#include <Adept\CollisionGrid.hpp>
#include <adept\collisionvolume.hpp>
#include "moverai.hpp"
#include "obstacle.hpp"
#include "bridge.hpp"
#include "cultural.hpp"
#include "airplane.hpp"
#include "building.hpp"
#include "mwplayer.hpp"

namespace MW4AI
{	

	struct LineData
	{
		LockData *root;
	};
	const int MAXLINES = 1024;
	LineData *g_Obstacles;
	Rect4DHashTable *g_Rect4DHash,*g_AirRect4DHash;
	Stuff::Scalar g_MaxBuildingHeight = 0;
	Stuff::Scalar g_MaxAirHeight = 0;
	Stuff::MemoryBlockOf<Rect4D> *Rect4D::m_EmptyRect=NULL;
	Stuff::MemoryBlockOf<LockData> *LockData::m_EmptyData = NULL;
	DWORD Obstacle_Size = 0;

	void InitObstacle (void)
	{
		int i;

		Rect4D::m_EmptyRect = new MemoryBlockOf<Rect4D> (1000,100,"Temporal Rects");
		LockData::m_EmptyData = new MemoryBlockOf<LockData> (1000,100,"Path lock Data");
		g_Rect4DHash = new Rect4DHashTable (200);
		g_AirRect4DHash = new Rect4DHashTable(200);
		g_Obstacles = new LineData[MAXLINES];
		Check_Pointer (g_Obstacles);
		for (i=0;i<MAXLINES;i++)
		{
			g_Obstacles[i].root = NULL;
		}
	}
	void CleanObstacle (void)
	{
		int i;
		for (i=0;i<MAXLINES;i++)
		{
			while (g_Obstacles[i].root)
			{
				LockData *temp;

				temp = g_Obstacles[i].root;
				Check_Pointer (temp);
				g_Obstacles[i].root = g_Obstacles[i].root->next;
				delete temp;
			}
		}
		delete g_Obstacles;
		g_Obstacles = NULL;
		delete g_Rect4DHash;
		g_Rect4DHash = NULL;
		delete g_AirRect4DHash;
		g_AirRect4DHash = NULL;
		delete Rect4D::m_EmptyRect;
		Rect4D::m_EmptyRect = NULL;
		delete LockData::m_EmptyData;
		LockData::m_EmptyData = NULL;
	}

	void MW4AI::KillPermRects (void)
	{
		g_Rect4DHash->KillRectList ();
	}

	void SetupPermRects (void)
	{
		Verify (g_Rect4DHash);

		int tablescan[] =
		{
			NameTable::BuildingArray,
			NameTable::TurretArray,
			NameTable::VehicleArray
		};
		g_MaxBuildingHeight = 0;
		g_MaxAirHeight = 0;
		int size,i,j;
		NameTable *table = NameTable::GetInstance();
		Check_Object (table);
		for (i=0;i<sizeof (tablescan)/sizeof (int);i++)
		{
			size = table->nameTableArray[tablescan[i]].GetLength ();
			for (j=0;j<size;j++)
			{
				NameTableEntry *data;
				Entity *who;

				data = &table->nameTableArray [tablescan[i]][j];

				if (data->objectName && !strnicmp (data->objectName,"water",5))
				{
					continue;
				}

				who = data->dataPointer->GetCurrent ();

				if (!who)
					continue;
				if ((!who->GetHierarchicalVolume ()) && (!who->GetSolidVolume ()))
					continue;

				MWObject *obj=NULL;
				OBB *obb;
				CollisionVolume *col;
				col = who->GetHierarchicalVolume ();
				if (!col)
					col = who->GetSolidVolume ();
				OBB world_bounds;
				world_bounds.Multiply(col->m_localSpaceBounds, who->GetLocalToWorld());
				obb = &(world_bounds);
				ExtentBox box (*obb);
				
				if (who->IsDerivedFrom (Vehicle::DefaultData))
				{
					if ((box.maxY - box.minY) > g_MaxAirHeight)
					{
						g_MaxAirHeight = box.maxY - box.minY;
					}
				}
				else 					// must be a building
				{
					if ((box.maxY - box.minY) > g_MaxBuildingHeight)
					{
						g_MaxBuildingHeight = box.maxY - box.minY;
					}
				}

				if (!who->IsDerivedFrom (MechWarrior4::Cultural::DefaultData))
				{
					obj = Cast_Object (MWObject *,who);
					AI *ai;
					ai = obj->GetAI ();
					if (ai && ai->IsDerivedFrom (MoverAI::DefaultData))
						continue;
				}
				if (who->IsDerivedFrom (Bridge::DefaultData))
					continue;
//				if ((obj->GetAI ()) && (tablescan[i] != NameTable::BuildingArray))
//					continue;

				Point3D loc = (Point3D) obb->localToParent;

				Rect4D temprect;
				temprect.left = box.minX;
				temprect.right = box.maxX;
				temprect.top = box.minZ;
				temprect.bottom = box.maxZ;
				temprect.who = who;
				g_Rect4DHash->AddPermRect (temprect);
			}
		}

		g_MaxAirHeight += 5.0f;
		g_MaxBuildingHeight += 10.0f;
		if (g_MaxBuildingHeight < 20.0f)
		{
			g_MaxBuildingHeight = 20.0f;
		}
		Check_Object (table);
		size = table->nameTableArray[NameTable::VehicleArray].GetLength ();
		for (j=0;j<size;j++)
		{
			NameTableEntry *data;
			Entity *who;
			Airplane *air;

			data = &table->nameTableArray [NameTable::VehicleArray][j];
			who = data->dataPointer->GetCurrent ();
			if (!who)
				continue;
			if (!who->IsDerivedFrom (Airplane::DefaultData))
			{
				continue;
			}
			air = Cast_Object (Airplane *,who);
			air->AdjustAltitude ();
		}
	}

	MWObject *BlockerLocal (int x,int z)
	{
		LockData *temp;

		x -= (int) (MinX/BLOCK_GRID_RES);
		z -= (int) (MinZ/BLOCK_GRID_RES);
		Clamp (x,0,(int) (2*(MaxX/BLOCK_GRID_RES)));
		Clamp (z,0,MAXLINES-1);
		Verify (z>=0);
		Verify (z<MAXLINES);
		Verify (g_Obstacles);
		temp = g_Obstacles[z].root;
		while (temp)
		{
			Check_Pointer (temp);
			if ((temp->locx == x) && (temp->locz == z))
				return temp->who;
			temp = temp->next;
		}
		return NULL;
	}

	bool AddBlockLocal (int x,int z,MWObject *who)
	{
		if (BlockerLocal (x,z))
		{
			return false;
		}
		x -= (int) (MinX/BLOCK_GRID_RES);
		z -= (int) (MinZ/BLOCK_GRID_RES);
		Verify (z>=0);
		Verify (z<MAXLINES);
		Verify (g_Obstacles);

		LockData *temp;
		temp = new LockData (x,z,who);
		temp->next = g_Obstacles[z].root;
		g_Obstacles[z].root = temp;
		Set_Statistic(Obstacle_Size, Obstacle_Size+1);
		return true;
	}
	
	void RemoveBlockLocal (int x,int z,MWObject *who)
	{
		x -= (int) (MinX/BLOCK_GRID_RES);
		z -= (int) (MinZ/BLOCK_GRID_RES);
		Verify (z>=0);
		Verify (z<MAXLINES);
		Verify (g_Obstacles);
		LockData *temp;

		temp = g_Obstacles[z].root;
		//
		// Very non-repro bug occassionally results in this being NULL. Theory is math order causes Z to fall just slightly
		// above or below truncation point, so the same Z passed in, results in a different index.
		//
		if (!temp)
			return;
		if ((temp->locx == x) && (temp->locz == z) && (temp->who == who))
		{
			g_Obstacles[z].root = temp->next;
			if (g_Obstacles[z].root)
				Check_Pointer (g_Obstacles[z].root);
			delete temp;
			Set_Statistic(Obstacle_Size, Obstacle_Size-1);
			return;
		}
		while (temp->next)
		{
			LockData *fred;
			fred = temp->next;
			if ((fred->locx == x) && (fred->locz == z) && (fred->who == who))
			{
				temp->next = fred->next;
				if (temp->next)
					Check_Pointer (temp->next);
				delete fred;
				Set_Statistic(Obstacle_Size, Obstacle_Size-1);
				return;
			}

			temp = temp->next;
		}

		Verify (false);
	}




void Rect4DHashTable::KillHashLine (int i)
{
	ablREPORT (i>=0,"");
	ablREPORT (i < m_ListSize,"");
	m_List[i] = NULL; // everybody else is responsible for freeing themselvs
}

void Rect4DHashTable::KillRectList (void)
{
	Rect4D *current,*temp;

	current = m_PermList;
	while (current)
	{
		temp = current;
		current = current->next;
		temp->next = temp->prev = NULL;

		delete temp;
	}
	m_PermList = NULL;
}

void Rect4DHashTable::AddRect (Rect4D* p1)
{
	Verify (false);
	int loc;
	ablREPORT (p1,"");
	ablREPORT (p1->who,"");
	ablREPORT (p1->who->IsDerivedFrom (Entity::DefaultData),"");
//	Verify (p1->who->IsDerivedFrom (MWObject::DefaultData));
	ablREPORT (p1->who->IsDerivedFrom (MechWarrior4::MWPlayer::DefaultData) || p1->who->IsDerivedFrom (AI::DefaultData) || p1->who->IsDerivedFrom (Vehicle::DefaultData) || p1->who->IsDerivedFrom (Building::DefaultData),"");
	if (p1->left < MinX)
		p1->left = MinX;
	if (p1->top < MinZ)
		p1->top = MinZ;
	if (p1->right < MinX)
		p1->right = MinX;
	if (p1->bottom < MinZ)
		p1->bottom = MinZ;

	if (p1->top >= (MaxZ-1))
		p1->top = MaxZ-1;
	if (p1->left >= (MaxX-1))
		p1->left = MaxX-1;
	if (p1->bottom >= (MaxZ-1))
		p1->bottom = MaxZ-1;
	if (p1->right >= (MaxX-1))
		p1->right = MaxX-1;

	ablREPORT (p1->left <= p1->right,"");
	ablREPORT (p1->top <= p1->bottom,"");
	loc = FindHashLine (*p1);
	if (loc==-1)
		return;
	ablREPORT (loc>=0,"");
	ablREPORT (loc < m_ListSize,"");

	ablREPORT (p1->prev == NULL,"");
	ablREPORT (p1->next == NULL,"");
	p1->prev = NULL;
	p1->next = m_List[loc];
	if (m_List[loc])
	{
		ablREPORT (m_List[loc],"");
		m_List[loc]->prev = p1;
	}
	m_List[loc] = p1;
}

Rect4DHashTable::Rect4DHashTable (int size)
{
	int i;
	m_List = (Rect4D **) gos_Malloc (sizeof (Rect4D *) * size);
	for (i=0;i<size;i++)
		m_List[i] = NULL;
	m_TopList = 0;
	m_PermList = NULL;
	m_CurrentTime = 0;
	m_ListSize = size;
}
Rect4DHashTable::~Rect4DHashTable (void)
{
	int i;
	for (i=0;i<m_ListSize;i++)
	{
		KillHashLine (i);
	}
	gos_Free (m_List);
	Rect4D *current,*temp;
	current = m_PermList;
	while (current)
	{
		temp = current;
		current = current->next;
		delete temp;
	}
}

void Rect4DHashTable::UpdateTime (Stuff::Time curtime)
{
	int temptime;

	curtime *= Rect4DTimeDelta;
	temptime = (int) ((float) curtime);
//	temptime = Truncate_Float_To_Word ((float) curtime);
	while (m_CurrentTime < temptime)
	{
		IncrementTime ();
	}
}

void Rect4DHashTable::IncrementTime (void)
{
	KillHashLine (m_TopList);
	m_TopList++;
	m_CurrentTime += 1;
	if (m_TopList == m_ListSize)
		m_TopList = 0;
}

void Rect4DHashTable::RemovePermRect (Entity *who)
{
	Rect4D *current;

	current = m_PermList;
	while (current)
	{
		if (current->who == who)
		{
			RemovePermRect (current);
			return;
		}
		current = current->next;
	}
}

Rect4D *Rect4DHashTable::Collide (const Rect4D& p1) const
{
	Verify (false);
	int loc;
	Rect4D *current;
//	Stuff::Scalar left,right,top,bottom;

	current = m_PermList;
	for (current=m_PermList;current;current = current->next)
	{
		if (p1.who != current->who)
		{
			if (p1.right < current->left)
				continue;
			if (p1.left > current->right)
				continue;
			if (p1.bottom < current->top)
				continue;
			if (p1.top > current->bottom)
				continue;
			return current;
		}															
	}

	if (p1.time < m_CurrentTime)
		return NULL;

	if (p1.time >= (m_CurrentTime + m_ListSize))
		return NULL;

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


	current = m_List[loc];
	for (current=m_List[loc];current;current = current->next)
	{
		if (p1.who != current->who)
		{
			if (p1.right < current->left)
				continue;
			if (p1.left > current->right)
				continue;
			if (p1.bottom < current->top)
				continue;
			if (p1.top > current->bottom)
				continue;
			return current;
		}
	}

	return NULL;

}

#ifdef _ARMOR

void Rect4DHashTable::VerifyRectGone (Rect4D *p1)
{
	int i;
	Rect4D *current;
	
	for (i=0;i<m_ListSize;i++)
	{
		current = m_List[i];
		while (current)
		{
			Verify (current != p1);
			current = current->next;
		}
	}
}

#endif


};







