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

MAPELEMENT g_Map[MAPSIZE][MAPSIZE];

void GenMap (MAPELEMENT map[MAPSIZE][MAPSIZE],int x1,int y1,int x2,int y2);

void InitMap (void)
{
	int x,y;

	for (y=0;y<MAPSIZE;y++)
		for (x=0;x<MAPSIZE;x++)
		{
			g_Map[x][y].passable = true;
			g_Map[x][y].weight = 0.0;
		}
	for (y=0;y<MAPSIZE;y++)
	{
		g_Map[0][y].passable = false;
		g_Map[MAPSIZE-1][y].passable = false;
	}
	for (x=0;x<MAPSIZE;x++)
	{
		g_Map[x][0].passable = false;
		g_Map[x][MAPSIZE-1].passable = false;
	}


	g_Map[0][0].weight = 0.5f;
	g_Map[MAPSIZE-1][0].weight = 0.5f;
	g_Map[0][MAPSIZE-1].weight = 0.5f;
	g_Map[MAPSIZE-1][MAPSIZE-1].weight = 0.5f;

	GenMap (g_Map,0,0,MAPSIZE-1,MAPSIZE-1);


	for (y=0;y<MAPSIZE;y++)
		for (x=0;x<MAPSIZE;x++)
		{
			if (g_Map[x][y].weight > 0.75)
				g_Map[x][y].passable = false;
			g_Map[x][y].weight+=1.0;
			g_Map[x][y].weight *= 5.0;
		}

	InitMoveData ();
}

void RenderEdges (CDIBSurface& dest,int dx)
{
	int x,y;

	for (y=0;y<MAPCELLCOUNT;y++)
		for (x=0;x<MAPCELLCOUNT;x++)
		{
			g_MapCells[x][y].RenderEdges (dest,dx);
		}
}

int SquareInfo (int x,int y)
{
	return Passable (x,y);
}

void GenMap (MAPELEMENT map[MAPSIZE][MAPSIZE],int x1,int y1,int x2,int y2)
{
	int midx,midy;
	double v1,v2,v3,v4;
	double rmax;


	if (x2 <= x1)
		return;
	if (y2 <= y1)
		return;

	midx = (x1+x2)/2;
	midy = (y1+y2)/2;

	rmax = RAND_MAX;
	if (map[midx][y1].weight == 0.0)
	{
		v1 = map[midx][y1].weight = ((map[x1][y1].weight + map[x2][y1].weight) / 2.0) + (((rand()/rmax)-0.5)*((x2-x1)/MAPX));
		if (v1<0)
			map[midx][y1].weight = 0;
		if (v1 > 1.0)
			map[midx][y1].weight = 1.0;
	}
	else v1	= map[midx][y1].weight;

	if (map[midx][y2].weight ==0.0)
	{
		v2 = map[midx][y2].weight = ((map[x1][y2].weight + map[x2][y2].weight) / 2.0) + (((rand()/rmax)-0.5)*((x2-x1)/MAPX));
		if (v2<0)
			map[midx][y2].weight = 0;
		if (v2 > 1.0)
			map[midx][y2].weight = 1.0;
	}
	else v2	= map[midx][y2].weight;

	if (map[x1][midy].weight == 0.0)
	{
		v3 = map[x1][midy].weight = ((map[x1][y1].weight + map[x1][y2].weight) / 2.0) + (((rand()/rmax)-0.5)*((y1-y2)/MAPY));
		if (v3<0)
			map[x1][midy].weight = 0;
		if (v3 > 1.0)
			map[x1][midy].weight = 1.0;
	}
	else v3	= map[x1][midy].weight;

	if (map[x2][midy].weight == 0.0)
	{
		v4 = map[x2][midy].weight = ((map[x2][y1].weight + map[x2][y2].weight) / 2.0) + (((rand()/rmax)-0.5)*((y1-y2)/MAPY));
		if (v4<0)
			map[x2][midy].weight = 0;
		if (v4 > 1.0)
			map[x2][midy].weight = 1.0;
	}
	else v4	= map[x2][midy].weight;


	if (map[midx][midy].weight == 0.0)
	{
		map[midx][midy].weight = ((v1+v2+v3+v4)/4.0) + 2*(((rand()/rmax)-0.5)*((y1-y2)/MAPY));
		if (map[midx][midy].weight<0)
			map[midx][midy].weight = 0;
		if (map[midx][midy].weight > 1.0)
			map[midx][midy].weight = 1.0;
	}
/*
	if (midx == x1)
		return;
	if (midx == x2)
		return;
	if (midy == y1)
		return;
	if (midy == y2)
		return;
*/
	if ((midx != x2) && (midx != x1))
	{
		GenMap (map,x1,y1,midx,midy);
		GenMap (map,midx,y1,x2,midy);
	}
	if ((midy != y1) && (midy != y2))
	{
		GenMap (map,x1,midy,midx,y2);
		GenMap (map,midx,midy,x2,y2);
	}
}










/*
float GetOneCost (int sx,int sy,int dx,int dy)
{
	int deltax,deltay;
	float toret;

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

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

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



/*
CMapNode::CMapNode (void)
{
	m_Left = NULL;
	m_Right = NULL;
	m_Bottom = NULL;
	m_Top = NULL;

	int i;

	for (i=0;i<PASS_NUM;i++)
	{
		m_Passable[i] = true;
	}

	m_PassAverage = 0;
	m_WeightAverage = 0;

	m_MapQuad = NULL;
	m_MapQuad = new CMapQuadNode (NULL,this);
	m_MapQuad->Left (0);
	m_MapQuad->Top (0);
	m_MapQuad->Right (MAPSIZE-1);
	m_MapQuad->Bottom (MAPSIZE-1);
	m_MapQuad->SubDivide ();
}

CMapNode::~CMapNode (void)
{
	delete m_Left;
	delete m_Right;
	delete m_Bottom;
	delete m_Top;
}

void CMapNode::RenderData (CDIBSurface& dest,int dx)
{
	m_MapQuad->RenderData (dest,dx);
}

void CMapNode::RenderEdges (CDIBSurface& dest,int dx)
{
	m_MapQuad->RenderEdges (dest,dx);
}

CMapQuadNode::CMapQuadNode (CMapQuadNode *parent,CMapNode *root)
{
	m_Root = root;
	m_Parent = parent;
	m_Left = m_Right = m_Bottom = m_Top = 0;
	m_Child[0] = NULL;
	m_Child[1] = NULL;
	m_Child[2] = NULL;
	m_Child[3] = NULL;

	int i;

	for (i=0;i<PASS_NUM;i++)
	{
		m_Passable[i] = true;
	}

	m_PassAverage = 0;
	m_WeightAverage = 0;
}

CMapQuadNode::~CMapQuadNode (void)
{
}

void CMapQuadNode::RenderData (CDIBSurface& dest,int dx)
{
	const int CELLSIZE=2;

	if (m_Child[0])
	{
		m_Child[0]->RenderData (dest,dx);
		m_Child[1]->RenderData (dest,dx);
		m_Child[2]->RenderData (dest,dx);
		m_Child[3]->RenderData (dest,dx);
	}
	else
	{
		int color = 255.0 * ((m_WeightAverage/5.0)-1.0);
		dest.Fill (dx+m_Left*CELLSIZE,m_Top*CELLSIZE,dx+(m_Right+1)*CELLSIZE,(m_Bottom+1)*CELLSIZE,RGB (color,color,color));
	}
}

void CMapQuadNode::RenderEdges (CDIBSurface& dest,int dx)
{
	const int CELLSIZE=2;

	if (m_Child[0])
	{
		m_Child[0]->RenderEdges (dest,dx);
		m_Child[1]->RenderEdges (dest,dx);
		m_Child[2]->RenderEdges (dest,dx);
		m_Child[3]->RenderEdges (dest,dx);
	}
	else
	{
		int x,y;

		for (x=m_Left;x<m_Right;x++)
		{
			dest.Fill (dx+x*CELLSIZE,m_Top*CELLSIZE,dx+(x+1)*CELLSIZE,(m_Top+1)*CELLSIZE,RGB (0,255,0));
//			dest.Fill (x*CELLSIZE,m_Bottom*CELLSIZE,(x+1)*CELLSIZE,(m_Bottom+1)*CELLSIZE,RGB (0,255,0));
		}
		for (y=m_Top;y<m_Bottom;y++)
		{
			dest.Fill (dx+m_Left*CELLSIZE,y*CELLSIZE,dx+(m_Left+1)*CELLSIZE,(y+1)*CELLSIZE,RGB (0,255,0));
//			dest.Fill (m_Right*CELLSIZE,y*CELLSIZE,(m_Right+1)*CELLSIZE,(y+1)*CELLSIZE,RGB (0,255,0));
		}
	}
}

bool CMapQuadNode::SubDivide (void)
{
	int midx,midy,count;
	bool flag;

	flag = true;
	if ((m_Right-m_Left)<=MAXDEPTH)
		flag = false;
	if ((m_Bottom-m_Top)<=MAXDEPTH)
		flag = false;

	assert (m_Left>=0);
	assert (m_Top>=0);
	assert (m_Right <=MAPSIZE);
	assert (m_Bottom <= MAPSIZE);
	assert (m_Left < m_Right);
	assert (m_Bottom > m_Top);

	midy = (m_Bottom-m_Top+1)/2;
	midx = (m_Right-m_Left+1)/2;
	if (flag)
	{
		m_Child[0] = new CMapQuadNode (this,m_Root);
		m_Child[1] = new CMapQuadNode (this,m_Root);
		m_Child[2] = new CMapQuadNode (this,m_Root);
		m_Child[3] = new CMapQuadNode (this,m_Root);

		m_Child[TOPLEFT_CHILD]->SetSize (m_Left,m_Top,m_Left+midx,m_Top+midy);
		m_Child[BOTTOMLEFT_CHILD]->SetSize (m_Left,m_Top+midy+1,m_Left+midx,m_Bottom);
		m_Child[TOPRIGHT_CHILD]->SetSize (m_Left+midx+1,m_Top,m_Right,m_Top+midy);
		m_Child[BOTTOMRIGHT_CHILD]->SetSize (m_Left+midx+1,m_Top+midy+1,m_Right,m_Bottom);


		m_Child[0]->SubDivide ();
		m_Child[1]->SubDivide ();
		m_Child[2]->SubDivide ();
		m_Child[3]->SubDivide ();
	}
	else
	{
		m_Child[0] = NULL;
		m_Child[1] = NULL;
		m_Child[2] = NULL;
		m_Child[3] = NULL;
	}


	m_PassAverage = 0;
	m_WeightAverage = 0;
	count = 0;
	for (midx = m_Left;midx<=m_Right;midx++)
	{
		for (midy=m_Top;midy<=m_Bottom;midy++)
		{
			count++;
			m_WeightAverage+=g_Map[midx][midy].weight;
			if (g_Map[midx][midy].passable)
			{
				m_PassAverage++;
			}
		}
	}
	m_PassAverage /= count;
	m_WeightAverage /= count;


	return true;
}
*/

#if 0
double CMapQuadNode::CalcAverageCostSplit (int where,bool left )
{
	int x,y,z;
	int lx,hx,ly,hy;
	double current;
	int count;

	assert (where < (m_LeftSplit?m_Bottom:m_Right));
	assert (where > (m_LeftSplit?m_Top:m_Left));
	assert (m_Left>=0);
	assert (m_Top>=0);
	assert (m_Right <=MAPSIZE);
	assert (m_Bottom <= MAPSIZE);
	assert (m_Left < m_Right);
	assert (m_Bottom > m_Top);
	if (m_LeftSplit)
	{
		lx = m_Left;
		hx = m_Right;
		if (left)
		{
			ly = m_Top;
			hy = m_Top+where;
		}
		else
		{
			ly = m_Top+where+1;
			hy = m_Bottom;
		}
	}
	else
	{
		ly = m_Top;
		hy = m_Bottom;
		if (left)
		{
			lx = m_Left;
			hx = m_Left+where;
		}
		else
		{
			lx = m_Left+where+1;
			hx = m_Right;
		}
	}
	count = 0;
	current = 0.0;
	for (x=lx;x<=hx;x++)
	{
		for (y=ly;y<=hy;y++)
		{
			for (z=0;z<OFFSIZE;z++)
			{
				double cost;

				cost = g_MapTree->GetOneCost (x,y,x+offset[z][0],y+offset[z][1]);
				if (cost < 0)
					cost = 10000.0;
				current+=cost;
				count++;
			}
		}
	}
	return current / ((double)count);
}

double CMapQuadNode::AnalyzeSplit (int where,bool left)
{
	double average;
	int x,y,z;
	int lx,hx,ly,hy;
	double current;
	int count;

	assert (where < (m_LeftSplit?m_Bottom:m_Right));
	assert (where > (m_LeftSplit?m_Top:m_Left));

	assert (m_Left>=0);
	assert (m_Top>=0);
	assert (m_Right <=MAPSIZE);
	assert (m_Bottom <= MAPSIZE);
	assert (m_Left < m_Right);
	assert (m_Bottom > m_Top);

	average = CalcAverageCostSplit (where,left);

	if (m_LeftSplit)
	{
		lx = m_Left;
		hx = m_Right;
		if (left)
		{
			ly = m_Top;
			hy = m_Top+where;
		}
		else
		{
			ly = m_Top+where+1;
			hy = m_Bottom;
		}
	}
	else
	{
		ly = m_Top;
		hy = m_Bottom;
		if (left)
		{
			lx = m_Left;
			hx = m_Left+where;
		}
		else
		{
			lx = m_Left+where+1;
			hx = m_Right;
		}
	}
	count = 0;
	current = 0.0;
	for (x=lx;x<=hx;x++)
	{
		for (y=ly;y<=hy;y++)
		{
			for (z=0;z<OFFSIZE;z++)
			{
				double cost = g_MapTree->GetOneCost (x,y,x+offset[z][0],y+offset[z][1]);
				if (cost < 0)
					cost = 10000.0;

				current+= ((cost-average) * (cost-average));
				count++;
			}
		}
	}
	return current / ((double)count);
}

int CMapQuadNode::ChooseSplit (void)
{
	int mid,min;
	double minvalue;
	double temp;
	double splits[MAPSIZE][2];
	double cost[MAPSIZE];

	assert (m_Left>=0);
	assert (m_Top>=0);
	assert (m_Right <=MAPSIZE);
	assert (m_Bottom <= MAPSIZE);
	assert (m_Left < m_Right);
	assert (m_Bottom > m_Top);

	for (mid = 0;mid<MAPSIZE;mid++)
	{
		splits[mid][0] = 0.0;
		splits[mid][1] = 0.0;
	}

	min = -1;
	minvalue = DBL_MAX;
	if (m_LeftSplit)
	{
		for (mid = (m_Top+MAXDEPTH4);mid<=(m_Bottom-MAXDEPTH4);mid++)
		{
			splits[mid][0] = AnalyzeSplit (mid,0);
			splits[mid][1] = AnalyzeSplit (mid,1);
			cost[mid] = splits[mid][0] + splits[mid][1];
			temp = ((double)(mid-m_Top)/(double) (m_Bottom - m_Top));
			temp *= PI;
//			temp -= (PI/2.0);
			temp = sin(temp);
			temp = 1.0 - temp;
			cost[mid] *= temp;
			if (cost[mid] < minvalue)
			{
				min = mid;
				minvalue = cost[mid];
			}
		}
	}
	else
	{
		for (mid = (m_Left+MAXDEPTH4);mid<=(m_Right-MAXDEPTH4);mid++)
		{
			splits[mid][0] = AnalyzeSplit (mid,0);
			splits[mid][1] = AnalyzeSplit (mid,1);
			cost[mid] = splits[mid][0] + splits[mid][1];
			temp = ((double)(mid-m_Left)/(double) (m_Right - m_Left));
			temp *= PI;
//			temp -= (PI/2.0);
			temp = sin(temp);
			temp = 1.0 - temp;
			cost[mid] *= temp;

//			temp = (((mid-m_Left)/(m_Right - m_Left)) * PI)-(PI/2.0);
//			temp = 1.0 - sin (temp);
			cost[mid] *= temp;
			if (cost[mid] < minvalue)
			{
				min = mid;
				minvalue = cost[mid];
			}
		}
	}
	if (min < 0)
	{
		if (m_LeftSplit)
			mid = (m_Bottom-m_Top+1)/2;
		else
			mid	= (m_Right-m_Left+1)/2;
	}
	else
		mid	= min;

	assert (mid < (m_LeftSplit?m_Bottom:m_Right));
	assert (mid > (m_LeftSplit?m_Top:m_Left));

	return mid;
}




bool CMapNode::CalcPath (std::stack<PATHELEMENT> *dest,CPoint start,CPoint end,int depth,bool followshort)
{
	int i,j;
	FibHeap calc;
	CALCELEMENT *temp;
	CALCELEMENT *shortdes = NULL;
	int lastdepth;

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

	while (!dest->empty ())
		dest->pop ();

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

	temp = new CALCELEMENT (start.x,start.y);
	pathmap[start.x][start.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,end.x,end.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 == end.x) && (y == end.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 = end.x;
		cury = end.y;
	}
	dest->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);
		dest->push (PATHELEMENT (curx+offset[min][0],cury+offset[min][1]));
		if (((curx+offset[min][0]) == start.x) && ((cury+offset[min][1]) == start.y))
		{
			done = true;
			continue;
		}
		curx += offset[min][0];
		cury += offset[min][1];
	}

//	m_CurPath.push (PATHELEMENT (curdes.x,curdes.y));
	return true;
}

#endif














#if 0
	int i,j;
	FibHeap calc;
	CALCELEMENT *temp;
	CALCELEMENT *shortdes = NULL;
	int lastdepth;

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

	while (!dest->empty ())
		dest->pop ();

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

	temp = new CALCELEMENT (start.x,start.y);
	pathmap[start.x][start.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,end.x,end.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 == end.x) && (y == end.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 = end.x;
		cury = end.y;
	}
	dest->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);
		dest->push (PATHELEMENT (curx+offset[min][0],cury+offset[min][1]));
		if (((curx+offset[min][0]) == start.x) && ((cury+offset[min][1]) == start.y))
		{
			done = true;
			continue;
		}
		curx += offset[min][0];
		cury += offset[min][1];
	}

//	m_CurPath.push (PATHELEMENT (curdes.x,curdes.y));
	return true;
#endif

