#!/bin/awk -f
#
#	router [ variable=value] [files]
#
#	Sets distribute and threshhold values.
#
#	Supports the following input forms:
#		dump		nearly any version, deity or player
#		neweff		Chainsaw 3.0
#		prod		any version?  player only
#
#	Variables:		(see BEGIN for defaults)
#		etu		number of etus
#		myno		Country number
#		myname		Country name	(can obtain from nation)
#
#		-harmless


function num(str) {
  return substr(str,1,length(str)-1);
}


#
#	Turns a letter into a name.  Tries the first letter, if that
#	isn't a commodity, tries the last letter (useful for ship, unit,
#	and prod outputs)
#
function commstr(str) {
  char=substr(str,1,1);
  if (char == "c") return "civ";
  if (char == "m") return "mil";
  if (char == "u") return "uw";
  if (char == "f") return "food";
  if (char == "s") return "shell";
  if (char == "g") return "gun";
  if (char == "p") return "pet";
  if (char == "i") return "iron";
  if (char == "d") return "dust";
  if (char == "b") return "bar";
  if (char == "o") return "oil";
  if (char == "l") return "lcm";
  if (char == "h") return "hcm";
  if (char == "r") return "rad";

  char=substr(str,length(str),1);
  if (char == "c") return "civ";
  if (char == "m") return "mil";
  if (char == "u") return "uw";
  if (char == "f") return "food";
  if (char == "s") return "shell";
  if (char == "g") return "gun";
  if (char == "p") return "pet";
  if (char == "i") return "iron";
  if (char == "d") return "dust";
  if (char == "b") return "bar";
  if (char == "o") return "oil";
  if (char == "l") return "lcm";
  if (char == "h") return "hcm";
  if (char == "r") return "rad";


  return "";
}




BEGIN {
  SUBSEP=",";
  if (etu=="") etu=16;
  if (myno=="") myno=41;
  if (myname=="") myname="harmless";
  if (showall=="") showall=0;

  mode="";
  stat=0;

  maxciv=maxuw=999;

  eatrate = 0.001;
  babyeat = 0.012;
  obrate  = 0.005;
  uwbrate = 0.0025;
  easytech= 1.0;
  logtech = 2.5;


  weight["civ"]=1;	packing["civ"]=1;
  weight["mil"]=1;	packing["mil"]=1;
  weight["uw"]=2;	packing["uw"]=1;
  weight["food"]=1;	packing["food"]=10;
  weight["shell"]=1;	packing["shell"]=10;
  weight["gun"]=10;	packing["gun"]=10;
  weight["pet"]=1;	packing["pet"]=10;
  weight["iron"]=1;	packing["iron"]=10;
  weight["dust"]=5;	packing["dust"]=10;
  weight["bar"]=50;	packing["bar"]=5;
  weight["oil"]=1;	packing["oil"]=10;
  weight["lcm"]=1;	packing["lcm"]=10;
  weight["hcm"]=1;	packing["hcm"]=10;
  weight["rad"]=8;	packing["rad"]=10;
}



#
#	Single line parsings
#
/Technology\.* * .+ * Research\.* .+/ {
  tech = $2;
  research= $4;
  next;
}

/Education\.* * .+ * Happiness\.* .+/ {
  edu = $2;
  happiness= $4;
  next;
}

/Max population : [0-9]+/ { maxpop = $2 };

/Max safe population for civs\/uws:/ { 
  split($6,a,"/");
  maxciv=a[1];
  maxuw=a[2];
  next;
}



#
#	Version parsing stuff
#
/An update consists of [0-9]+ empire time units/ {  etu = $5; next; }
/No food is needed!!/ { nofood=1; next; }
/1000 civilians will give birth to [0-9\.]+ babies per etu./ { 
  obrate = $7/1000; next;
}
/In one time unit, 1000 people eat [0-9\.]+ units of food./ {
  eatrate = $8/1000; next;
}
/1000 babies eat [0-9\.]+ units of food becoming adults./ {
  babyeat = $4/1000; next;
}
/1000 uncompensated workers will give birth to [0-9\.]+ babies./ {
  uwbrate = $8/1000; next;
}
/Tech Buildup is limited to logarithmic growth \(base .+\) after .+/ {
  easytech = substr($11,1,length($11)-1);
  logtech = $9;
}




#
#	Headers for various multi-line read modes
#
/ *DUMP SECTOR/ { mode="dump"; stat=1; next; }
/CENSUS/ { mode="census"; stat=1; next; }
/COMMODITIES/ { mode="comm"; stat=1; next; }
/PRODUCTION/ {mode="prod"; stat=1; next; }
/EFFICIENCY SIMULATION/ {mode="neweff"; stat=1; next; }



#
#	Parse continuation lines of multi-line modes
#
{

  if (mode=="dump") {
    if (stat==1) {
      for (i=1;i<=NF;i++) header[i]=$i;
      nheader=NF;
      stat=2;
      dsects=0;
      for (i in order) delete order[i];
      next;
    } else if (NF != nheader) {
      stat=0;
      mode="";
    } else if (stat==2) {
      for (i=1;i<=nheader;i++) temp[header[i]] = $i;
      sect = temp["x"] "," temp["y"];
      if (temp["own"]=="") temp["own"]=myno;
      for (i=1;i<=nheader;i++) val[sect,header[i]] = $i;
      sects[sect]="1";
      order[dsects++] = sect;
      next;
    }
    mode="";
  } else if (mode=="neweff") {
    if (stat==1) {
      if ($0=="   sect  des    projected eff") {
	stat=2;
	next;
      }
    } else if (stat==2 && NF==3) {
      newdes[$1]=$2;
      neweff[$1]=num($3);
      sects[$1]="1";
      next;
    }
    mode="";
  } else if (mode="prod") {
    if (stat==1) {
      if ($0 =="   sect  des eff wkfc will make- p.e. cost  use1 use2 use3  max1 max2 max3  max" ) {
	stat=2;
	next;
      } else {
	mode="";
      }
    } else if (stat==2 && NF >2) {
      sect=$1;
      sects[sect]="1";
      newdes[sect]=$2;
      will[sect]=$5;
      make[sect]=$6;
      if (make[sect]=="bars") make[sect]="bar";
      max[sect]=$(NF);
      if (NF==11) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($10);
      } else if (NF==13) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($11);
	use2[sect]=commstr($10);
	amt2[sect]=num($10);
	max2[sect]=num($12);
      } else if (NF==15) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($12);
	use2[sect]=commstr($10);
	amt2[sect]=num($10);
	max2[sect]=num($13);
	use3[sect]=commstr($11);
	amt3[sect]=num($11);
	max3[sect]=num($14);
      }
      next;
    } else {
      mode="";
      stat=0;
    }
  }
}



#
#	Subroutines that may be used by END {}
#
function movcost(sect,    des, c)
{
    if (sect in mem_movcost) return mem_movcost[sect];
    des=val[sect,"des"];
    if (des == "." || des == "s" || des == "\\") {
        c = 1e9;  # something large
    } else if (des == "^") {
        c = 25;
    } else if (des == "+" || des == "=") {
        c = 1;
    } else {
        c = 2;
    }
    c = (100*c - val[sect,"eff"])/500;
    if (c == 0.0) c = 0.01;  # min_movecost
    return mem_movcost[sect] = c;
}


# create an array cost[] with movement costs to sect
# iterate until it returns zero, or the desired path has been found.
# returns the number of sectors added to the cost[] array.
function buildcost(cost,marked, added,i,j,x,y,dx,dy,tmp)
{
  bestcost=1e9;

  for (i in cost) {
    if (cost[i]<bestcost && !(i in marked)) bestcost=cost[i];
  }
  for (i in cost) if (cost[i]==bestcost) {
    marked[i]=1;

    x=val[i,"x"];
    y=val[i,"y"];

    tmp= (x+2) "," y;
    if (!(tmp in cost) && (tmp in sects)) {
      cost[tmp]=cost[i]+movcost(tmp); added++;
    }
    tmp= (x-2) "," y;
    if (!(tmp in cost) && (tmp in sects)) {
      cost[tmp]=cost[i]+movcost(tmp); added++;
    }
    tmp= (x+1) "," (y+1);
    if (!(tmp in cost) && (tmp in sects)) {
      cost[tmp]=cost[i]+movcost(tmp); added++;
    }
    tmp= (x+1) "," (y-1);
    if (!(tmp in cost) && (tmp in sects)) {
      cost[tmp]=cost[i]+movcost(tmp); added++;
    }
    tmp= (x-1) "," (y+1);
    if (!(tmp in cost) && (tmp in sects)) {
      cost[tmp]=cost[i]+movcost(tmp); added++;
    }
    tmp= ((x-1) "," (y-1));
    if (!(tmp in cost) && (tmp in sects)) {
      cost[tmp]=cost[i]+movcost(tmp); added++;
    }
  }

  return added;
}


function pathcost(from,to,   cost,marked)
{
  cost[from]=0;
  while (!(to in cost) && buildcost(cost,marked)) ;
  if (to in cost) return cost[to];
  else return 1e9;
}

# moving in the other direction
function invcost(from,to,cost)
{
  if ((from in cost) && (to in cost)) 
    return cost[to]-movcost(to)+movcost(from);
  return 9999;
}


#
#	Simulates a production update (no delivery or distribution)
#	( Requires valid neweff data and prod data )
function update( sect)
{
  for (sect in sects) {
    if (!nofood) {
      eat = (val[sect,"civ"]+val[sect,"mil"]+val[sect,"uw"])*etu*eatrate;
      eat += (val[sect,"civ"]*obrate + val[sect,"uw"]*uwbrate)*etu*babyeat;
      val[sect,"food"] -= int(eat);
      net["food"] -= eat;
      delta["food"] -= eat;
      consume["food"] -= eat;
    }
    if (val[sect,"food"]< -1) {
      val[sect,"work"]=0;
      val[sect,"civ"] /= 2;
      val[sect,"mil"] /= 2;
      val[sect,"uw"] /= 2;
    } else {
      val[sect,"civ"] += int(val[sect,"civ"]*etu*obrate);
      val[sect,"uw"] += int(val[sect,"uw"]*etu*uwbrate);
    }
    if (val[sect,"civ"] > maxpop) val[sect,"civ"]=maxpop;
    if (val[sect,"uw"] > maxpop) val[sect,"uw"]=maxpop;
  }

  for (sect in neweff) {
    val[sect,"des"]=newdes[sect];
    val[sect,"eff"]=neweff[sect];
  }
  for (sect in will)
    if (will[sect]+val[sect,make[sect]] > 999)
      val[sect,make[sect]] = 999;
    else {
      val[sect,make[sect]] += will[sect];
  }
  for (sect in use1) val[sect,use1[sect]] -= amt1[sect];
  for (sect in use2) val[sect,use2[sect]] -= amt2[sect];
  for (sect in use3) val[sect,use3[sect]] -= amt3[sect];
}


#
#	Two functions to simulate distribution
#
function distexport( i,sect,dest,expcost,comm,thresh,maxtomove,mobtomove,ware)
{
  for (i=0;i<dsects;i++) {
    sect=order[i];
    dest=val[sect,"dist_x"] "," val[sect,"dist_y"];
    if (dest!=sect) {
      expcost=pathcost(sect,dest)/4;
      ware=((val[sect,"des"]=="w" && val[sect,"eff"]>=60) ||
	    (val[dest,"des"]=="w" && val[dest,"eff"]>=60));
      for (comm in weight) {
	thresh = val[sect,substr(comm,1,1) "_dist"];
	if (thresh && val[sect,comm]>thresh) {
	  maxtomove = val[sect,comm]-thresh;
	  mobtomove = maxtomove*expcost;
	  if (ware) mobtomove /= packing[comm];
	  if (comm=="bar" && val[sect,"des"]=="b" && val[source,"eff"]>=60)
	    mobtomove/=4;
	  if (mobtomove > val[sect,"mob"]) {
	    maxtomove *= val[sect,"mob"]/mobtomove;
	    mobtomove = val[sect,"mob"];
	  }
	  val[dest,comm] += maxtomove;
	  imported[dest,comm] += maxtomove;
	  exported[sect,comm] += maxtomove;
	  val[sect,comm] -= maxtomove;
	  val[sect,"mob"] -= mobtomove;
	  interesting[dest]=1;
	  interesting[sect]=1;
	}
      }
    }
  }
}

function distimport(i,sect,source,expcost,comm,thresh,maxtomove,mobtomove,ware)
{
  for (i=0;i<dsects;i++) {
    sect=order[i];
    source=val[sect,"dist_x"] "," val[sect,"dist_y"];
    if (source!=sect) {
      impcost=pathcost(source,sect)/4;
      ware=((val[sect,"des"]=="w" && val[sect,"eff"]>=60) ||
	    (val[source,"des"]=="w" && val[source,"eff"]>=60));
      for (comm in weight) {
	thresh = val[sect,substr(comm,1,1) "_dist"];
	if (thresh && val[sect,comm]<thresh) {
	  maxtomove = thresh - val[sect,comm];
	  if (maxtomove>val[source,comm]) maxtomove=val[source,comm];
	  mobtomove = maxtomove*impcost;
	  if (ware) mobtomove /= packing[comm];
	  if (comm=="bar" && val[source,"des"]=="b" && val[source,"eff"]>=60)
	    mobtomove/=4;
	  if (mobtomove > val[source,"mob"]) {
	    maxtomove *= val[source,"mob"]/mobtomove;
	    mobtomove = val[source,"mob"];
	  }
	  val[sect,comm] += maxtomove;
	  imported[sect,comm] += maxtomove;
	  exported[source,comm] += maxtomove;
	  val[source,comm] -= maxtomove;
	  val[source,"mob"] -= mobtomove;
	  interesting[source]=1;
	  interesting[sect]=1;
	}
      }
    }
  }
}




#
#	What to do at the end
#

END {
  update();

  for (sect in sects) {

    for (i in cost) delete cost[i];
    for (i in marked) delete marked[i];
    cost[sect]=0;
    while (buildcost(cost,marked)) ;

    if ((sect in will) && will[sect]>0) {
      if (make[sect]=="iron" || make[sect]=="lcm" || make[sect]=="hcm") {
	for (dest in cost) {
	  mobcost = cost[dest]*weight[make[sect]]*val[sect,make[sect]]/4;
	  if (val[sect,"mob"] >= mobcost) {
	    if ((dest in use1) && use1[dest]==make[sect] &&
		max1[dest]>=val[dest,make[sect]] &&
		(!(sect in max1) ||
		 val[dest,commstr(max1[sect])]>=num(max1[dest]))) {
	      dist[sect]=dest;
	      val[dest,commstr(max1[sect])] -= num(max1[dest]);
	      val[dest,"mob"] -= (cost[dest]-movcost(dest)+movcost(sect)) * weight[commstr(max1[sect])] * num(max1[dest]) / 4;
	      val[dest,make[sect]] += will[sect];
	      val[sect,"mob"] -= mobcost;
	      if (!global) printf("dist %s %s\n",sect,dest);
	      break;
	    } else if ((dest in use2) && use2[dest]==make[sect] &&
		       max2[dest]>val[dest,make[sect]] &&
		       (!(sect in max1) ||
			val[dest,commstr(max1[dest])]>=num(max1[dest]))) {
	      dist[sect]=dest;
	      val[dest,commstr(max1[sect])] -= num(max1[dest]);
	      val[dest,"mob"] -= (cost[dest]-movcost(dest)+movcost(sect))*weight[commstr(max1[sect])] * num(max1[dest]) / 4;
	      val[dest,make[sect]] += will[sect];
	      val[sect,"mob"] -= mobcost;
	      if (!global) printf("dist %s %s\n",sect,dest);
	      break;
	    } else if ((dest in use3) && use3[dest]==make[sect] &&
		       max3[dest]>val[dest,make[sect]] &&
		       (!(sect in max1) ||
			val[dest,commstr(max1[dest])]>=num(max1[dest]))) {
	      dist[sect]=dest;
	      val[dest,commstr(max1[sect])] -= num(max1[dest]);
	      val[dest,"mob"] -= (cost[dest]-movcost(dest)+movcost(sect))*weight[commstr(max1[sect])] * num(max1[dest]) / 4;
	      val[dest,make[sect]] += will[sect];
	      val[sect,"mob"] -= mobcost;
	      if (!global) printf("dist %s %s\n",sect,dest);
	      break;
	    }
	  }
	}
      }
    }

    if (val[sect,"des"]=="^") dist[sect]=sect;

    best=sect;
    bestcost=9999;
    if (!(sect in dist)) {
      for (dest in cost) {
	if (val[dest,"des"]=="w" && val[dest,"eff"]>=60 && val[dest,"mob"]>0 && dest!=sect) {
	  if (cost[dest]<bestcost) {
	    bestcost=cost[dest];
	    best=dest;
	  }
	}
      }
      if (best != dest) {
	dist[sect]=best;
	if (!global) printf("dist %s %s\n",sect,best);
      }
    }
  }

  if (global) {
    printf("dist *\n");
    for (i=0;i<dsects;i++) {
      sect=order[i];
      if ((sect in dist) && dist[sect]!=val[sect,"dist_x"] "," val[sect,"dist_y"]) {
	printf("%s\n",dist[sect]);
      } else {
	printf("%d,%d\n",val[sect,"dist_x"],val[sect,"dist_y"]);
      }
    }
  }

}
