#ifndef lint
static char *RCSid = "$Header: schedule.c,v 1.15 90/04/13 12:25:46 mr-frog Exp $";
#endif

/*
 * schedule.c
 *
 * called by interrupt to either immediately schedule
 * the execution of an update, or to plan on interrupting
 * the currently executing empire job which is blocking
 * execution of the update.
 *
 * Dave Pare, 1986
 *
 * NeXT patch: itimer has a problem on the NeXT.
 *             It thinks 1 second = 1.042 seconds. (something like that)
 *             This little hack forces a call to check for the update
 *             determined by the UPDATE_CHECK time (in seconds).
 *             Someday this should all be #defined but for now it works.
 *             Without this your updates will be on average of
 *             3-5 minutes late depending on how often the game updates.
 *             Even with this code the update might be a bit off
 *             but its better then nothing.  Chad Zabel/93
 *
 * Coder     : Matt Miller
 * Added     : March 30, 1993
 */

#include <stdio.h>
#include "misc.h"
#include "user.h"
#include "tm.h"
#include "file.h"
#include "keyword.h"

#include <fcntl.h>
#include <netinet/in.h>
#include <errno.h>
#include <sys/wait.h>
#include <signal.h>

int	blocked = 0;
int	kind = 0;
int	tm_flags = 0;

#ifdef NeXT
static  timeof_nextupdate = 0;
#endif /* NeXT */

schedule_update()
{
#ifdef	UPDATESCHED
	time_t	now;
	time_t	delta;
	time_t	when;

	blocked = 0;
	tm_flags = 0;
	time(&now);
	next_update_check_time(&now, &when, &delta);

#else	UPDATESCHED
	extern	int s_p_etu;
	extern	int etu_per_update;
	extern	int adj_update;
	time_t	now;
	time_t	delta;
	s_char	*p;
	int	hour[2];
	int	secs_per_update;

#ifdef NeXT
	time_t timeof_lastupdate = 0;
#endif /* NeXT */

	blocked = 0;
	tm_flags = 0;
	time(&now);
	(void) gamehours(now, hour);
	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);
	now += adj_update;
	secs_per_update = etu_per_update * s_p_etu;
	delta = secs_per_update - (now % secs_per_update);
#endif	UPDATESCHED

#ifdef NeXT
	time(&timeof_lastupdate);
	timeof_nextupdate = timeof_lastupdate + delta;
	alarm(UPDATE_CHECK);
	logerror("Next update in %d seconds",delta);
	logerror("Checking updates every %d seconds", UPDATE_CHECK);
#else
	logerror("Next update in %d seconds", delta);
	alarm(delta);
#endif /* NeXT */
}

static	int pid;

int
execute()
{
	extern  int etu_per_update;
	register int i;
	int	lock;
	int	hour[2];
	time_t	now;

	time(&now);

#ifdef NeXT
        /* First check and see if we do want an update not just
         * an UPDATE_CHECK call.
         */
        if (now < timeof_nextupdate){
		if ((diff = timeof_nextupdate - now) > UPDATE_CHECK){
			alarm(UPDATE_CHECK);
			return;
		}else{
			alarm(diff);
			return;
		}
	}

	/* if not back to normal updates */
#endif /* NeXT */
	if (!gamehours(now, hour)) {
		logerror("No update permitted (hours restriction)");
		schedule_update();
		return;
	}
#ifdef	UPDATESCHED
	if ((tm_flags == 0) && !updatetime(&now)) {
		logerror("No update wanted");
		schedule_update();
		return;
	}
#endif	UPDATESCHED
	if (tm_flags == 0) {
		tm_flags = L_NOLOCK;
		lock = 0;
		for (i=0; i<EF_MAX; i++)
			lock += listlock(i);
		if (lock) {
			blocked = 1;
			(void) alarm(10);
			return;
		}
	} else {
		blocked++;
		lock = 0;
		for (i=0; i<EF_MAX; i++) 
			lock += listlock(i);
		if (lock != 0 && blocked > 0) {
			for (i=0; i<EF_MAX; i++) 
				breaklock(i);
			lock = 0;
			io_flush();
		} 
		if (lock != 0) {
			(void) alarm(10);
			return;
		}
	}
	pid = doupdate(PRODUCE, etu_per_update);
}

exec_done()
{
#if defined (hpux) || defined (aix)
	extern	int child();	/* arrgh! */
#endif
	extern	s_char *signame();
	int	n;
	union	wait w;

	while ((n = wait(&w)) != pid && n != -1)
		;
	if (n == -1) {
		logerror("exec_done: wait on %d failed", pid);
		return;
	}
	if (w.w_termsig)
		logerror("update terminated, %s%s", signame(w.w_termsig),
			w.w_coredump ? " (core dumped)" : "");
	else if (w.w_retcode)
		logerror("update terminated, return %d", w.w_retcode);
#if defined(hpux) || defined(aix)
	/* HP-UX can't reinstall signal handler until wait(2) done */
	(void) signal(SIGCLD, child);
#endif
	schedule_update();
}

int
doupdate(kind, etu)
	int	kind;
	int	etu;
{
	extern	s_char updatebin[];
	int	pid;
	s_char	av2[32];
	s_char	*argv[4];

	if ((pid = fork()) < 0) {
		logerror("doupdate: fork failed");
		schedule_update();
		return -1;
	}
	if (pid == 0) {
		argv[0] = "update";
		if (kind == PRODUCE)
			argv[1] = "produce";
		else
			argv[1] = "mob";
		sprintf(av2, "%d", etu);
		argv[2] = av2;
		argv[3] = 0;
		execv(updatebin, argv);
		logerror("exec %s failed", updatebin);
		_exit(1);
		/*NOTREACHED*/
	}
	return pid;
}
