#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/gen/emp_config.c,v 2.9 1995/10/11 02:55:05 empire Exp $";
#endif

/*
 * emp_config.c
 *
 * Allows to control lots of the empire constants.
 * Julian Onions, 1995
 */


/*
 * STILL TO DO
 *
 * 1. Change other constants - such as WORLDX/Y and Num Countries etc.
 *    Just requires variables to be assigned, then dynamic allocation in
 *    a few places. Some checks needed in the server to check the world
 *    hasn't changed size etc.
 * 2. Could look at loading in planes, units etc. Should be easy enough.
 *
 */

#include <stdio.h>

#include "misc.h"
#include "com.h"
#include "match.h"
#include "file.h"
#include "optlist.h"

/* for systems without strdup  */
#ifdef NOSTRDUP
extern char *strdup();
#endif /* NOSTRDUP */

/* All the configurable variables ... */
extern s_char *infodir, *datadir, *gamedir, *loginport, *privname, *privlog;

extern double  buytax, flakscale, maxmult, minmult, tradetax, uwbrate;
extern double buil_bc, buil_bt, combat_mob, edu_cons, hap_cons, money_civ;
extern double money_land, money_mil, money_plane, money_res, money_ship;
extern double money_uw, people_damage, powe_cost, unit_damage, babyeat;
extern double collateral_dam, assault_penalty;
extern double bankint, eatrate, fcrate, fgrate, obrate, mission_mob_cost;

extern float btu_build_rate, easy_tech, hard_tech, land_mob_scale;
extern float level_age_rate, plane_mob_scale, sect_mob_scale;
extern float ship_mob_scale, tech_log_base, ally_factor, edu_avg, hap_avg;

extern int startmob, at_least_one_100, buil_bh, etu_per_update;
extern int land_grow_scale, land_mob_max, m_m_p_d, max_btus, max_idle;
extern int plane_grow_scale, plane_mob_max, players_at_00, sect_mob_max;
extern int ship_grow_scale, ship_mob_max, torpedo_damage;
extern int fort_max_interdiction_range;
extern int land_max_interdiction_range;
extern int ship_max_interdiction_range;

extern long adj_update;	
extern long s_p_etu;	

/* conditional ones */
#ifdef	FALLOUT
extern double	 decay_per_etu, fallout_spread;
#endif	/* FALLOUT */

extern int fuel_mult;

#ifdef SLOW_WAR
extern int War_Cost;	
#endif 
#ifdef DEMANDUPDATE
extern long last_demand_update;	
#endif 

extern float	drnuke_const;
extern float start_education, start_happiness;
extern float start_technology, start_research;

#ifdef TRADESHIPS
extern int   trade_1_dist, trade_2_dist, trade_3_dist;
extern float trade_1, trade_2, trade_3, trade_ally_bonus, trade_ally_cut;
#endif 

/* TO DO - WORLDX/Y, NCountries ... - harder to do */


/* things that can be changed */
struct keymatch configkeys[] = {
{ "data",		strset,	(caddr_t) &datadir },
{ "info",		strset,	(caddr_t) &infodir },
{ "port",		strset,	(caddr_t) &loginport },
{ "privname",		strset,	(caddr_t) &privname },
{ "privlog",		strset, (caddr_t) &privlog },

{ "option",		optionset, (caddr_t) NULL},
{ "nooption",		optiondel, (caddr_t) NULL},

{ "btu_build_rate",	floatset, (caddr_t) &btu_build_rate },
{ "easy_tech", 		floatset, (caddr_t) &easy_tech },	
{ "hard_tech", 		floatset, (caddr_t) &hard_tech },	
{ "start_tech",		floatset, (caddr_t) &start_technology },
{ "start_happy",	floatset, (caddr_t) &start_happiness },
{ "start_research",	floatset, (caddr_t) &start_research },
{ "start_edu",		floatset, (caddr_t) &start_education },
{ "land_mob_scale", 	floatset, (caddr_t) &land_mob_scale },	
{ "level_age_rate", 	floatset, (caddr_t) &level_age_rate },	
{ "plane_mob_scale", 	floatset, (caddr_t) &plane_mob_scale },	
{ "sect_mob_scale", 	floatset, (caddr_t) &sect_mob_scale },	
{ "ship_mob_scale", 	floatset, (caddr_t) &ship_mob_scale },	
{ "tech_log_base", 	floatset, (caddr_t) &tech_log_base },	
{ "ally_factor", 	floatset, (caddr_t) &ally_factor },	
{ "edu_avg", 		floatset, (caddr_t) &edu_avg },
{ "hap_avg", 		floatset, (caddr_t) &hap_avg },

{ "startmob", 		intset, (caddr_t) &startmob },	
{ "at_least_one_100", 	intset, (caddr_t) &at_least_one_100 },	
{ "buil_bh", 		intset, (caddr_t) &buil_bh },		
{ "etu_per_update", 	intset, (caddr_t) &etu_per_update },	
{ "land_grow_scale", 	intset, (caddr_t) &land_grow_scale },	
{ "land_mob_max", 	intset, (caddr_t) &land_mob_max },	
{ "m_m_p_d", 		intset, (caddr_t) &m_m_p_d },	
{ "max_btus", 		intset, (caddr_t) &max_btus },		
{ "max_idle", 		intset, (caddr_t) &max_idle },	
{ "plane_grow_scale", 	intset, (caddr_t) &plane_grow_scale },	
{ "plane_mob_max", 	intset, (caddr_t) &plane_mob_max },	
{ "players_at_00", 	intset, (caddr_t) &players_at_00 },	
{ "sect_mob_max", 	intset, (caddr_t) &sect_mob_max },	
{ "ship_grow_scale", 	intset, (caddr_t) &ship_grow_scale },	
{ "ship_mob_max", 	intset, (caddr_t) &ship_mob_max },	
{ "torpedo_damage", 	intset, (caddr_t) &torpedo_damage },	
{ "fort_max_interdiction_range", intset, (caddr_t) &fort_max_interdiction_range },
{ "land_max_interdiction_range", intset, (caddr_t) &land_max_interdiction_range },
{ "ship_max_interdiction_range", intset, (caddr_t) &ship_max_interdiction_range },

{ "buytax",		doubleset, (caddr_t) &buytax },	
{ "flakscale", 		doubleset, (caddr_t) &flakscale },	
{ "maxmult", 		doubleset, (caddr_t) &maxmult },	
{ "minmult", 		doubleset, (caddr_t) &minmult },	
{ "tradetax", 		doubleset, (caddr_t) &tradetax },	
{ "uwbrate", 		doubleset, (caddr_t) &uwbrate },	
{ "buil_bc", 		doubleset, (caddr_t) &buil_bc },	
{ "buil_bt", 		doubleset, (caddr_t) &buil_bt },		
{ "combat_mob", 	doubleset, (caddr_t) &combat_mob },	
{ "edu_cons", 		doubleset, (caddr_t) &edu_cons },
{ "hap_cons", 		doubleset, (caddr_t) &hap_cons },
{ "money_civ", 		doubleset, (caddr_t) &money_civ },
{ "money_land", 	doubleset, (caddr_t) &money_land },
{ "money_mil", 		doubleset, (caddr_t) &money_mil },
{ "money_plane", 	doubleset, (caddr_t) &money_plane },
{ "money_res", 		doubleset, (caddr_t) &money_res },
{ "money_ship", 	doubleset, (caddr_t) &money_ship },
{ "money_uw", 		doubleset, (caddr_t) &money_uw },
{ "people_damage", 	doubleset, (caddr_t) &people_damage },	
{ "powe_cost", 		doubleset, (caddr_t) &powe_cost },	
{ "unit_damage", 	doubleset, (caddr_t) &unit_damage },	
{ "collateral_dam", 	doubleset, (caddr_t) &collateral_dam },	
{ "assault_penalty", 	doubleset, (caddr_t) &assault_penalty },	
{ "babyeat", 		doubleset, (caddr_t) &babyeat },	
{ "bankint", 		doubleset, (caddr_t) &bankint },	
{ "eatrate", 		doubleset, (caddr_t) &eatrate },	
{ "fcrate", 		doubleset, (caddr_t) &fcrate },	
{ "fgrate", 		doubleset, (caddr_t) &fgrate },	
{ "obrate", 		doubleset, (caddr_t) &obrate },	
{ "mission_mob_cost", 	doubleset, (caddr_t) &mission_mob_cost },	

{ "adj_update",		longset,   (caddr_t) &adj_update },
{ "s_p_etu",		longset,   (caddr_t) &s_p_etu },

#ifdef	FALLOUT
{ "decay_per_etu",	doubleset, (caddr_t) &decay_per_etu },
{ "fallout_spread",	doubleset, (caddr_t) &fallout_spread },
#endif	/* FALLOUT */

{ "fuel_mult",	intset, (caddr_t) &fuel_mult },

#ifdef SLOW_WAR
{ "war_cost",	intset, (caddr_t) &War_Cost },
#endif 
#ifdef DEMANDUPDATE
{ "last_demand_update", longset, (caddr_t) &last_demand_update },
#endif 

{ "drnuke_const",	floatset, (caddr_t) &drnuke_const },

#ifdef TRADESHIPS
{ "trade_1_dist",	intset, (caddr_t) &trade_1_dist },
{ "trade_2_dist",	intset, (caddr_t) &trade_2_dist },
{ "trade_3_dist",	intset, (caddr_t) &trade_3_dist },
{ "trade_1",	floatset, (caddr_t) &trade_1 },
{ "trade_2",	floatset, (caddr_t) &trade_2 },
{ "trade_3",	floatset, (caddr_t) &trade_3 },
{ "trade_ally_bonus",	floatset, (caddr_t) &trade_ally_bonus },
{ "trade_ally_cut",	floatset, (caddr_t) &trade_ally_cut },
#endif 

{ NULL }
};

static void fixup_files _PROTO((void));

/*
 * read in empire configuration
 */
int emp_config (file)
char *file;
{
	FILE *fp;
	s_char  scanspace[1024];
	s_char *av[65];
	char buf[BUFSIZ];
	struct keymatch *kp;

	if (file == NULL || (fp = fopen (file, "r")) == NULL) {
		fixup_files ();
		return RET_OK;
	}
	while (fgets (buf, sizeof buf, fp) != NULL) {
		if (buf[0] == '#' || buf[0] == '\n')
			continue;
		if (parse (buf, av, 0, scanspace, 0) < 0) {
			fprintf (stderr, "Can't parse line %s", buf);
			continue;
		}
		if ((kp = keylookup (av[0], configkeys)) != NULL) {
			(*kp -> km_func) (kp, av + 1);
		}
		else {
			fprintf (stderr, "Unknown config key %s\n", av[0]);
		}
	}
	fclose (fp);
	fixup_files ();

	return RET_OK;
}

struct otherfiles {
	char **files;
	char *name;
};

extern s_char *upfil, *downfil, *disablefil, *banfil, *authfil, *hoursfil;
extern s_char *commfil, *annfil, *telfil, *teldir, *syncfil, *syncdir;

/* list of other well known files... -maybe tailor these oneday
 * anyway - meantime they are all relative to datadir */
static struct otherfiles ofiles[] = {
{ &upfil,	"up" },
{ &downfil,	"down"},
{ &disablefil,	"disable"},
{ &banfil,	"ban" },
{ &authfil,	"auth" },
{ &hoursfil,	"hours" },
{ &commfil,	"comm" },
{ &annfil,	"ann" },
{ &teldir,	"tel" },
{ &telfil,	"tel/tel" },
{ &syncdir,	"sync" },
{ &syncfil,	"sync/sync" },
{ NULL,		NULL }
};

/* fix up the empfile struct to reference full path names */
static void fixup_files ()
{
	struct empfile *ep;
	struct otherfiles *op;
	s_char buf[1024];

	for (ep = empfile; ep < &empfile[EF_MAX]; ep ++) {
		sprintf (buf, "%s/%s", datadir, ep->name);
		ep->file = strdup (buf);
	}

	for (op = ofiles; op -> files; op ++) {
		sprintf (buf, "%s/%s", datadir, op -> name);
		*op -> files = strdup (buf);
	}
}

/* find the key in the table */
static struct keymatch *keylookup (command, tbl)
register s_char *command;
struct  keymatch *tbl;
{
        register struct keymatch *kp;

        if (command == 0 || *command == 0)
                return 0;
        for (kp = tbl; kp->km_key != 0; kp++) {
                if (strcmp (kp -> km_key, command) == 0)
                        return kp;
        }
	return NULL;
}

/* generic int setting function */
void intset (kp, av)
struct keymatch *kp;
s_char **av;
{
	int *intptr = (int *)kp -> km_data;

	if (*av == NULL || intptr == NULL)
		return;
	*intptr = atoi (*av);
}

/* generic float set function */
void floatset (kp, av)
struct keymatch *kp;
s_char **av;
{
	float *floatptr = (float *)kp -> km_data;

	if (*av == NULL || floatptr == NULL)
		return;
	*floatptr = atof (*av);
}



/* generic string set function */
void strset (kp, av)
struct keymatch *kp;
s_char **av;
{
	s_char **confstrp = (s_char **)kp -> km_data;

	if (*av == NULL || confstrp == NULL)
		return;
	if (kp -> km_flags & KM_ALLOC)
		free (*confstrp);
	*confstrp = strdup (*av);
	kp -> km_flags |= KM_ALLOC;
}

/* generic double set function */
void doubleset (kp, av)
struct keymatch *kp;
s_char **av;
{
	double *doublep = (double *)kp -> km_data;

	if (*av == NULL || doublep == NULL)
		return;
	*doublep = atof (*av);
}

/* generic long set function */
void longset (kp, av)
struct keymatch *kp;
s_char **av;
{
	long int *longp = (long int *)kp -> km_data;

	if (*av == NULL || longp == NULL)
		return;
	*longp = atol (*av);
}

print_config (fp)
FILE *fp;
{
	struct empfile *ep;
	struct otherfiles *op;
	struct keymatch *kp;

	fprintf (fp, "# Empire Configuration :\n");
	for (kp = configkeys; kp -> km_key; kp ++) {
		if (kp -> km_func == strset) {
			fprintf (fp, "%s \"%s\"\n", kp -> km_key,
				 *(s_char **)kp -> km_data);
		}
		else if (kp -> km_func == intset) {
			fprintf (fp, "%s %d\n", kp -> km_key,
				 *(int *)kp -> km_data);
		}
		else if (kp -> km_func == floatset) {
			fprintf (fp, "%s %g\n", kp -> km_key,
				 *(float *)kp -> km_data);
		}
		else if (kp -> km_func == doubleset) {
			fprintf (fp, "%s %g\n", kp -> km_key,
				 *(double *)kp -> km_data);
		}
		else if (kp -> km_func == longset) {
			fprintf (fp, "%s %ld\n", kp -> km_key,
				 *(long *)kp -> km_data);
		}
		else if (kp -> km_func == optionset) {
			struct option_list *op;

			for (op = Options; op -> opt_key; op++) {
				if (*op -> opt_valuep)
					fprintf (fp, "%s %s\n", kp -> km_key,
						 op -> opt_key);
			}
		}
		else if (kp -> km_func == optiondel) {
			struct option_list *op;

			for (op = Options; op -> opt_key; op++) {
				if (*op -> opt_valuep == 0)
					fprintf (fp, "%s %s\n", kp -> km_key,
						 op -> opt_key);
			}
		}
		else fprintf (fp, "# Unknown format %s\n", kp -> km_key);
	}

	for (ep = empfile; ep < &empfile[EF_MAX]; ep ++) 
		fprintf (fp, "# File %s -> %s\n", ep -> name, ep -> file);
	for (op = ofiles; op -> files; op ++)
		fprintf (fp, "# File %s -> %s\n", op -> name, *(op -> files));

}

    
/* add an option to the list */
void set_option (s)
const char *s;
{
	struct option_list *op;

	for (op = Options; op -> opt_key; op++) {
		if (strcmp (op -> opt_key, s) == 0)
			*op -> opt_valuep = 1;
	}
}

/* delete an option from the list */
void delete_option (s)
const char *s;
{
	struct option_list *op;

	for (op = Options; op -> opt_key; op++) {
		if (strcmp (op -> opt_key, s) == 0)
			*op -> opt_valuep = 0;
	}
}

/* config interface */
void optionset (kp, av)
struct keymatch *kp;		/* unused - we have a well known global */
s_char **av;
{
    char **cpp;

    for (cpp = av; *cpp; cpp++)
	set_option (*cpp);
}

/* config interface */
void optiondel (kp, av)
struct keymatch *kp;            /* unused - we have a well known global */
s_char **av;
{
    char **cpp;

    for (cpp = av; *cpp; cpp++)
        delete_option (*cpp);
}
