#ifndef lint
static char *RCSid = "$Header: /usr/brule/guest/empire/empire/emprcs/lib/common/wantupd.c,v 2.3 1995/10/11 02:56:04 empire Exp $";
#endif

/*
 * wantupd.c
 *
 * Check to see if an update is wanted, and if it is allowed.
 *
 * Doug Hay, 1990
 */

#include <stdio.h>
#include "misc.h"
#ifdef	UPDATESCHED
#include "nat.h"
#include "file.h"
#include "keyword.h"
#include "wantupd.h"
#include "optlist.h"

#include <sys/types.h>
#include <fcntl.h>
#ifdef Rel4
#include <time.h>
#else /* Rel4 */
#include <sys/time.h>
#endif /* Rel4 */

/* Make sure the data is current */
static int
init_updatecheck()
{
	extern int errno;
	extern s_char *hoursfil;
	FILE *fp;

	if ((fp = fopen(hoursfil, "r")) == 0) {
		errno = 0;
		return(1);
	}
	kw_read(fp);
	fclose(fp);

	return 1;
}

static int
update_policy()
{
	s_char *p;
	int  pol;

	pol = UDP_DEFAULT;
	if (p=kw_find("update_policy")) {
		kw_parse(CF_VALUE, p, &pol);
		if (pol < 0)
			pol = UDP_DEFAULT;
		if (pol > UDP_MAX)
			pol = UDP_DEFAULT;
	}
	return(pol);
}

static int
update_blitz()
{
        s_char *p;
        int  pol;
 
        if (p=kw_find("blitz_time")) {
                kw_parse(CF_VALUE, p, &pol);
                if (pol < 0)
                        pol = 10;
                if (pol > BLITZTIME)
                        pol = BLITZTIME;
        }
        return(pol);
}
 
#ifdef DEMANDUPDATE
static int
update_wantpol()
{
	s_char *p;
	int  pol;

	pol = UDDEM_DEFAULT;
	if (p=kw_find("update_demandpolicy")) {
		kw_parse(CF_VALUE, p, &pol);
		if (pol < 0)
			pol = UDDEM_DEFAULT;
		if (pol > UDDEM_MAX)
			pol = UDDEM_DEFAULT;
	}
	return(pol);
}

static int
update_wantmin(absmin)
	int *absmin;
{
	s_char *p;
	int min, abs;

	if (p=kw_find("update_wantmin")) {
		kw_parse(CF_VALUE, p, &min);
		if (min < 0)
			min = 0;
		if (min > MAXNOC)
			min = MAXNOC;
	} else
		min = MAXNOC;

	if (p=kw_find("update_abswantmin")) {
		kw_parse(CF_VALUE, p, &abs);
		if (abs < 1)
			abs = 1;
		if (abs > MAXNOC)
			abs = MAXNOC;
	} else
		abs = 1;

	*absmin = abs;
	return(min);
}

static int
demand_update_time(now)
	time_t *now;
{
	struct tm *tm;
	s_char *p;
	int curtime;
	int hour[2];

	tm = localtime(now);
	curtime = tm->tm_min + tm->tm_hour * 60;
	if (!(p=kw_find("update_demandtimes")))
		return(1);
	while (p = kw_parse(CF_TIMERANGE, p, &hour[0])) {
		if (curtime >= hour[0] && curtime < hour[1])
			return(1);
	}
	return(0);
}
#endif /* DEMANDUPDATE */

/* When is the next regularly scheduled update from now. */
static int
regular_update_time(now, tim, delta)
	time_t *now, *tim, *delta;
{
	extern int s_p_etu;
	extern int etu_per_update;
	extern int adj_update;
	s_char *p;
	time_t tw;
	int secs_per_update;

	if (p=kw_find("s_p_etu"))
		kw_parse(CF_VALUE, p, &s_p_etu);
	if (p=kw_find("etu_per_update"))
		kw_parse(CF_VALUE, p, &etu_per_update);
	if (p=kw_find("adj_update"))
		kw_parse(CF_VALUE, p, &adj_update);

	tw = *now + adj_update;
	secs_per_update = etu_per_update * s_p_etu;
	*delta = secs_per_update - (tw % secs_per_update);
	*tim = *now + *delta;
}

/* Is this a valid time for a scheduled update. */
static int
scheduled_update_time(now, which)
	time_t *now;
	int *which;
{
	struct tm *tm;
	s_char *p, *p1;
	int curtime;
	int hour;
	int hourslop;

	*which = -1;
	if (!(p=kw_find("update_times")))
		return(0);

	hourslop = 5;
	if (p1=kw_find("update_timeslop"))
		kw_parse(CF_VALUE, p1, &hourslop);

	tm = localtime(now);
	curtime = tm->tm_min + tm->tm_hour * 60;
	while (p = kw_parse(CF_TIME, p, &hour)) {
		(*which) ++;
		if (curtime >= hour &&
		    curtime < hour+hourslop)
			return(1);
	}

	return 0;
}

static int
next_scheduled_time(now, tim, delta)
	time_t *now, *tim, *delta;
{
	struct tm *tm;
	s_char *p;
	int curtime;
	int hour;
	int mintime;

	if (!(p=kw_find("update_times")))
		return(0);

	tm = localtime(now);
	curtime = tm->tm_min + tm->tm_hour * 60; /* now - in minutes */
	mintime = curtime + 24*60+1; 		/* start with now + 1 day */
	while (p = kw_parse(CF_TIME, p, &hour)) {
		if (hour <= curtime)
			hour += 24 * 60; 	/* this must be tomorrow */
		if (hour < mintime)
			mintime = hour;  	/* this is best bet so far */
	}
	*delta = 60*(mintime - curtime);
	*tim = *now + *delta - tm->tm_sec;
	return(1);
}

#ifdef DEMANDUPDATE
int
demand_update_want(want, pop, which)
	int *want, *pop;
	int which;
{
	natid	cn;
	struct	natstr *natp;
	int	totpop;
	int	totwant;
	int	whichwants;

	whichwants = totpop = totwant = 0;
	for (cn = 1; natp = getnatp(cn); cn++) {
		/* Only countries which are normal. */
		/* Should probably include sanctuaries ..... */
		if (((natp->nat_stat & NORM) == NORM) &&
			((natp->nat_stat & GOD) != GOD)) { 
			totpop++;
			if ((natp->nat_update & WUPD_WANT) == WUPD_WANT) {
				totwant++;
				if (which == cn)
					whichwants++;
			}
		}
	}
	*want = totwant;
	*pop = totpop;
	return(whichwants);
}

static int
demand_check()
{
	struct	natstr *natp;
	int min, absmin;
	int want, pop, cn, maxmissed, veto;
	time_t    now;
	long	cur;
	extern long	last_demand_update;

	time(&cur);

/*
	if (last_demand_update == 0){
		natp=getnatp(0);
		last_demand_update = natp->nat_reserve;
	}

	logerror("last_demand_update = %d\n",last_demand_update);
	logerror("update_between = %d\n",update_between());
	logerror("now = %d\n",cur);
	diff = (cur-(last_demand_update + update_between()));
	logerror("diff = %d\n",diff);
	if (diff >= 0){
		logerror("Forced update!\n");
		last_demand_update = cur;
		for (cn = 1; natp = getnatp(cn); cn++){
			if (((natp->nat_stat & NORM) == NORM)  &&
				((natp->nat_stat & GOD) != GOD)){
				natp->nat_missed = 0;
			}
		}
		return(1);
	}

	logerror("No forced update!\n");
*/
	min = update_wantmin(&absmin);
	if (0 == min) {
		logerror("no demand update allowed, wantmin = 0");
		return(0);
	}

	demand_update_want(&want, &pop, 0);
	if ((want < absmin) || (want < min)) {
		logerror("no demand update, want = %d, min = %d, abs = %d",
				want, min, absmin);
		return(0);
	}

	time(&now);
	if (!demand_update_time(&now)) {
		logerror("no demand update, not within hours allowed.");
		return(0);
	}


	veto=0;
	maxmissed = update_maxmissed();
	for (cn = 1; natp = getnatp(cn); cn++){
		if (((natp->nat_stat & NORM) == NORM)  &&
			((natp->nat_stat & GOD) != GOD)){
			if (natp->nat_missed >= maxmissed)
				veto=cn+1;
		}
	}

	if (veto){
		logerror("no demand update, %d has missed more than %d updates",
			veto-1, maxmissed);
		return(0);
	}

	last_demand_update = cur;
	natp=getnatp(0);
	/* A dumb way to do it, but simple */
	last_demand_update = natp->nat_reserve;
	return(1);
}

/* Check if enough countries want an update,
 * and if demand updates are allowed now.
 */
int
demandupdatecheck()
{
	int pol;

	init_updatecheck();

	pol = update_wantpol();
	if (UDDEM_COMSET != pol) {
		logerror("no demand update, not policy.");
		return(0);
	}

	return(demand_check());
}

update_maxmissed()
{
	int	max;
	s_char	*p1;

	max = 999;
	if (p1=kw_find("update_missed"))
		kw_parse(CF_VALUE, p1, &max);

	return(max);
}

update_between()
{
	int	max;
	s_char	*p1;

	max = 99999;
	if (p1=kw_find("update_between"))
		kw_parse(CF_VALUE, p1, &max);

	return(max);
}

#endif /* DEMANDUPDATE */

/* Is it time for a regular or scheduled update?
 * As well, if none of the above, check to see if
 * a demand update can occur.
 */
int
updatetime(now)
	time_t *now;
{
	int	pol, which;

	init_updatecheck();

	pol = update_policy();

        if (opt_BLITZ && pol == UDP_BLITZ) {
                logerror("BLITZ Update.");
                return(1);
        }

	if (UDP_NORMAL == pol) {
		logerror("Regular update, etu type.");
		return(1);
	}

	if (UDP_TIMES == pol) {
		if (scheduled_update_time(now, &which)) {
			logerror("Scheduled update, %d.", which);
			return(1);
		}
	}
#ifdef DEMANDUPDATE
	if (demand_check()) {
		logerror("Demand update, at check time.");
		return(1);
	}
#endif /* DEMANDUPDATE */
	return(0);
}

/* And a routine to return all the info. */
/* Bleah */
int
demand_update_info(pol, wpol, min, absmin)
	int *pol, *wpol, *min, *absmin;
{
	init_updatecheck();
	*pol = update_policy();
#ifdef	DEMANDUPDATE
	*wpol = update_wantpol();
	*min = update_wantmin(absmin);
#else	/* DEMANDUPDATE */
	*wpol = 0;
	*min = 0;
	*absmin = MAXNOC;
#endif	/* DEMANDUPDATE */
}


/* Return the time, and delta seconds, of the next update.
 * If the policy is no regular updates, return the time of
 * the next possible check.
 */
int
next_update_time(now, tim, delta)
	time_t *now;	/* From when */
	time_t *tim;	/* Time of next update */
	time_t *delta;	/* Seconds till next update */
{
	time_t stim, sdelta;
	time_t blitz_time;

	init_updatecheck();

	switch (update_policy()) {
	case UDP_NORMAL:
		regular_update_time(now, tim, delta);
		break;
	case UDP_TIMES:
		if (!next_scheduled_time(now, tim, delta))
			regular_update_time(now, tim, delta);
		break;
        case UDP_BLITZ:
                blitz_time = update_blitz();
                *delta = (blitz_time * 60) - (*now % (blitz_time * 60));
                *tim = *now + *delta;
                break;

	case UDP_NOREG:
	default:
		regular_update_time(now, tim, delta);
		if (next_scheduled_time(now, &stim, &sdelta)) {
			if (*delta > sdelta) {
				*delta = sdelta;
				*tim = stim;
			}
		}
		break;
	}
}

int
next_update_check_time(now, tim, delta)
	time_t *now;	/* From when */
	time_t *tim;	/* Time of next update */
	time_t *delta;	/* Seconds till next update check */
{
	time_t stim, sdelta;
	time_t blitz_time;

	init_updatecheck();

	switch (update_policy()) {
	case UDP_NORMAL:
		regular_update_time(now, tim, delta);
		break;
        case UDP_BLITZ:
                blitz_time = update_blitz();
                *delta = (blitz_time * 60) - (*now % (blitz_time * 60));
                *tim = *now + *delta;
                break;
	case UDP_TIMES:
	case UDP_NOREG:
	default:
		regular_update_time(now, tim, delta);
		if (next_scheduled_time(now, &stim, &sdelta)) {
			if (*delta > sdelta) {
				*delta = sdelta;
				*tim = stim;
			}
		}
	}
}
#endif	/* UPDATESCHED */

int
updates_disabled()
{
        extern  s_char *disablefil;
	int	fd;

        if ((fd = open(disablefil, O_RDONLY, 0)) < 0)
                return 0;
	close(fd);
	return 1;
}
