#ifndef lint
static char *RCSid = "$Header: distribute.c,v 1.5 89/09/28 01:56:18 mr-frog Exp $";
#endif

/*
 * distribute.c
 *
 * do distribution sector stuff.  
 *
 * Dave Pare, 1986
 */

#include "misc.h"
#include "var.h"
#include "nat.h"
#include "sect.h"
#include "item.h"
#include "xy.h"
#include "path.h"
#include "file.h"

dodistribute(sp, local)
	register struct sctstr *sp;
	register int *local;
{
	struct	sctstr *getdistsp();
	float	distpathcost();
	register struct ichrstr *ip;
	register struct sctstr *dist;
	int	amt;
	int	thresh;
	int	amt_dist;
	int	amt_sect;
	float	pathcost;
	int	packing;
	float	imcost;
	float	excost;
	int	wh_packing;
	int	diff;
	int	item;
	int	dists[I_MAX+1];
	int	remote[I_MAX+1];
	int	changed;

	if (sp->sct_distpath == 0)
		return 0;
	if (getvec(VT_DIST, dists, (char *)sp, EF_SECTOR) <= 0)
		return 0;
	if ((pathcost = distpathcost(sp)) < 0.0) {
		if (sp->sct_own != 0) {
			wu(0, sp->sct_own, fmt("Bad dist path in %s\n",
				ownxy(sp)));
		}
		return 0;
	}
	wh_packing = dchr[SCT_WAREH].d_pkg;
	packing = dchr[sp->sct_type].d_pkg;
	dist = getdistsp(sp);
	getvec(VT_ITEM, remote, (char *)dist, EF_SECTOR);
	changed = 0;
	for (item = 1; item < I_MAX+1; item++) {
		if (dists[item] == 0)
			continue;
		ip = &ichr[item];
		thresh = dists[item];
		/*
		 * calculate costs for importing and exporting.
		 * the div 2.0 is because delivering straight through
		 * to the dist sect is cheaper than stopping at each
		 * sector along the way (processor-timewise)
		 */
		excost = (pathcost / ip->i_pkg[packing] * ip->i_lbs) / 2.0;
		imcost = (pathcost / ip->i_pkg[wh_packing] * ip->i_lbs) / 4.0;
		amt_sect = local[item];
		amt_dist = remote[item];
		diff = amt_sect - thresh;
		if (diff < 0) {
			diff = -diff;
			/*
			 * import.
			 * don't import if no mobility.
			 * check to make sure have enough mobility in the
			 * dist sector to import what we need.
			 */
			if (dist->sct_mobil <= 0) {
				/*logerror("  dist mobil < 0");*/
				continue;
			}
			amt = diff;
			if (amt_dist < amt) {
				amt = amt_dist;
				if (amt_dist == 0)
					continue;
			}
			if (dist->sct_mobil < imcost * amt)
				amt = dist->sct_mobil / imcost;

			/* XXX replace with vector assign and putvec() */
			local[item] += amt;
			remote[item] -= amt;
			changed++;
			dist->sct_mobil -= (int) (imcost * amt);
		} else {
			/*
			 * export.
			 * don't export if no mobility. check to make sure we
			 * have mobility enough to do the right thing.
			 * also make sure that there's enough space in the
			 * target sector to hold the required amt.
			 */
			if (sp->sct_mobil <= 0) {
				/*logerror("  sp mob is zero");*/
				continue;
			}
			amt = diff;
			if (amt > amt_sect)
				amt = amt_sect;
			if (sp->sct_mobil < excost * amt)
				amt = sp->sct_mobil / excost;
			if (amt + amt_dist > 9999)
				amt = 9999 - amt_dist;
			if (amt == 0)
				continue;
			/* XXX replace with vector assign and putvec() */
			local[item] -= amt;
			remote[item] += amt;
			changed++;
			sp->sct_mobil -= (int) (excost * amt);
		}
	}
	putvec(VT_ITEM, remote, (char *)dist, EF_SECTOR);
	return changed;
}

/*
 * return path cost to distribution sector
 * (in unit quantity...i.e. cost to move one unit
 * of weight one)
 */
float
distpathcost(sp)
	struct	sctstr *sp;
{
	float	movecost();
	u_long	mask;
	u_long	bitmask;
	int	i;
	u_long	val;
	int	x;
	int	y;
	float	cost;
	float	totalcost;
	struct	sctstr *new;

	mask = sp->sct_distpath;
	if (mask == 0)
		return -1;
	x = sp->sct_x;
	y = sp->sct_y;
	bitmask = 07;	/* three bits for direction */
	totalcost = 0.0;
	for (i=0; i<sizeof(mask)*8; i += 3) {
		val = (mask & bitmask) >> i;
		bitmask <<= 3;
		if (val == 0)
			break;
		x += diroff[val][0];
		y += diroff[val][1];
		new = getsectp(x, y);
		if (new->sct_own != sp->sct_own)
			return -1.0;
		if ((cost = movecost(new)) < 0.0)
			return -1.0;
		totalcost += cost;
	}
	return totalcost;
}

float
movecost(sp)
	struct	sctstr *sp;
{
	int	mcost;

	if ((mcost = dchr[sp->sct_type].d_mcst) == 0)
		return -1.0;
	return (mcost * 100 - sp->sct_effic) / 500.0;
}

struct sctstr *
getdistsp(sp)
	struct	sctstr *sp;
{
	u_long	mask;
	u_long	bitmask;
	int	i;
	u_long	val;
	int	x;
	int	y;

	mask = sp->sct_distpath;
	if (mask == 0)
		return sp;
	x = sp->sct_x;
	y = sp->sct_y;
	bitmask = 07;
	for (i=0; i<sizeof(mask)*8; i += 3) {
		val = (mask & bitmask) >> i;
		if (val == 0)
			break;
		bitmask <<= 3;
		x += diroff[val][0];
		y += diroff[val][1];
	}
	/*
	 * in case some high bits got set somewhere
	 */
	if (i == 0)
		return 0;
	return getsectp(x, y);
}
