#pragma warning (disable:4786)
#include <utils\utils.h>
#include "map.hpp"
#include "move.hpp"
#include "movecell.hpp"

float pathmapcell[MAPCELLSIZE][MAPCELLSIZE];

FixedHeap<CALCCELLELEMENT> CALCCELLELEMENT::m_EmptyCalcCellElement(1);

void InitMoveCellData (void)
{
}

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

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

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

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

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

	if (pathmapcell[x][y] < pathmapcell[fred.x][fred.y])
		return true;
	if (pathmapcell[x][y] > pathmapcell[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;
}


float CCellPath::GetOneCost (int sx,int sy,int dx,int dy)
{
	float toret;

	if (dx<0)
		return -1;
	if (dy<0)
		return -1;
	if (dx>=MAPCELLSIZE)
		return -1;
	if (dy>=MAPCELLSIZE)
		return -1;

	sx += g_MapCells[m_Cell.x][m_Cell.y].Left ();
	dx += g_MapCells[m_Cell.x][m_Cell.y].Left ();
	sy += g_MapCells[m_Cell.x][m_Cell.y].Top ();
	dy += g_MapCells[m_Cell.x][m_Cell.y].Top ();
	if (!Passable (dx,dy))
		return -1;

	toret = abs (g_Map[sx][sy].weight - g_Map[dx][dy].weight)+1;
//		toret *= 0.5;

	return toret;

/*
	int deltax,deltay;
	deltax = dx - sx;
	deltay = dy - sy;
	if (deltax == 0)
		return toret*0.5;
	if (deltay == 0)
		return toret*0.5;
	return toret * 0.75;
*/	
}

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

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

	__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
	}
//		return t1+t2;

	return t2 < t1 ? (t1+(t2>>1)) : (t2+(t1>>1));
}
	
CCellPath::CCellPath (CPoint start,CPoint cell,CPoint end)
{
	assert (start.x>=0);
	assert (start.x<MAPCELLSIZE);
	assert (start.y>=0);
	assert (start.y<MAPCELLSIZE);

	m_Start = start;
	m_Start.x -= g_MapCells[cell.x][cell.y].Left ();
	m_Start.y -= g_MapCells[cell.x][cell.y].Top ();
	
	end.x -= g_MapCells[cell.x][cell.y].Left ();
	end.y -= g_MapCells[cell.x][cell.y].Top ();

	m_Cell = cell;
	m_StartArea = g_MapCells[cell.x][cell.y].AreaLocal (start.x,start.y);
	m_EndArea = g_MapCells[cell.x][cell.y].AreaLocal (end.x,end.y);
	m_End.push_back (end);
	m_EndAreaOther = 0;
}

CCellPath::CCellPath (CPoint start,CPoint cell,int endarea,int endareaother,int edge)
{
	int x,y;
	int dcx,dcy;

	assert (start.x>=0);
	assert (start.x<MAPCELLSIZE);
	assert (start.y>=0);
	assert (start.y<MAPCELLSIZE);

	m_Start = start;

	m_Start.x -= g_MapCells[cell.x][cell.y].Left ();
	m_Start.y -= g_MapCells[cell.x][cell.y].Top ();

	m_Cell = cell;
	m_StartArea = g_MapCells[cell.x][cell.y].AreaLocal (start.x,start.y);
	assert (endareaother != 0);
	m_EndAreaOther = endareaother;
	dcx = cell.x;
	dcy = cell.y;
	switch (edge)
	{
		case LEFTEDGE:
			dcx--;
			assert (dcx>=0);
			for (y=0;y<MAPCELLSIZE;y++)
				if ((g_MapCells[cell.x][cell.y].AreaLocal (0,y) == endarea) && (g_MapCells[dcx][dcy].AreaLocal (MAPCELLSIZE-1,y)==endareaother))
					m_End.push_back (CPoint (0,y));
		break;
		case TOPEDGE:
			dcy--;
			assert (dcy>=0);
			for (x=0;x<MAPCELLSIZE;x++)
				if ((g_MapCells[cell.x][cell.y].AreaLocal (x,0) == endarea) && (g_MapCells[dcx][dcy].AreaLocal (x,MAPCELLSIZE-1)==endareaother))
					m_End.push_back (CPoint (x,0));
		break;
		case RIGHTEDGE:
			dcx++;
			assert (dcx<MAPCELLCOUNT);
			for (y=0;y<MAPCELLSIZE;y++)
				if ((g_MapCells[cell.x][cell.y].AreaLocal (MAPCELLSIZE-1,y) == endarea) && (g_MapCells[dcx][dcy].AreaLocal (0,y)==endareaother))
					m_End.push_back (CPoint (MAPCELLSIZE-1,y));
		break;
		case BOTTOMEDGE:
			dcy++;
			assert (dcy<MAPCELLCOUNT);
			for (x=0;x<MAPCELLSIZE;x++)
				if ((g_MapCells[cell.x][cell.y].AreaLocal (x,MAPCELLSIZE-1) == endarea) && (g_MapCells[dcx][dcy].AreaLocal (x,0)==endareaother))
					m_End.push_back (CPoint (x,MAPCELLSIZE-1));
		break;
		default:
			assert (false);
	}
}

CCellPath::~CCellPath (void)
{
	while (!m_Path.empty ())
		m_Path.pop ();
}


bool CCellPath::CalcPath (void)
{
	int i,j;
	CPoint end;
	FibHeap calc;
	CALCCELLELEMENT *temp;

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

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

	for (i=0;i<MAPCELLSIZE;i++)
		for (j=0;j<MAPCELLSIZE;j++)
			pathmapcell[i][j] = 0;

	temp = new CALCCELLELEMENT (m_Start.x,m_Start.y);
	pathmapcell[m_Start.x][m_Start.y] = 1;
	calc.Insert (temp);

	done = false;
	while (!done)
	{
		temp = (CALCCELLELEMENT *) calc.ExtractMin ();
		if (temp == NULL)
			break;
		for (i=0;i<OFFSIZECELL;i++)
		{
			x = offsetcell[i][0] + temp->x;
			y = offsetcell[i][1] + temp->y;
			float cost = GetOneCost (temp->x,temp->y,x,y);
			if (cost == -1)
				continue;
			cost += PathCostGuess (x,y,m_End[0].x,m_End[0].y);
			if ((pathmapcell[x][y] == 0) || (pathmapcell[x][y] > (pathmapcell[temp->x][temp->y]+cost)))
			{
				pathmapcell[x][y] = pathmapcell[temp->x][temp->y]+cost;
				calc.Insert (new CALCCELLELEMENT(x,y));
			}
			if (ValidEnd (x,y))
			{
				end = CPoint (x,y);
				done = true;
				break;
			}
		}
		delete temp;
	}

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

	if (!done)
		return false;


	done = false;
	int curx,cury,min;

	curx = end.x;
	cury = end.y;
	m_RealEnd = end;

	m_Path.push (PATHCELLELEMENT (curx,cury));
	min = -1;
	while (!done)
	{
		min = -1;
		assert (pathmapcell[curx][cury] != 0);
		for (i=0;i<OFFSIZECELL;i++)
		{
			x = offsetcell[i][0] + curx;
			y = offsetcell[i][1] + cury;
			if (x < 0)
				continue;
			if (x >= MAPCELLSIZE)
				continue;
			if (y < 0)
				continue;
			if (y >= MAPCELLSIZE)
				continue;
			if (pathmapcell[x][y] == 0)
				continue;
			if (pathmapcell[x][y] < pathmapcell[curx][cury])
			{
				if (min == -1)
					min = i;
				else
				{
					if (pathmapcell[x][y] < pathmapcell[curx+offsetcell[min][0]][cury+offsetcell[min][1]])
						min = i;
				}
			}
		}
		assert (min != -1);
		m_Path.push (PATHCELLELEMENT (curx+offsetcell[min][0],cury+offsetcell[min][1]));
		if (((curx+offsetcell[min][0]) == m_Start.x) && ((cury+offsetcell[min][1]) == m_Start.y))
		{
//			m_RealEnd.x = curx+offsetcell[min][0];
//			m_RealEnd.y = cury+offsetcell[min][1];
			done = true;
			continue;
		}
		curx += offsetcell[min][0];
		cury += offsetcell[min][1];
	}

	return true;
}


