#ifndef GRID_H
#define GRID_H

#include "parser.h"

struct routed_net
{
	int net_id;
	struct routed_net *next;
};

struct grid_element
{
	routed_net *rn;
};

struct grid_cost
{
	// this is multiplexed as well, cells with x&0x01==1 have their
	// cost in the low nybble, other cells have it in the high nybble
	unsigned char cost;
};

struct grid_prev_visited
{
	// 3 bits for previous direction
	// 4th bit for visited
	// two grid spaces per element
	unsigned char stuff;
};

class grid
{
 private:
	// this array holds the grid structure
	grid_element ***ge;
	grid_prev_visited ***gpv;
	grid_cost ***gc;
	float cost_quantizer;
	int max_cost;

	// here we store how much each net costs to cross for
	// a given run, it is indexed by net id
	
 public:
	vector<int> net_costs;
	int quantized_costs[256];
	int width;
	int height;

	void resetCost(cell c);
	int quantizeCosts();
	int countNets(cell c);
	int isPin(cell c);
	int makeGrid(vector<pin> &vp, int width, int height, int max_net);
	int clearVisitedAndPrev();
	inline void setVisited(cell c){
		if (c.x&0x01) {
			gpv[c.l][c.y][c.x/2].stuff|=0x08;
		} else {
			gpv[c.l][c.y][c.x/2].stuff|=0x80;
		}
	}
	
	inline bool getVisited(cell c){
		if (c.x&0x01) {
			if (gpv[c.l][c.y][c.x/2].stuff&0x08) return true;
			return false;
		} else {
			if (gpv[c.l][c.y][c.x/2].stuff&0x80) return true;
			return false;
		}
	}
	inline int cellCost(cell c) {
		int l;
		if (c.x&0x1) {
			l=gc[c.l][c.y][c.x/2].cost&0x0f;
		} else {
			l=(gc[c.l][c.y][c.x/2].cost&0xf0)>>4;
		}
		return quantized_costs[l];
	}
		
		
	int cellCostAccurate(cell c);
	/*
	inline int getNet(cell c){
		return ge[c.l][c.y][c.x].net;
	}
	*/
	inline char getPrevDir(cell c) {
		if (c.x&0x01) {
			return (gpv[c.l][c.y][c.x/2].stuff&0x07);
		} else {
			return (gpv[c.l][c.y][c.x/2].stuff&0x70)>>4;
		}
	}
	
	inline void setPrev(cell c, cell prev, int dir) {
		if (c.x&0x01) {
			gpv[c.l][c.y][c.x/2].stuff&=0xF8;
			gpv[c.l][c.y][c.x/2].stuff|=dir;
		} else {
			gpv[c.l][c.y][c.x/2].stuff&=0x8F;
			gpv[c.l][c.y][c.x/2].stuff|=(dir<<4);
		}
	}
		
	cell getPrev(cell c);

	void addCellToPath(cell &lc, int net);
	int addNetToPath(list<cell> &lc, int net);
	int removeNetFromPath(list<cell> &lc, int net);

	int getNeighbor(cell c, int dir, cell &nc){
		int nx,ny,nz;
		switch (dir) {
		case 0: // north
			nx=c.x; ny=c.y-1; nz=c.l;
			break;
		case 1: // east
			nx=c.x+1; ny=c.y; nz=c.l;
			break;
		case 2: // south
			nx=c.x; ny=c.y+1; nz=c.l;
			break;
		case 3: // west
			nx=c.x-1; ny=c.y; nz=c.l;
			break;
		case 4: // up
			nx=c.x; ny=c.y; nz=c.l+1;
			break;
		case 5: // down
			nx=c.x; ny=c.y; nz=c.l-1;
			break;
		}
		if (nx<0 || nx>=width) return 0;
		if (ny<0 || ny>=height) return 0;
		if (nz<0 || nz>3) return 0;

		nc.x=nx; nc.y=ny; nc.l=nz;
		return 1;
	}
};


#endif