#ifndef lint
static char *RCSid = "$Header: /sequent2/empire/EMP/player/commands/RCS/prod.c,v 1.12 89/09/10 17:24:57 mr-frog Exp $";
#endif

/*
 * budg.c
 *
 * calculate production levels, prioritize
 *
 * Thomas Ruschak, 1992
 */

#include "misc.h"
#include "var.h"
#include "xy.h"
#include "nsc.h"
#include "sect.h"
#include "product.h"
#include "nat.h"
#include "tm.h"
#include "item.h"
#include "file.h"
#include "deity.h"
#include "ship.h"
#include "plane.h"

short	ship_a[WORLD_X][WORLD_Y];

int
budg()
{
	extern  char *argp[];
	char	stype=0, *pq, buf[80];
	int	priority, x, y, z;
	int	costs[SCT_MAXDEF+1], taxes=0, bars=0, mil=0, cap=0;
	int	amounts[SCT_MAXDEF+1], delta=0;
	int	sbuild=0,smaint=0,pbuild=0,pmaint=0, ships=0,planes=0;
	int	nsbuild=0,npbuild=0, which=0;
	struct	natstr *np;

	bzero(costs,sizeof(costs));
	bzero(amounts,sizeof(amounts));

	np=getnatp(cnum);
	if (argp[1] != (char *)0)
		if (isalpha(argp[1][0]) || (argp[1][0] == '%'))
			stype=argp[1][0];
		else
			return RET_SYN;

	if ((stype != 0) && (stype != 'C'))
		pq = getstarg(argp[2], "Priority? ");
	else
		pq = (char *)0;

	if (pq != (char *)0)
		if (isdigit(*pq)){
			priority=(atoi(pq) < 0 ? -1*atoi(pq) : atoi(pq));
			if (priority >= SCT_MAXDEF+6){
				pr(fmt("Priorities must be less than %d!\n",
					SCT_MAXDEF+6));
				return RET_FAIL;
			}
			for(x=0;x<SCT_MAXDEF+6;x++)
				if (priority &&
					(np->nat_priorities[x] == priority)){
					pr("Priorities must be unique!\n");
					return RET_FAIL;
				}
		}
		else if (*pq == '~')
			priority = -1;
		else
			return RET_SYN;

	if ((stype) && !god){
		if (!isupper(stype)){
			which=0;
			while ((which<SCT_MAXDEF+2) &&
				(stype != dchr[which].d_mnem))
				which++;
			if (which == SCT_MAXDEF+2)
				return RET_SYN;
		}else{
			switch(stype){
				case 'P': which=PRI_PBUILD;break;
				case 'S': which=PRI_SBUILD;break;
				case 'M': which=PRI_SMAINT;break;
				case 'N': which=PRI_PMAINT;break;
				case 'C': which=(-1);break;
				default:  return RET_SYN;
			}
		}
		if (((which == PRI_SMAINT) || (which == PRI_PMAINT)) &&
			(priority == 0)){
			pr("Ship and plane maintenance may not be skipped!\n");
			return RET_FAIL;
		}
		if (which == -1){
			for(x=0;x<SCT_MAXDEF+6;x++){
				NAT_SETARY(nat_priorities[0],cnum,x,-1);
				np->nat_priorities[x] = -1;
			}
		}else{
			NAT_SETARY(nat_priorities[0],cnum,which,priority);
			np->nat_priorities[which] = priority;
		}
		
	}
	calc_prod(costs,amounts,&taxes,&bars,&mil,&cap);
	pr(fmt("Current treasury:\t\t\t\t\t\t%d\n",np->nat_money));
	pr(fmt("Income from taxes+bars:\t\t\t\t\t\t%d+%d\n",taxes,bars));
	pr(fmt("Payments for Military+Capitals\t\t\t\t\t%d+%d\n\n",-1*mil,cap));
	delta=taxes+bars+mil-cap;
	pr("Sector Type\t\tAbbr\tProduction\tPriority\tTotal Cost\n");
	for(x=0;x<SCT_MAXDEF+2;x++){
		if (pchr[dchr[x].d_prd].p_cost == 0)
			if (x != SCT_ENLIST){
				if (((np->nat_money+delta) > 0) &&
					(x <= SCT_MAXDEF)){
					delta -= costs[x];
				}
				if (np->nat_priorities[x] != -1){
					pr(fmt("Error, priority %d = %d\n",x,np->nat_priorities[x]));
					NAT_SETARY(nat_priorities[0],cnum,x,-1);
				}
				continue;
			}
		pr(fmt("%-17s\t%c\t",dchr[x].d_name,dchr[x].d_mnem));
		if (x == SCT_ENLIST)
			pr(fmt("%d mil    \t",amounts[x]));
		else
			pr(fmt("%d %-7s\t",amounts[x],
				pchr[dchr[x].d_prd].p_sname));
		if (np->nat_priorities[x] != -1){
			pr(fmt("%d",np->nat_priorities[x]));
		}
		pr(fmt("\t"));
		pr(fmt("\t"));
		if (np->nat_priorities[x] != 0){
			if ((np->nat_money+delta) > 0){
				pr(fmt("$%-3d",costs[x]));
				delta -= costs[x];
			}
			else
				pr(fmt("$[%-3d]",costs[x]));
		}else{
			if ((np->nat_money+delta) > 0)
				pr(fmt("$(%-3d)",costs[x]));
			else
				pr(fmt("$[(%-3d)]",costs[x]));
		}

		pr("\n");
		}
	bzero(ship_a,sizeof(ship_a));
	ships=calc_ships(&sbuild,&nsbuild,&smaint);

	sprintf(buf,"%d ship%s",nsbuild,splur(nsbuild));
	pr(fmt("Ship building\t\tS\t%-16s", buf));
	if (np->nat_priorities[PRI_SBUILD] != -1)
		pr(fmt("%d",np->nat_priorities[PRI_SBUILD]));
	if ((np->nat_money+delta) > 0){
		if (np->nat_priorities[PRI_SBUILD] != 0)
			pr(fmt("\t\t$%-4d\n",sbuild));
		else
			pr(fmt("\t\t$(%-4d)\n",sbuild));
		if (np->nat_priorities[PRI_SBUILD] != 0)
			delta -= sbuild;
	}else{
		if (np->nat_priorities[PRI_SBUILD] != 0)
			pr(fmt("\t\t$[%-4d]\n",sbuild));
		else
			pr(fmt("\t\t$[(%-4d)]\n",sbuild));
	}

	sprintf(buf,"%d ship%s",ships,splur(ships));
	pr(fmt("Ship maintenance\tM\t%-16s",buf));
	if (np->nat_priorities[PRI_SMAINT] != -1)
		pr(fmt("%d",np->nat_priorities[PRI_SMAINT]));
	if (np->nat_priorities[PRI_SMAINT] != 0)
		pr(fmt("\t\t$%-4d\n",-1*smaint));
	else
		pr(fmt("\t\t$(%-4d)\n",-1*smaint));
	if (np->nat_priorities[PRI_SMAINT] != 0)
		delta += smaint;

	planes=calc_planes(&pbuild,&npbuild,&pmaint);
	sprintf(buf,"%d plane%s",npbuild,splur(npbuild));
	pr(fmt("Plane building\t\tP\t%-16s",buf));
	if (np->nat_priorities[PRI_PBUILD] != -1)
		pr(fmt("%d",np->nat_priorities[PRI_PBUILD]));
	if ((np->nat_money+delta) > 0){
		if (np->nat_priorities[PRI_PBUILD] != 0)
			pr(fmt("\t\t$%-4d\n",pbuild));
		else
			pr(fmt("\t\t$(%-4d)\n",pbuild));
		if (np->nat_priorities[PRI_PBUILD] != 0)
			delta -= pbuild;
	}else{
		if (np->nat_priorities[PRI_PBUILD] != 0)
			pr(fmt("\t\t$[%-4d]\n",pbuild));
		else
			pr(fmt("\t\t$[(%-4d)]\n",pbuild));
	}

	sprintf(buf,"%d plane%s",planes,splur(planes));
	pr(fmt("Plane maintenance\tN\t%-16s",buf));
	if (np->nat_priorities[PRI_PMAINT] != -1)
		pr(fmt("%d",np->nat_priorities[PRI_PMAINT]));
	if (np->nat_priorities[PRI_PMAINT] != 0)
		pr(fmt("\t\t$%-4d\n",-1*pmaint));
	else
		pr(fmt("\t\t$(%-4d)\n",-1*pmaint));
	if (np->nat_priorities[PRI_PMAINT] != 0)
		delta += pmaint;
	pr(fmt("\t\t\t\t\tEstimated Delta:\t%d\n",delta));
	pr(fmt("\t\t\t\t\tNew treasury:\t\t%d\n",np->nat_money+delta));
	if ((np->nat_money+delta) < 0){
		pr("After processsing sectors, you will be broke!\n");
		pr("Sectors will not produce, distribute, or deliver!\n\n");
	}

	return RET_OK;
}

int
calc_prod(costs,amounts,taxes,bars,mil,cap)
	int	costs[SCT_MAXDEF+1];
	int	amounts[SCT_MAXDEF+1];
	int	*taxes,*bars,*mil,*cap;
{
	struct	nstr_sect ns;
	struct	sctstr sect;
	struct	natstr *natp;
	extern	int etu_per_update;
	extern	double bankint;
	extern	double money_civ;
	extern	double money_uw;
	extern	double money_res;
	extern	double money_mil;
	extern	double obrate, uwbrate; 
	extern	int etu_per_update;
	struct	nstr_sect nstr;
	struct	pchrstr *pp;
	double  effic;
	double  maxr;		    /* floating version of max */
	double  prodeff;
	double  real;		    /* floating pt version of act */
	double  work;
	int     act;		    /* actual production */
	int     cost;
	int     i;
	int     max;		    /* production w/infinate materials */
	double  maxtake;
	int     nsect;
	int     take;
	int     mtake;
	int     there;
	int     totcomp;	    /* sum of component amounts */
	int     used;		    /* production w/infinite workforce */
	int     wforce;
	int     it;
	u_short	*amount;	    /* amount for component pointer */
	u_char	*comp;		    /* component pointer */
	u_char	*endcomp;
	u_char  vtype;
	char	*resource;
	int     c;
	char    maxc[3][10];
	char    use[3][10];
	int     items[I_MAX+1];
	int	bwork;
	int	twork;
	int	type;
	int	eff,civs,uws;
	float	t=0.0;

	natp = getnatp(cnum);

	*taxes=0;
	*bars=0;
	*mil += (int) (natp->nat_reserve * money_res);

	snxtsct_all(&nstr);
	while (nxtsct(&nstr, &sect)) {
		if ((sect.sct_own != cnum) && !god)
			continue;
		if (sect.sct_type == SCT_WATER)
			continue;

                getvec(VT_ITEM, items, (char *)&sect, EF_SECTOR);

		t = 0.0;

		t += 0.5 + ((double)items[I_CIVIL] *
			sect.sct_effic/100.0 * etu_per_update * money_civ);

		t += 0.5 + ((double)items[I_UW] * sect.sct_effic/100.0 *
			etu_per_update * money_uw);

		*taxes += t;

		*mil += (items[I_MILIT] * etu_per_update * money_mil);

		civs = min(999, (int) ((obrate * (double) etu_per_update + 1.0)
		       * (double) items[I_CIVIL]));
		uws = min(999, (int) ((uwbrate * (double) etu_per_update + 1.0)
		       * (double) items[I_UW]));

		wforce = (int)
			((civs * sect.sct_work) / 100.0
			+ uws + items[I_MILIT] * 2 / 5.0);

		if ((sect.sct_type == SCT_CAPIT) && (sect.sct_effic > 60))
			*cap += etu_per_update;

		if ((sect.sct_type == SCT_BANK) && (sect.sct_effic > 60))
			*bars += items[I_BAR] * etu_per_update * bankint;

		work = roundavg((etu_per_update * wforce) / 100.0);
		bwork = work/2;

		type = sect.sct_type;
		eff = sect.sct_effic;
		if(sect.sct_newtype != type) {
			twork = (eff+3)/4;
			if(twork > bwork) {
				twork = bwork;
			}
			work -= twork;
			bwork -= twork;
			eff -= twork*4;
			costs[sect.sct_type] += twork;
			if(eff <= 0) {
				type = sect.sct_newtype;
				eff = 0;
			}

			twork = 100 - eff;
			if(twork > bwork) {
				twork = bwork;
			}
			costs[sect.sct_type] += twork;
			work -= twork;
			eff += twork;
		}
		else if(eff < 100) {
			twork = 100 - eff;
			if(twork > bwork) {
				twork = bwork;
			}
			work -= twork;
			costs[sect.sct_type] += twork;
			eff += twork;
		}

		if(eff < 60 || (type != SCT_ENLIST && eff < 61))
			continue;

		effic = eff/100.0;
		if (effic > 1.0)
			effic = 1.0;

		if (dchr[type].d_prd == 0 && type != SCT_ENLIST)
			continue;

		totcomp = 0;
		pp = &pchr[dchr[type].d_prd];
		vtype = pp->p_type;
		/*
		 * sect effic  (inc improvements)
		 */
		if (type == SCT_ENLIST)
			goto is_enlist;
		if (pp->p_nrndx != 0) {
			totcomp++;
			resource = ((char *) &sect) + pp->p_nrndx;
			effic = (*resource * effic) / 100.0;
			if (pp->p_nrdep > 0) {
				maxtake = (*resource * 100.0) / pp->p_nrdep;
				if (effic > maxtake)
					effic = maxtake;
			}
		}
		/*
		 * production effic.
		 */
		if (pp->p_nlndx >= 0) {
			prodeff = natp->nat_level[pp->p_nlndx] - pp->p_nlmin;
			if (prodeff < 0.0) {
				prodeff = 0.0;
			}
			prodeff = prodeff / (prodeff + pp->p_nllag);
		} else {
			prodeff = 1.0;
		}
		used = 999;
		comp = pp->p_vtype;
		endcomp = pp->p_vtype + pp->p_nv;
		amount = pp->p_vamt;
		while (comp < endcomp) {
			used = min(used,
			    (int)(getvar((int)*comp, (char *)&sect, EF_SECTOR) /
			    	*amount));
			totcomp += *amount;
			++comp;
			++amount;
		}
		if (totcomp == 0)
			continue;
		/*
		 * is production limited by resources or
		 * workforce?
		 */
		max = (int) (work * effic / (double) totcomp) + 0.5;
		act = min(used, max);
		/*
		 * some things are easier to make..  food,
		 * pet, etc.
		 */
		act = (int) (pp->p_effic * 0.01 * act) + 0.5;


		if (vtype != 0) {
			if (real < 0.0)
				real = 0.0;
			if ((there = getvar(
				(int)vtype, (char *)&sect, EF_SECTOR)) >= 999) {
				/*
				 * production backlog
				 */
				there = 999;
			}
			act = min(act, 999 - there);
		}

		real = act * prodeff;

		if (prodeff != 0)
			take = real / prodeff;
		else
			take = 0.0;

		cost = take * pp->p_cost;
		take = take / ((double) pp->p_effic * 0.01);
is_enlist:
		if (type != SCT_ENLIST) {
			amounts[type] += (real+0.5);
			costs[type] += cost;
		} else {
			int	maxmil;
			int	enlisted;

			enlisted = 0;
			maxmil = (civs / 2) - items[I_MILIT];
			if (maxmil > 0) {
				enlisted = (etu_per_update * (10 + items[I_MILIT]) * 0.05);
				if (enlisted > maxmil)
					enlisted = maxmil;
			}
			if (enlisted < 0)
				enlisted = 0;

			amounts[type] += enlisted;
			costs[type] += enlisted*3;
			items[I_MILIT] += enlisted;
			continue;
		}

	}

}

calc_ships(build,nbuild,maint)
int	*build,*nbuild,*maint;
{
	extern	int etu_per_update;
	struct  nstr_item ni;
	struct	shpstr ship;
	struct	natstr *np;
	int	n;
	extern	double money_ship;
	extern	long pops[];
	extern  double money_mil;
	struct	sctstr *sectp;
	struct	mchrstr *mp;
	int	vec[I_MAX+1], ships=0;
	int	cvec[I_MAX+1];


	np = getnatp(cnum);
	snxtitem_all(&ni, EF_SHIP);
	while(nxtitem(&ni, (char *)&ship)){
		if (ship.shp_own == 0)
			continue;
		if ((ship.shp_own != cnum) && !god)
			continue;
		ships++;
		mp = &mchr[ship.shp_type];
		getvec(VT_ITEM, vec, (char *)&ship, EF_SHIP);
		if (vec[I_MILIT]){
			*maint += (int) (etu_per_update * vec[I_MILIT] *
					money_mil);
		}
		*build += calc_shiprepair(&ship,vec, np, etu_per_update,
						maint, nbuild);
	}

	return(ships);
}

int
calc_shiprepair(ship, vec, np, etus, maint, nbuild)
	struct shpstr *ship;
	int	*vec;
	struct	natstr *np;
	int	etus, *maint, *nbuild;
{
	extern	double money_ship;
	extern	int ship_grow_scale;
	register int delta;
	struct	sctstr *sp, sect;
	struct	mchrstr *mp;
	int	wf;
	int	left;
	int	avail;
	int	w_p_eff;
	int	mult;
#ifdef ALLYHARBORWORK
	int	rel;
#endif /* ALLYHARBORWORK */

	mp = &mchr[ship->shp_type];
	getsect(ship->shp_x, ship->shp_y, &sect);
	if (ship_a[ship->shp_x][ship->shp_y] == 0)
		ship_a[ship->shp_x][ship->shp_y] = sect.sct_avail;
	sp = &sect;
	mult = 1;
	if (np->nat_level[NAT_TLEV] < ship->shp_tech * 0.85)
		mult = 2;
	*maint += (int) (mult * etus * dmin(0.0, money_ship * mp->m_cost));

	if (ship->shp_effic == 100) {
		/* ship is ok; no repairs needed */
		return 0;
	}
	if ((sp->sct_own != ship->shp_own) && (sp->sct_own != 0)) {
#ifdef ALLYHARBORWORK
		rel=getrel(getnatp(sp->sct_own),ship->shp_own);

		if (rel != ALLIED)
			return 1;
#else
		/* not our harbor -> shore leave; no eff gain */
		return 1;
#endif /* ALLYHARBORWORK */
	}
	left = 100 - ship->shp_effic;
	wf = 0;
	/* only military can work on a military boat */
	if (mp->m_glim > 0)
		wf = etus * vec[I_MILIT]/2;
	else
		wf = etus * (vec[I_CIVIL]/2 + vec[I_MILIT]/5);
	if (sp->sct_type != SCT_HARBR)
		wf /= 3;
	avail = wf + ship_a[ship->shp_x][ship->shp_y] * 100;
	if (avail <= 0)
		return 0;
	w_p_eff = 20 + (mp->m_lcm + 2 * mp->m_hcm);
	delta = roundavg((double)avail/w_p_eff);
	if (delta <= 0)
		return 0;
	(*nbuild)++;
	if (delta > etus)
		delta = etus*ship_grow_scale;
	if (delta > left)
		delta = left;
	wf -= delta * w_p_eff;
	if (wf < 0) {
		/*
		 * I didn't use roundavg here, because I want to penalize
		 * the player with a large number of ships.
		 */
		avail = (ship_a[ship->shp_x][ship->shp_y] * 100 + wf) / 100;
		if (avail <= 0)
			avail = -1;
		ship_a[ship->shp_x][ship->shp_y] = avail;
	}
	return mult * mp->m_cost * delta / 100.0;
}

calc_planes(pbuild,npbuild,pmaint)
int	*pbuild, *npbuild, *pmaint;
{
	short	a[WORLD_X][WORLD_Y];
	extern	int etu_per_update;
	extern	int plane_grow_scale;
	extern	double money_mil;
	extern	double money_plane;
	extern	long money[MAXNOC];
	register struct plnstr *pp, plane;
	register struct plchrstr *plp;
	struct	natstr *np;
	int	n, nplanes=0;
	struct	nstr_item ni;
	struct	shpstr *shp;
	struct	mchrstr *mp;
	struct	plchrstr *desc;
	struct  sctstr *sp, sect;
	int	delta;
	int	mult;
	int	eff;
	int	avail;
	int	w_p_eff;
	int	left;
	int	used;
	int     start_money;

	bzero(a,sizeof(a));
	np = getnatp(cnum);
	snxtitem_all(&ni, EF_PLANE);
	while (nxtitem(&ni, (char *)&plane)){
		n = plane.pln_uid;
		pp = &plane;
		if (pp->pln_own == 0)
			continue;
		if ((pp->pln_own != cnum) && !god)
			continue;
		nplanes++;
		shp = 0;
		plp = &plchr[pp->pln_type];

		/* flight pay is 5x the pay received by other military */
		*pmaint += (int) (etu_per_update *
			(plp->pl_crew * money_mil * 5));
		if (np->nat_money < 0)
			continue;
		desc = &plchr[pp->pln_type];

		getsect(pp->pln_x, pp->pln_y, &sect);
		if (a[pp->pln_x][pp->pln_y] == 0)
			a[pp->pln_x][pp->pln_y] = sect.sct_avail;
		sp = &sect;
		mult = 1;
		if (np->nat_level[NAT_TLEV] < pp->pln_tech * 0.85)
			mult = 2;
		*pmaint += (int) (mult * etu_per_update *
			dmin(0.0, desc->pl_cost * money_plane));

		if ((pp->pln_flags & PLN_LAUNCHED) == PLN_LAUNCHED)
			continue;

		left = 100 - pp->pln_effic;
		if (left <= 0)
			continue;
		avail = a[pp->pln_x][pp->pln_y] * 100;
		if (avail <= 0)
			continue;
		w_p_eff = 20 + (desc->pl_lcm + 2 * desc->pl_hcm);
		delta = roundavg((double)avail/w_p_eff);
		if (delta <= 0)
			continue;
		(*npbuild)++;
		if (delta > etu_per_update)
			delta = etu_per_update*plane_grow_scale;
		if (delta > left)
			delta = left;
		used = delta * w_p_eff;
		/*
		 * I didn't use roundavg here, because I want to penalize
		 * the player with a large number of planes.
		 */
		avail = (a[pp->pln_x][pp->pln_y] * 100 - used) / 100;
		if (avail <= 0)
			avail = -1;
		a[pp->pln_x][pp->pln_y] = avail;
		if (sp->sct_type != SCT_AIRPT)
			delta /= 3;
		*pbuild += roundavg(mult * delta * desc->pl_cost / 100.0);
	}

	return nplanes;
}
