/
dirt31/
dirt31/bin/
#include "kernel.h"
#include <sys/signal.h>
#include <sys/time.h>
#include <errno.h>
#include "mobile.h"
#include "weather.h"
#include "timing.h"
#include "bprintf.h"

#define  SECS_IN_A_MIN          60
#define  SECS_IN_AN_HOUR        (SECS_IN_A_MIN*60)
#define  SECS_IN_A_DAY          (SECS_IN_AN_HOUR*24)
#define  SECS_IN_A_WEEK         (SECS_IN_A_DAY*7)

#define  WARNING_1  15    /* Minutes before closing time to give warnings.  */
#define  WARNING_2   3    /* Only used if the mud is not open 24 hrs. a day */

extern char *sys_errlist[];

#define NUM_INTERV 30

static int wrap = 0; /* Number of times we wrap around */
int intervals = 0;
struct tm open_times[NUM_INTERV];

#define MUD_ALWAYS   0
#define MUD_OPEN     1
#define MUD_WARNING1 2
#define MUD_WARNING2 3
#define MUD_CLOSED   (-1)

int open_state = MUD_OPEN;
int       wrap_point = -1;

/************************************************************************
 * 									*
 *  "DIRT"'s system for controlling it's opening and closing times.     *
 *									*
 *  mud_open() is called. It reads DATA/hours and returns true if mud   *
 *  is open, false if not. Read DATA/=README= for the format. The       *
 *  opening time (if closed) or closing time (if open) is placed in     *
 *  the global variable 'next_event' and checked regularly. Two         *
 *  warnings are given before the players are kicked off at closing     *
 *  time.								*
 ***********************************************************************/

time_t  next_event;      /* closing time if open, opening time if closed. */


/* Return True if the time represented by a is later then that of b :
 * This procedure is to be used by the boot, where one and only one
 * interval may be later so one occurence should return true,
 * remaining false.
 * (We are only using the day, hour and min fields in tm.)
 */
static Boolean blater( struct tm *a, struct tm *b )
{
  
  if ( a->tm_wday > b->tm_wday
      ||
      a->tm_wday == b->tm_wday && a->tm_hour > b->tm_hour
      ||
      a->tm_wday == b->tm_wday && a->tm_hour == b->tm_hour &&
      a->tm_min > b->tm_min) return True;

  ++wrap;
  if (wrap_point >= 0) return False;

  wrap_point = a - open_times;

  return True;
}

/* Return True if the time represented by a is later then that of b :
 * (We are only using the day, hour and min fields in tm.)
 */
static Boolean later( struct tm *a, struct tm *b )
{
  
  if ( a->tm_wday > b->tm_wday
      ||
      a->tm_wday == b->tm_wday && a->tm_hour > b->tm_hour
      ||
      a->tm_wday == b->tm_wday && a->tm_hour == b->tm_hour &&
      a->tm_min > b->tm_min) return True;

  return False;
}


/* Return True if the time t occurs after a and before b, (between a and b).
 */
static Boolean between( struct tm *t, struct tm *a, struct tm *b )
{
    return ( later(a,b) ? !between(t,b,a) : later(t,a) && !later(t,b) );
}


/* Return the time difference between two events in seconds.
 */
static time_t diff( struct tm *from, struct tm *to )
{
    if (later(from,to))
        return( SECS_IN_A_WEEK - diff(to,from) );
    else
        return( (to->tm_wday - from->tm_wday)*SECS_IN_A_DAY +
                (to->tm_hour - from->tm_hour)*SECS_IN_AN_HOUR +
                (to->tm_min  - from->tm_min )*SECS_IN_A_MIN );
}


static int next_time(struct tm *now)
{
  struct tm *u;
  int x;

  for (x = 0, u = open_times; x < intervals; x++, u++) {
    if (later(u,now)) break;
  }
  if (x == 0) {
    now->tm_wday += 7;
    for (x = 0, u = open_times; x < intervals; x++, u++) {
      if (later(u,now)) break;
    }
    now->tm_wday -= 7;
  }
  if (x == intervals) x = 0;
  return x;
}

static Boolean ok_week_time(struct tm *t,char *d, char *fname, int lineno)
{
  static char *wdays[] =  { "Sunday",   "Monday", "Tuesday", "Wednesday",
                              "Thursday", "Friday", "Saturday", TABLE_END };

  int x;

  if ((x = tlookup(d,wdays)) < 0) {
    printf("%s:%d - No such day: %s\n", fname, lineno, d);
    return False;
  }
  t->tm_wday = x;
  if (t->tm_hour < 0 || t->tm_hour >= 24 || t->tm_min < 0 || t->tm_min >= 60) {
    printf("%s:%d -  Illegal time: %d:%d\n",
	   fname, lineno, t->tm_hour, t->tm_min);
    return False;
  }
  return True;
}

static Boolean is_later(struct tm *b, struct tm *a,
			char *fname, int lineno,int x)
{
  if (blater(b,a)) return True;
  mudlog("%s:%d - Illegal format %s \'->\', not increasing times.",
	 fname, lineno, (x == 0) ? "before" : "after");
  return False;
}

/* If AberMUD is open, return True and the closing-time in the variable
 * pointed to by next_ev (global UNIX time). If the game is closed,
 * return False, and the opening time in the variable pointed to by next_ev.
 */
Boolean mud_open(time_t *next_ev, time_t *current_time)
{
  struct tm     *tm_now, *i;
  int           ct;
  time_t        now = *current_time;

  now = round_to_min(now);
  tm_now = localtime( &now );

  if (intervals > 0) {
    ct = next_time(tm_now);
    *next_ev = now + diff( tm_now, open_times + ct);
    return ODD(ct);
  } else {
    *next_ev = TIME_NEVER;
    return True;
  }
}

static int xboot_times(FILE *f,char *fname)
{
  struct tm        *i = open_times;
  FILE             *fl;
  char             dnamefrom[20];
  char             dnameto[20];
  char             b[256];
  int              status, ct = 0, lineno = 0;


  if ((fl = f) == NULL && (fl = fopen(fname, "r")) == NULL) {
    intervals      = 0;
    next_event     = TIME_NEVER;
    open_state     = MUD_ALWAYS;
    return 0;
  }
  while (fgets(b,sizeof b,fl)) {
    lineno++;
    if (ct >= NUM_INTERV) {
      printf("%s:%d - Too many intervals in hours file.\n",
	     fname, lineno);
      fclose(fl);
      return -1;
    }
    status = sscanf(b,"%s %d:%d %*s %s %d:%d\n",
		    dnamefrom, &i->tm_hour, &i->tm_min,
		    dnameto,   &i[1].tm_hour,   &i[1].tm_min);
    if (status <= 0) continue;
    if (status != 6) {
      fclose(fl);
      printf( "status == %d\n", status);
      printf( "%s:%d - Illegal format in hours file: %s", fname, lineno, b );
      return -1;
    }

    if (!ok_week_time(i,dnamefrom, fname, lineno)
	|| ( (ct > 0) && !is_later(i, i-1, fname, lineno, 0))
	|| !ok_week_time(++i,dnameto, fname, lineno)
	|| !is_later(i,i-1,fname,lineno,1)) {
      fclose(fl);

      return -1;
    }
    ct += 2;
    ++i;
  }
  if ( !is_later(open_times, i - 1, fname, 1, 0) ) {
    fclose(fl);
    return -1;
  }
  if (wrap != 1) {
    printf( "%s:%d - hours file should wrap once.\n", fname, lineno);
    fclose(fl);
    return -1;
  }
  if (!feof(fl)) {
    printf("%s:%d [%d]%s\n", fname, lineno, errno, sys_errlist[errno]);
    fclose(fl);
    return -1;
  }
  if (f == NULL) fclose(fl);
  intervals = ct;
  for (ct = wrap_point; ct < intervals; ct++) {
    open_times[ct].tm_wday += 7;
  }
  return 0;
}
  
int boot_hours(FILE *f, char *fname)
{
  struct tm *i = open_times;
  int k;
  time_t now = time(NULL);

  if ( (k = xboot_times(f, fname)) < 0 ) {
    intervals      = 0;
    next_event     = TIME_NEVER;
    open_state     = MUD_ALWAYS;
  } else if (open_state == MUD_ALWAYS) return k;
  else if (mud_open(&next_event, &now)) open_state = MUD_OPEN;
  else open_state = MUD_CLOSED;
    
  return k;
}


/* Check if closing time has arrived. If so, kick everyone off (except
 * the 'masteruser' if he is on). Give 2 warnings ahead of time.
 */
static void check_if_closed(time_t *now)
{
  int            time_left;

  if (next_event == TIME_NEVER) 
    return;

  time_left = next_event - *now;

  switch(open_state) {
  case MUD_ALWAYS: return;
  case MUD_OPEN:
    if (time_left < WARNING_1 * SECS_IN_A_MIN) {
      bprintf("  MUD is closing in %s.\a\n", 
	      sec_to_str(round_to_min(time_left)) );
      open_state = MUD_WARNING1;
    }
    break;
  case MUD_WARNING1:
    if (time_left < WARNING_2 * SECS_IN_A_MIN) {
      bprintf("  MUD is closing in %s.\a\n", 
	      sec_to_str(round_to_min(time_left)) );
      open_state = MUD_WARNING2;
    }
    break;
  case MUD_WARNING2:
    if (time_left < 0) {
#if 0
/*    remove all players from the game except operators */
#endif
      broad("\t\tMUD has closed. Thank you for playing...");
      open_state = MUD_CLOSED;
      if (mud_open(&next_event, now)) {
	mudlog( "Internal error: mud_open is not False" );
      }
    }
    break;
  case MUD_CLOSED:
    if (time_left < 0) {
      open_state = MUD_OPEN;
      if (!mud_open(&next_event, now)) {
	mudlog( "Internal error: mud_open is not True" );
      }
    }
  }
}


/* Return a pointer to a string containing the a date, without the \n
 * supplied by ctime(3). If the argument is 0, use the current local time.
 */
char *time2ascii(time_t t)
{
    char   *str;

    if (t == 0)
        t = global_clock;

    *strchr((str = ctime(&t)), '\n') = '\0';

    return (t == TIME_UNAVAIL) ? "Not Available" : str;
}


/* The TIME command. List game time elapsed, current time, last reset
 * and closing time if applicable.
 */
void timecom(void)
{
    FILE    *a;
    time_t  x;

    bprintf("Elapsed Time : ");
    eltime();

    bprintf("Current Time : %s\n", time2ascii(TIME_CURRENT));

    bprintf("Last Reset   : %s", ctime(&last_reset));

    if (next_event != TIME_NEVER) {
	bprintf("Closing Time : %s\n", time2ascii(next_event));
    }
}



/* Prints the game time elapsed. Called by timecom() and main.
 */
void eltime(void)
{
    long int et, i;

    switch (et = gametime())
    {
      case TIME_NEVER:
        bprintf("AberMUD has yet to ever start!");
        break;
      case TIME_UNAVAIL:
        bprintf("Current time is unavailable!");
        break;
      default:
        if (et > SECS_IN_A_DAY)
            bprintf("Over a day!");
        else
            bprintf("%s", sec_to_str( et ));
        break;
    }
    bprintf("\n");
}


long int gametime( void )
{
	return global_clock - last_reset;
}



/*  Takes a number of seconds as input and converts this to seconds,
 *  minutes, hours, days, which is returned if the pointers != NULL.
 */
static void split_time( long int sec, int *secs, int *min, int *hrs, int *days)
{
	int s = 0, m = 0, h = 0, d = 0;

	if (sec >= SECS_IN_A_DAY) {
		d = sec / SECS_IN_A_DAY;

		sec -= d * SECS_IN_A_DAY;
	}

	if (sec >= SECS_IN_AN_HOUR) {
		h = sec / SECS_IN_AN_HOUR;

		sec -= h * SECS_IN_AN_HOUR;
	}

	if (sec >= SECS_IN_A_MIN) {
		m = sec / SECS_IN_A_MIN;

		sec -= m * SECS_IN_A_MIN;
	}

	s = sec;

	/* Assign return values:
	 */
	if (secs != NULL) *secs = s;
	if (min != NULL)  *min = m;
	if (hrs != NULL)  *hrs = h;
	if (days != NULL) *days = d;
}


/*  Takes a number of seconds as input and returns a pointer to a string
 *  containing the amount of time in english, ex: "2 hours, 3 minutes"..etc..
 */
char *sec_to_str( long int seconds )
{
	static char str[50];

	int sec, min, hrs, days;

	char aux[15];

	*str = '\0';

	split_time(seconds, &sec, &min, &hrs, &days);

	if (days > 0) {
		sprintf(str, "%d day%s", days, (days == 1) ? "" : "s");
	}

	if (hrs > 0) {
		if (days > 0) strcat(str, ", ");
		sprintf(aux, "%d hour%s", hrs, (hrs == 1) ? "" : "s");
		strcat(str, aux);
	}

	if (min > 0) {
		if (days > 0 || hrs > 0) strcat(str, ", ");
		sprintf(aux, "%d minute%s", min, (min == 1) ? "" : "s");
		strcat(str, aux);
	}

	if (sec > 0) {
		if (days > 0 || hrs > 0 || min > 0) strcat(str, ", ");
		sprintf(aux, "%ld second%s", sec, (sec == 1) ? "" : "s");
		strcat(str, aux);
	}

	return( str );
}


/*  Takes a number of seconds as input and returns a pointer to a string
 *  containing hh:mm:ss.
 */
char *sec_to_hhmmss( long int seconds )
{
	static char str[25];

	int sec, min, hrs, days;


	split_time(seconds, &sec, &min, &hrs, &days);

	if (days > 0) {
		hrs += days * 24;
	}

	if (hrs > 0) {
		sprintf(str, "%d:%02d:%02d", hrs, min, sec);
	}
	else
	if (min > 0) {
		sprintf(str, "%d:%02d", min, sec);
	}
	else {
		sprintf(str, "%d", sec);
	}

	return( str );
}


/* Returns time t rounded to the nearest minute.
 */
time_t round_to_min( time_t t )
{
	t += SECS_IN_A_MIN / 2;

	return( t -= t % SECS_IN_A_MIN );
}


/* Like ctime(3) but strips the seconds, year-part and the \n.
 */
char *my_ctime( time_t *clock )
{
	char *t = ctime(clock);
	t[16] = '\0';
	return t;
}


void set_timer(void)
{
	alarm(TIMER_INTERRUPT);
}

void on_timer(void)
{
  static long int n = 0;
  static const int interupts_per_hour = 3600/TIMER_INTERRUPT;
  int plx;

  if (++n % interupts_per_hour != 0 || time(&global_clock) == -1) {
	  global_clock += TIMER_INTERRUPT;
  }

  check_if_closed(&global_clock);
  move_mobiles();
  regenerate();
  special_events(SP_ALL);
  longwthr();

  for (plx = 0; plx < max_players; plx++) {
    setup_globals(plx);
    exectable(inter,0);
  }
}


#if 0
void on_follow()
{
  int ct;

  on_trace();
  if (!i_follow)
    return;
  if (in_fight || fpbn(folnam) != i_folcode) {
    bprintf("You can no longer follow \001P%s\377.\n", folnam);
    i_follow = False;
    return;
  }
  if (ploc(mynum) == ploc(i_folcode))		/* Now to find the guy */
    return;
  if (ltstflg(ploc(i_folcode), LFL_ON_WATER))	/* Don't follow to sea! */
    return;
  for (ct = 0; ct < 6; ct++) {
    if (getexit(ploc(mynum), ct) == ploc(i_folcode)
	|| doorthru(getexit(ploc(mynum), ct)) == ploc(i_folcode)) {
      dodirn(ct + 2);
      return;
    }
  }
  bprintf("You can no longer follow \001P%s\377.\n", folnam);
  strcpy(folnam, "");
  i_folcode = -1;
  i_follow = False;
}

int doorthru(int x)
{
  if (x < 1000 || x > 9999)
    return x;
  if (state(x - 1000))
    return 0;
  return oloc((x - 1000) ^ 1);
}

void on_trace()
{
  if (trace_item == -1)
    return;
  if (trace_class == 1) {
    if (trace_loc != oloc(trace_item)
	|| trace_carrf != ocarrf(trace_item)) {
      bprintf(">>%s now at ", oname(trace_item));
      trace_carrf = ocarrf(trace_item);
      trace_loc = oloc(trace_item);
      desrm(trace_loc, trace_carrf);
    }
    return;
  }
  if (EMPTY(pname(trace_item))) {
    bprintf(">>Can no longer trace %s\n", pname(trace_item));
    trace_item = -1;
    return;
  }
  if (ploc(trace_item) != trace_loc) {
    bprintf(">>\001p%s\377 now at ", pname(trace_item));
    trace_loc = ploc(trace_item);
    desrm(trace_loc, IN_ROOM);
  }
}
#endif

 

#if 0
/* New, from Vitastjern: */

void on_follow()
{
  int ct;
  
  /*
   * on_trace();
   */
  if (cur_player->i_follow == -1)
    return;
  if (pfighting(mynum) >= 0)
    {
      sendf(mynum,"You can no longer follow \001P%s.\003\n", pname(cur_player->i_follow));
      cur_player->i_follow = -1;
      return;
    }
  if((!IS_ON(cur_player->i_follow)) || (pvis(cur_player->i_follow)>plev(mynum)))
    {
      sendf(mynum,"Your leader has departed, you can no longer follow.\n");
      cur_player->i_follow = -1;
      return;
    }

  if (ploc(mynum) == ploc(cur_player->i_follow))/* Now to find the guy */
    return;
  if (ltstflg(ploc(cur_player->i_follow), LFL_ON_WATER))/* Don't follow to sea! */
    return;
  for (ct = 0; ct < 6; ct++)
    {
      if (getexit(ploc(mynum), ct) == ploc(cur_player->i_follow)
	  || doorthru(getexit(ploc(mynum), ct)) == ploc(cur_player->i_follow))
	{
	  dodirn(ct + 2);
	  bprintf( "\r%s", cur_player->cprompt);
	  return;
	}
    }
  sendf(mynum,"You can no longer follow \001P%s\003.\n", pname(cur_player->i_follow));
  if(*(see_name(cur_player->i_follow, mynum))==0)
    sendf(cur_player->i_follow,"Your partner can no longer follow you.\n", see_name(cur_player->i_follow, mynum));
  else
    sendf(cur_player->i_follow,"%s can no longer follow you.\n", see_name(cur_player->i_follow, mynum));
  cur_player->i_follow = -1;
}

int doorthru(int x)
{
  if ( x >= DOOR && x < EDOOR)
    {
      int drnum, droff;
      
      drnum = x - DOOR;
      droff = drnum^1;
      if (!state(drnum))
	return oloc(droff);
    }
}


void on_help()
{
  if(phelping(mynum)>=0)
    {
      if(!IS_ON(phelping(mynum)) || (pvis(phelping(mynum))>plev(mynum)))
	{
	  sendf(mynum,"Your leader has departed, you can no longer help.\n");
	  setphelping(mynum, -1);
	  return;
	}
      if(ploc(mynum)!=ploc(phelping(mynum)))
	{
	  sendf(mynum,"You can no longer help \001p%s\003.\n", pname(phelping(mynum)));
	  if(!IS_ON(mynum))
	    {
	      sendf(phelping(mynum),"You are now beyond any help.\n");
	      setphelping(mynum, -1);
	      return;
	    }
	  sillytp(phelping(mynum),"can no longer help you.");
	  setphelping(mynum, -1);
	}
    }
}
#endif