/* ReminderCheck checks and alerts about event generated with Reminder */

/* $Id: CheckMain.c,v 1.13 1993/04/09 15:19:25 Matti_Rintala Exp $ */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <time.h>
#include <exec/types.h>
#include <dos/dosextens.h>
#include <utility/tagitem.h>
#include <exec/libraries.h>
#include <intuition/intuition.h>

#include <libraries/reqtools.h>

#ifdef __SASC
#include <proto/exec.h>
#include <proto/intuition.h>
#include <proto/reqtools.h>
#endif

#ifdef _DCC
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/intuition_protos.h>
#include <clib/reqtools_protos.h>
#include <dos/dos.h>
#include <clib/dos_protos.h>
#include <workbench/startup.h>

struct Library *CxBase = NULL;	/* DICE does not auto open commodities.library */
struct Library *IconBase = NULL; /* Nor icon.library */

/* DICE does not have difftime(), although its prototype is in time.h. This is
   a bug in DICE, so we'll have to declare difftime() as macro */
#define difftime(a,b) ((double)(a) - (double)(b))

#endif

#include "Constants.h"
#include "CalcDate.h"
#include "ARexx.h"

/* Responses from makealarm */
#define QUIT 0
#define ACK 2

/* Maximum width of requester */
#define MAXWIDTH 30

char *VersionStr = "$VER: ReminderCheck 1.10";

static int makealarm(time_t *date, const char *text);
static void CloseLibraries(void);

static void putack(struct EventNode *node, time_t acked);

char *filename;			/* Filename of the database file */

BOOL noreqtools = FALSE;	/* TRUE if NOREQTOOLS tooltype is present */
BOOL noarexx = FALSE;		/* TRUE if NOAREXX tooltype is present */

#ifdef _DCC
static struct FileLock *oldlock = NULL;

/* DICE uses different entry point for workbench startup */
int wbmain(struct WBStartup *msg) {

  /* We have to change to correct directory */
  oldlock = CurrentDir(msg->sm_ArgList[0].wa_Lock);
  return main(0, (char **)msg);	/* Simply call main() */
}
#endif

/* ReqTools library is only opened, if alerts are needed */
struct ReqToolsBase *ReqToolsBase = NULL;

int main(int argc, char **argv) {

  UBYTE **ttypes;
  FILE *infile;
  long fpos;
  struct EventNode *node;
  time_t today, tim, alarm, acked, stamp;
  double diff;
  int response, interval;
  size_t size;
  
  atexit(CloseLibraries);	/* Close libraries on exit */

#ifdef _DCC
  /* With DICE we have to open commodities.library and icon.library, too */
  if (!(CxBase = OpenLibrary("commodities.library", 0))) {
    printf("Can't open commodities.library!\n");
    exit(-1);
  }
  if (!(IconBase = OpenLibrary("icon.library", 0))) {
    printf("Can't open icon.library!\n");
    exit(-1);
  }
#endif

  /* Get today's date */
  gettoday(&today);

  /* Get memory for one node */
  if ((node = malloc(sizeof(struct EventNode))) == NULL)
    exit(-1);

  /* Parse arguments */
  ttypes = ArgArrayInit(argc, argv);
  /* Try to find database filename */
  filename = ArgString(ttypes, FILETYPE, DEFAULTFILE);
  /* And checking interval */
  interval = ArgInt(ttypes, INTRVLTYPE, DEFAULTINTRVL);
  /* And whether we use ReqTools.library or not */
  noreqtools = (ArgString(ttypes, NOREQTOOLSTYPE, NULL) != NULL);
  /* And whether we use ARexx or not */
  noarexx = (ArgString(ttypes, NOAREXXTYPE, NULL) != NULL);

  /* Try to open database */
  infile = fopen(filename, "r+b");

  /* Clear up argument parsing */
  ArgArrayDone();

  /* Exit if database open failed */
  if (infile == NULL)
    exit(0);

  /* Initialize ARexx, if permitted */
  if (!noarexx)
    initarexx();

  /* Check the stamp */
  if (fread(&stamp, 1, sizeof(time_t), infile) == sizeof(time_t)) {
    tim = time(NULL);
    diff = difftime(tim, stamp); /* Current interval in seconds */
    if (diff >= (double)interval * 3600.0) {
      /* Write new stamp */
      fseek(infile, 0, SEEK_SET);
      fwrite(&tim, 1, sizeof(time_t), infile);
      fflush(infile);

      /* Go through all events in file */
      while (TRUE) {
	/* Read event to node */
	clearerr(infile);
	while (TRUE) {
	  fpos = ftell(infile);	/* Remember position */
	  size = fread(SAVEADDR(node), 1, SAVELEN(node), infile);
	  /* Stop looping if error or wrong number of bytes read */
	  if (ferror(infile) || size < SAVELEN(node))
	    break;
	  /* Also if event is not deleted */
	  if (node->mode != DELETEDMODE)
	    break;
	}
	/* Stop looping if end-of-file */
	if (ferror(infile) || size < SAVELEN(node))
	  break;
	
	/* Get event's next alarm date */
	makedate(&alarm, &today, node->day, node->month, node->year, node->wday);
	/* And when it has been acknowledged last time */
	makedate(&acked, &today, node->aday, node->amonth, node->ayear, 0);
	
	/* If acked time is the as alarm time, this event has already been acked */
	if (acked == alarm)
	  continue;
	
	/* If difference between now and alarm time is less than 'before'
	   value, alarm is made */
	diff = difftime(alarm, today);
	if (diff < 0.0 || diff > ((double)node->before)*((double)3600*24)) {
	  /* If next alarm is too far away or in past, try previous alarm */
	  makeprevdate(&alarm, &today, node->day, node->month,
		       node->year, node->wday);
	  
	  /* If already acked, give up */
	  if (acked == alarm)
	    continue;
	  
	  /* If difference between alarm and now is more than 'after' value,
	     no alarm is made */
	  diff = difftime(today, alarm);
	  if (diff < 0.0 || diff > (double)node->after*(3600.0*24.0))
	    continue;
	}
	/* Use ARexx, if requested */
	makearexx(node, &alarm);
	/* Make the alarm requester */
	response = makealarm(&alarm, node->text);
	/* If response was 'quit', stop making alarms */
	if (response == QUIT)
	  break;
	/* If it was 'acknowledged', write the ack information to file */
	if (response == ACK) {
	  /* If autodelete flag is set, whole event can be deleted */
	  if (node->autodelete)
	    node->mode = DELETEDMODE;
	  else
	    putack(node, alarm);
	  
	  fseek(infile, fpos, SEEK_SET);
	  size = fwrite(SAVEADDR(node), 1, SAVELEN(node), infile);
	  /* Stop if error */
	  if (ferror(infile) || size < SAVELEN(node))
	    break;
	  fflush(infile);
	}
      }
    }
  }

  fclose(infile);
  free(node);
  
  exit(0);
}
  

/* putack puts the acknowledgement date to EventNode */
static void putack(struct EventNode *node, time_t acked) {

  struct tm *tmptr;

  tmptr = localtime(&acked);	/* Change to struct */
  node->aday = tmptr->tm_mday;
  node->amonth = tmptr->tm_mon + 1;
  node->ayear = tmptr->tm_year + 1900;
}

static char buffer[TEXTLEN+19];	/* Place for requester text */

/* makealarm puts up an alarm requester */
static int makealarm(time_t *date, const char *text) {

  char *str = buffer;
  struct tm *d;
  int i;
  
  /* Calculate date string */
  d = localtime(date);
  str += strftime(str, MAXWIDTH, "%a %d-%b-%Y :\n", d); /* Print date */

  /* divide text into lines */
  while (strlen(text) > MAXWIDTH) {
    /* Find first space in the end of line */
    for (i = MAXWIDTH; i > 0 && text[i] != ' '; i--);
    if (i < 2)
      i = MAXWIDTH;		/* If no space found */

    strncpy(str, text, i);	/* Copy the line into buffer */
    str += i;
    text += i;
    *str++ = '\n';		/* Add linefeed to end of line */

    while (*text != '\0' && *text == ' ')
      text++;			/* Skip spaces in the beginning of new line */
  }

  if (*text != '\0')
    strcpy(str, text);		/* Copy the last partial line */

  /* Use ReqTools or EasyRequest depending on toolstype/cmdarg */
  if (noreqtools) {
    /* Structure for Intuition EasyRequest */
    struct EasyStruct es = {sizeof(struct EasyStruct), 0, "Reminder",
			      buffer, "Go away!|Ackn|Quit!"};

    return (int)EasyRequest(NULL, &es, NULL, NULL);
  }
  else {
    /* Open ReqTools library if not already open */
    if (ReqToolsBase == NULL) {
      if ((ReqToolsBase = (struct ReqToolsBase *)
	   OpenLibrary(REQTOOLSNAME, REQTOOLSVERSION)) == NULL)
	return QUIT;		/* If cannot open, quit program */
    }

    /* Make the alarm requester */
    return (int)rtEZRequestTags(buffer, "_Go away!|_Ackn|_Quit!", NULL, NULL,
				RT_PubScrName, (Tag)"Workbench",
				RT_ReqPos, REQPOS_CENTERSCR,
				RT_Underscore, (Tag)'_', 
				RTEZ_Flags, EZREQF_CENTERTEXT, TAG_END);
  }
}

/* CloseLibraries simply closes the opened libraries */
static void CloseLibraries(void) {

  if (ReqToolsBase != NULL)
    CloseLibrary((struct Library *)ReqToolsBase);

  /* Release ARexx */
  releasearexx();

#ifdef _DCC
  /* With DICE we have to close commodities.library and icon.library, too */
  if (CxBase != NULL)
    CloseLibrary(CxBase);

  if (IconBase != NULL)
    CloseLibrary(IconBase);

  /* Let's also return us to correct directory */
  if (oldlock != NULL)
    CurrentDir(oldlock);

#endif

}
