#ifndef __MOVEZONEHPP__
#define __MOVEZONEHPP__

#include <stack>
#include <queue>


const int OFFSIZEZONE = 4;

const	int offsetzone[OFFSIZEZONE][3] = {			// third number is the direction l,t,r,b  0,1,2,3
	{-1,0,0},
	{0,-1,1},
	{1,0,2},
	{0,1,3}
	};

const int LEFTEDGEMASK=0x01;
const int TOPEDGEMASK=0x02;
const int RIGHTEDGEMASK=0x04;
const int BOTTOMEDGEMASK=0x08;

const int LEFTEDGE = 0;
const int TOPEDGE = 1;
const int RIGHTEDGE = 2;
const int BOTTOMEDGE = 3;

struct AREADATA
{
	union
	{
		struct
		{
			unsigned left_connect:1;
			unsigned top_connect:1;
			unsigned right_connect:1;
			unsigned bottom_connect:1;
			unsigned passable:1;
		};
		unsigned char flags;
	};
	int info;
	float weight;
	bool operator< (const AREADATA& p1) const
	{
		return flags < p1.flags;
	}
	bool operator== (const AREADATA& p1) const
	{
		return flags == p1.flags;
	}
	bool operator!= (const AREADATA& p1) const
	{
		return flags != p1.flags;
	}
};

struct PATHZONEELEMENT
{
	int sx,sy;										// cell id
	int dx,dy;										// cell id
	int sarea,darea;							// sarea is the start area for the edge, darea is the area for the edge on the other cell
																// sarea may not be the area that you entered the cell on
	int dir;
	bool operator< (const PATHZONEELEMENT& p1) const;
	bool operator== (const PATHZONEELEMENT& p1) const;
	PATHZONEELEMENT (int nsx,int nsy,int ndx,int ndy,int nsarea,int ndarea,int ndir) {sx=nsx;sy=nsy;dx=ndx;dy=ndy;sarea=nsarea;darea=ndarea;dir=ndir;}
	PATHZONEELEMENT (void) { sx = -1; sy = -1; dx = -1; dy = -1; sarea = 0;darea = 0;dir = -1;}
};

const int MAPCELLSIZE = 16;
const int DRAWCELLSIZE=2;

bool SetWeight (int sx,int sy,int offset,int areaentry,int areaexit,float newweight);			// offset is the index into the offsetzone table
float GetWeight (int sx,int sy,int offset,int areaentry,int areaexit);		// offset is the index into the offsetzone table
float GetWeight (int sx,int sy,int dx,int dy,int areaentry,int areaexit);		// offset is the index into the offsetzone table
void 	FindLowerEdge (int dx,int dy,int endarea,int& sx,int& sy,int& curarea,int& dir,int startx,int starty,int startarea);	// will get the lowest connection from dx,dy to sx,sy
																																							// used to find path in reverse order
																																							// will return startx,starty,startarea if there is a choice for this connection

CPoint GlobalToCellIndex (CPoint src);
bool CalcPathZone (std::stack<PATHZONEELEMENT> *dest,CPoint start,CPoint end);		// in the zone
float GetOneCostZone (int sx,int sy,int offset,int areaentry,int areaexit,int newarea);		// offset is the index into the offsetzone table
float PathCostGuessZone (int sx,int sy,int dx,int dy);
void InitMoveZoneData (void);
bool AddCalcElements (FibHeap& calc,int sx,int sy,int area,float startweight);

class CALCZONEELEMENT : public FibHeapNode
{
private:
	static FixedHeap<CALCZONEELEMENT> m_EmptyCalcZoneElement;

public:
	int sx,sy;
	int dx,dy;
	int sarea,darea;
	int dir;
	CALCZONEELEMENT (int nsx,int nsy,int ndx,int ndy,int nsarea,int ndarea,int ndir) {sx=nsx;sy=nsy;dx=ndx;dy=ndy;sarea=nsarea;darea=ndarea;dir = ndir;}
	CALCZONEELEMENT (void) { sx = -1; sy = -1; dx = -1; dy = -1; sarea = 0;darea = 0;dir = -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); }
};

//const int MAPCELLCOUNT = ((MAPSIZE/(MAPCELLSIZE-1))+((MAPSIZE%(MAPCELLSIZE-1))?1:0));
const int MAPCELLCOUNT = 17;
const int MAXAREAS=16;


class CMapCell
{
	friend float GetOneCostZone (int sx,int sy,int offset,int areaentry,int areaexit,int newarea);		// offset is the index into the offsetzone table
	friend bool CalcPathZone (std::stack<PATHZONEELEMENT> *dest,CPoint start,CPoint end);		// in the zone
	friend bool AddCalcElements (FibHeap& calc,int sx,int sy,int area,float startweight);


private:
	
	int m_Left,m_Right,m_Bottom,m_Top;
	float m_WeightAverage;
	float m_PassAverage;
	int m_Areas[MAPCELLSIZE][MAPCELLSIZE];
	AREADATA m_AreaData[MAXAREAS];								// first four bits are edge connection data l,t,r,b  0,1,2,3

	void CalcArea (int sx,int sy,int id,int info);

public:
	CMapCell (void);
	~CMapCell (void);
/*
	void Left (int value) { m_Left = value; }
	void Right (int value) { m_Right = value; }
	void Top (int value) { m_Top = value; }
	void Bottom (int value) { m_Bottom = value; }
*/
	int Left (void) { return m_Left; }
	int Right (void) { return m_Right; }
	int Top (void) { return m_Top; }
	int Bottom (void) { return m_Bottom; }
	float AverageWeight (void) { return m_WeightAverage; }

	int AreaGlobal (int globalx,int globaly)								// takes global coordinates
	{ return m_Areas[globalx-m_Left][globaly-m_Top]; }

	int AreaLocal (int localx,int localy)									// takes coordinates local to this cell
	{ return m_Areas[localx][localy]; }

	AREADATA AreaData (int area)
	{ return m_AreaData[area]; }


	void CalcWeight (void);
	void CalcEdges (void);
	void CalcInterior (void);
	void SetSize (int left,int top,int right,int bottom)
	{
		assert (left>=0);
		assert (left<MAPSIZE);
		assert (right>=0);
		assert (right<MAPSIZE);
		assert (top>=0);
		assert (top<MAPSIZE);
		assert (bottom>=0);
		assert (bottom<MAPSIZE);
		assert (left<right);
		assert (top<bottom);	
		m_Left = left;
		m_Top = top;
		m_Right = right;
		m_Bottom = bottom;
	}
	
	void RenderEdges (CDIBSurface& dest,int dx);
};

extern CMapCell g_MapCells[MAPCELLCOUNT][MAPCELLCOUNT];


#endif

