#pragma warning (disable:4786)
#include "ai formation.hpp"
#include "globals.hpp"

extern float pathmap[MAPSIZE][MAPSIZE];
bool Passable (int dx,int dy);

AIFormation::AIFormation (void)
{
	m_Location.x = 1;
	m_Location.y = 1;
}

AIFormation::~AIFormation (void)
{
}

bool AIFormation::AddAI (AI *newai)
{
	AIMove *temp;

	temp = newai->Move ();
	if (!temp)
		return false;
	return temp->JoinFormation (*this);
}

bool AIFormation::AddMove (AIMove *newmove)
{
	FormationData temp;

	temp.member = newmove;
	temp.dx = 1;
	temp.dy = 1;
	m_CurrentMembers.push (temp);
	return true;
}

void AIFormation::SetDestination (int x,int y)
{
	curdes.x = x;
	curdes.y = y;
	newdes = true;
}

bool AIFormation::Think (double value)
{
	if (newdes)
	{
		if (!g_Map[curdes.x][curdes.y].passable)
			return false;

		newdes = false;

		if (!CalcPath (-1,true))
			return false;
	}
	if (m_CurPath.empty())
		return false;

	int i,size;
	FormationData temp;

	PATHELEMENT path = m_CurPath.top();
	m_CurPath.pop ();

	size = m_CurrentMembers.size();
	for (i=0;i<size;i++)
	{
		temp = m_CurrentMembers.front();
		m_CurrentMembers.pop();
		m_CurrentMembers.push (temp);
//		temp.member->SetFormationDes (path.x,path.y);
		temp.member->Parent ()->Think (value);
	}
//	m_Location.x = path.x;
//	m_Location.y = path.y;
	return true;
}

float AIFormation::GetOneCost (int sx,int sy,int dx,int dy)
{
	int deltax,deltay;

	if (!Passable (dx,dy))
		return -1;

	deltax = dx - sx;
	deltay = dy - sy;
	if (deltax == 0)
		return 1;
	if (deltay == 0)
		return 1;
	return 1.5f;
}

float AIFormation::PathCostGuess (int sx,int sy,int dx,int dy)
{
	int t1,t2;

	t1 = dx - sx;
	t2 = dy - sy;

	__asm
	{
		mov eax,t1
		cdq
		xor eax,edx
		sub eax,edx
		mov t1,eax
		mov eax,t2
		cdq
		xor eax,edx
		sub eax,edx
		mov t2,eax
	}
	if (t1 == 0)
		return t2;
	if (t2 == 0)
		return t1;
	if ((t1 == 0) && (t2 == 0))
		return 0;
	float f1,f2;
	f1 = t1;
	f2 = t2;
	return f2 < f1 ? (f1+(f2/2.0)) : (f2+(f1/2.0));
}

bool AIFormation::CalcPath (int depth,bool followshort)
{
#if 0
	int i,j;
	FibHeap calc;
	CALCELEMENT *temp;
	CALCELEMENT *shortdes = NULL;
	const int OFFSIZE = 8;
	int lastdepth;

	int offset[OFFSIZE][2] = {
	{-1,-1},
	{-1,0},
	{-1,1},
	{0,-1},
	{0,1},
	{1,-1},
	{1,0},
	{1,1}
	};

	int x=-1,y=-1;
	bool done;

	while (!m_CurPath.empty ())
		m_CurPath.pop ();

	for (i=0;i<MAPSIZE;i++)
	{
		for (j=0;j<MAPSIZE;j++)
		{
			pathmap[i][j] = 0;
		}
	}

	temp = new CALCELEMENT (Location().x,Location().y);
	pathmap[Location().x][Location().y] = 1;
	calc.Insert (temp);

	done = false;	
	lastdepth = 0;
	while (!done && ((depth == -1)||(depth<lastdepth)))
	{
		temp = (CALCELEMENT *) calc.ExtractMin ();
		if (temp == NULL)
			break;
		lastdepth = pathmap[temp->x][temp->y];
		for (i=0;i<OFFSIZE;i++)
		{
			x = offset[i][0] + temp->x;
			y = offset[i][1] + temp->y;
			float cost = GetOneCost (temp->x,temp->y,x,y);
			if (cost == -1)
				continue;
			cost += PathCostGuess (x,y,curdes.x,curdes.y);
			if ((pathmap[x][y] == 0) || (pathmap[x][y] > (pathmap[temp->x][temp->y]+cost)))
			{
				pathmap[x][y] = pathmap[temp->x][temp->y]+cost;
				calc.Insert (new CALCELEMENT(x,y));
			}
			if ((x == curdes.x) && (y == curdes.y))
			{
				done = true;
				break;
			}
		}
		delete temp;
	}
	if ((!done) && (depth != -1) && (depth>=lastdepth) && followshort)
	{
		shortdes = (CALCELEMENT *) calc.ExtractMin ();
		if (shortdes)
			done = true;
	}

	while (temp = (CALCELEMENT *) calc.ExtractMin ())
		delete temp;

	if (!done)
		return false;

	done = false;
	int curx,cury,min;

	if (shortdes)
	{
		curx = shortdes->x;
		cury = shortdes->y;
		delete shortdes;
	}
	else
	{
		curx = curdes.x;
		cury = curdes.y;
	}
	m_CurPath.push (PATHELEMENT (curx,cury));
	min = -1;
	while (!done)
	{
		min = -1;
		assert (pathmap[curx][cury] != 0);
		for (i=0;i<OFFSIZE;i++)
		{
			x = offset[i][0] + curx;
			y = offset[i][1] + cury;
			if (x < 0)
				continue;
			if (x >= MAPSIZE)
				continue;
			if (y < 0)
				continue;
			if (y >= MAPSIZE)
				continue;
			if (pathmap[x][y] == 0)
				continue;
			if (pathmap[x][y] < pathmap[curx][cury])
			{
				if (min == -1)
					min = i;
				else
				{
					if (pathmap[x][y] < pathmap[curx+offset[min][0]][cury+offset[min][1]])
						min = i;
				}
			}
		}
		assert (min != -1);
		m_CurPath.push (PATHELEMENT (curx+offset[min][0],cury+offset[min][1]));
		if (((curx+offset[min][0]) == Location().x) && ((cury+offset[min][1]) == Location ().y))
		{
			done = true;
			continue;
		}
		curx += offset[min][0];
		cury += offset[min][1];
	}
#endif
	return true;
}
/*
bool PATHELEMENT::operator< (const PATHELEMENT& p1) const
{
	if (p1.x < x)
		return false;
	if (x < p1.x)
		return true;
	if (p1.y < y)
		return false;
	if (y < p1.y)
		return true;
	return true;
}

bool PATHELEMENT::operator== (const PATHELEMENT& p1) const
{
	if ((p1.x == x) && (p1.y == y))
		return true;
	return false;
}
*/


#if 0
class CALCELEMENT : public FibHeapNode
{
private:
	static FixedHeap<CALCELEMENT> m_EmptyCalcElement;

public:
	int x,y;
	CALCELEMENT (int nx,int ny) { x = nx; y = ny;}
	CALCELEMENT (void) { x = -1; y = -1; }
    virtual void operator =(FibHeapNode& RHS);
    virtual bool operator ==(FibHeapNode& RHS);
    virtual bool operator <(FibHeapNode& RHS);

//    static CALCELEMENT *GetBlankElement (void) { return (CALCELEMENT *) m_EmptyCalcElement.Alloc (); }
//    static void DoneElement (CALCELEMENT *oldelem)  { m_EmptyCalcElement.Free (oldelem); }

//	void *operator new (size_t size) {return GetBlankElement (); }
//	void operator delete (void *value,size_t size) { DoneElement ((CALCELEMENT *) value); }
};

//FixedHeap<CALCELEMENT> CALCELEMENT::m_EmptyCalcElement(1);

void CALCELEMENT::operator =(FibHeapNode& RHS)
{
	CALCELEMENT& fred = (CALCELEMENT&) RHS;

	x = fred.x;
	y = fred.y;
}

bool CALCELEMENT::operator ==(FibHeapNode& RHS)
{
	CALCELEMENT& fred = (CALCELEMENT&) RHS;

	if (x != fred.x)
		return false;
	if (y != fred.y)
		return false;
	return true;
}

bool CALCELEMENT::operator <(FibHeapNode& RHS)
{
	CALCELEMENT& fred = (CALCELEMENT&) RHS;

	if (pathmap[x][y] < pathmap[fred.x][fred.y])
		return true;
	if (pathmap[x][y] > pathmap[fred.x][fred.y])
		return false;
	if (fred.x < x)
		return false;
	if (x < fred.x)
		return true;
	if (fred.y < y)
		return false;
	if (y < fred.y)
		return true;
	return true;
}

#endif