#include <stdio.h>
#include <stdlib.h>

#include "grid.h"

int grid::makeGrid(vector<pin> &vp, int _width, int _height, int max_net)
{
	int i,j;
	width=_width;
	height=_height;
	
	ge=(grid_element ***) malloc(sizeof(grid_element **)*4);
	if (!ge) return -1;
	gpv=(grid_prev_visited ***) malloc(sizeof(grid_element **)*4);
	if (!gpv) return -1;
	gc=(grid_cost ***) malloc(sizeof(grid_cost **)*4);
	if (!gc) return -1;
	for (i=0;i<4;i++) {
		ge[i]=(grid_element **) malloc(sizeof(grid_element*)*(height));
		if (!ge[i]) return -1;
		gpv[i]=(grid_prev_visited **) malloc(sizeof(grid_element *)*height);
		if (!gpv[i]) return -1;
		gc[i]=(grid_cost **) malloc(sizeof(grid_cost *)*height);
		if (!gc[i]) return -1;
		for (j=0;j<height;j++) {
			ge[i][j]=(grid_element *) calloc(sizeof(grid_element)*(width),1);
			if (!ge[i][j]) return -1;
			gpv[i][j]=(grid_prev_visited *)
				calloc(sizeof(grid_prev_visited)*(width+1)/2,1);
			if (!gpv[i][j]) return -1;
			gc[i][j]=(grid_cost *)
				calloc(sizeof(grid_cost)*(width+1)/2,1);
			if (!gc[i][j]) return -1;
		}
	}

	cell c;
	for (i=0;i<vp.size();i++) {
		c.x=vp[i].x;
		c.y=vp[i].y-1;
		c.l=0;
		addCellToPath(c,-1);
		c.l=1;
		addCellToPath(c,-1);
		c.y++;
		addCellToPath(c,-1);
		c.l=0;
		addCellToPath(c,-1);
		c.y++;
		addCellToPath(c,-1);
		c.l=1;
		addCellToPath(c,-1);
	}
	
	net_costs.resize(max_net+1);
	for (i=0;i<=max_net;i++) net_costs[i]=0;
	return 0;
}

int grid::clearVisitedAndPrev()
{
	unsigned int x,y,l;
	for (l=0;l<4;l++) {
		for  (y=0;y<height;y++) {
			memset(gpv[l][y],0,sizeof(grid_prev_visited)*(width+1)/2);
		}
	}
	return 0;
}

/*
void grid::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;
	}
}
*/

int grid::cellCostAccurate(cell c)
{
	routed_net *rn;
	int cost=0;

	for (rn=ge[c.l][c.y][c.x].rn;rn;rn=rn->next) {
		if (rn->net_id==-1) return 1<<30;
		cost+=net_costs[rn->net_id];
	}
	
	return cost;
}

/*
int grid::setPrev(cell c, cell nc, 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);
	}
	return 0;
}
*/

cell grid::getPrev(cell c)
{
	cell ret;
	int dir;
	if (c.x&0x01) {
		dir=gpv[c.l][c.y][c.x/2].stuff&0x07;
	} else {
		dir=(gpv[c.l][c.y][c.x/2].stuff&0x70)>>4;
	}

	if (dir==7) {
		ret.x=-1;
		ret.y=-1;
		ret.l=0;
		return ret;
	}
	// now we just need to work backwards from the direction
	switch (dir) {
	case 0:
		dir=2;
		break;
	case 1:
		dir=3;
		break;
	case 2:
		dir=0;
		break;
	case 3:
		dir=1;
		break;
	case 4:
		dir=5;
		break;
	case 5:
		dir=4;
		break;
	}
	if (!getNeighbor(c,dir,ret)) {
		fprintf(stderr,"getPrev failed bigtime!\n");
		fprintf(stderr,"c=(%i,%i,%i) dir=%i\n",
			c.x,c.y,c.l,dir);
		exit(1);
	}
	return ret;
}

void grid::resetCost(cell c)
{
	int t=cellCostAccurate(c);
	if (t<(1<<30)) {
		t=min((int)((float)t/cost_quantizer),14);
	} else t=15;
	if (c.x&0x01) {
		gc[c.l][c.y][c.x/2].cost&=0xF0;
		gc[c.l][c.y][c.x/2].cost|=t;
	} else {
		gc[c.l][c.y][c.x/2].cost&=0x0F;
		gc[c.l][c.y][c.x/2].cost|=(t<<4);
	}
}

void grid::addCellToPath(cell &c, int net)
{
	routed_net *rn;
	// first check to see if this net is already here
	for (rn=ge[c.l][c.y][c.x].rn;rn && rn->net_id!=net;rn=rn->next);
	if (rn) return; // this net already exists
	rn=new routed_net;
	if (!rn) {
		fprintf(stderr,"addNetToPath: Out of Memory!\n");
		exit(1);
	}
	rn->net_id=net;
	rn->next=ge[c.l][c.y][c.x].rn;
	ge[c.l][c.y][c.x].rn=rn;
	resetCost(c);
}

int grid::addNetToPath(list<cell> &lc, int net)
{
	list<cell>::iterator lci;

	for (lci=lc.begin();lci!=lc.end();++lci) {
		addCellToPath(*lci,net);
	}
	return 0;
}


int grid::removeNetFromPath(list<cell> &lc, int net)
{
	list<cell>::iterator lci;

	for (lci=lc.begin();lci!=lc.end();++lci) {
		routed_net *rn,**parent;
		parent=&ge[lci->l][lci->y][lci->x].rn;
		for (rn=*parent;rn && rn->net_id!=net;parent=&rn->next,rn=rn->next);
		if (!rn) {
			//fprintf(stderr,"removeNetFromPath: net %i not found"
			//	"at cell (%i,%i,%i)\n", net,lci->x,lci->y,lci->l);
		} else {
			*parent=rn->next;
			delete rn;
			resetCost(*lci);
		}
	}
	return 0;
}

int grid::countNets(cell c)
{
	int count=0;
	routed_net *rn;
	for (rn=ge[c.l][c.y][c.x].rn;rn;rn=rn->next,count++) if (rn->net_id==-1) count--;
	return count;
}

int grid::isPin(cell c)
{
	routed_net *rn;
	for (rn=ge[c.l][c.y][c.x].rn;rn;rn=rn->next) if (rn->net_id==-1) return 1;
	return 0;
}

/*
int grid::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];
}
*/

// take the costs in the net_costs vector, and finds a good set of
// 16 different costs to use with them, then insert those into the
// quantize_costs array, go through every cell, and see what cost
// should be used for it
int grid::quantizeCosts()
{
	// to find a good set of quantized costs, lets look through
	// all the cells quick like and find the max cost, then we
	// will just make even divisions around it
	int t;
	cell c;
	int max_cost=0;
	for (c.l=0;c.l<4;c.l++) {
		for (c.y=0;c.y<height;c.y++) {
			for (c.x=0;c.x<width;c.x++) {
				t=cellCostAccurate(c);
				if (t<(1<<30) && t>max_cost) max_cost=t;
			}
		}
	}
	// now that we have the max_cost, divide it by 16 and set
	// up the schedule
	fprintf(stderr,"quantizeCosts max_cost=%i\n",max_cost);
	if (max_cost<14) max_cost=14; // don't want a degenerate case
	cost_quantizer=((float)max_cost/14.0);
	
	for (t=0;t<15;t++) {
		quantized_costs[t]=(int)(((float)t)*cost_quantizer);
		//fprintf(stderr,"quantized_costs[%i]=%i\n",t,quantized_costs[t]);
	}
	quantized_costs[15]=1<<30;

	// now we need to go through every cell, and assign a shortcut
	// cost for it
	for (c.l=0;c.l<4;c.l++) {
		for (c.y=0;c.y<height;c.y++) {
			for (c.x=0;c.x<width;c.x++) {
				resetCost(c);
				//if (t>0) fprintf(stdout,"(%i,%i,%i) cost(t)=%i"
				//		 " stuff=%i\n",
				//		 c.x,c.y,c.l,t,
				//		 gc[c.l][c.y][c.x].cost);
			}
		}
	}
	return 0;
}