/***********************************************************************
  Timer.c (c) HooverSoft 1993. Freely distributable for non-commercial
  purposes.

  It started as another test program, now it's actually turned into
  something useful. :-)

  Before I say anything else: The timer.device routines were completely
  ripped off from Mike Meyer's "mclk.c". Mike, I'm definitely not going
  to make money out of this, but thanks very much for mclk.c :-)

  Contact me at hoover@mathematik.uni-bielefeld.de if you have questions
  or suggestions for improvements.
***********************************************************************/

 /* Includes that we need */
#include <exec/types.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <devices/timer.h>
#include <stdio.h>


char *_procname = "Timer";

#define WINX 253		/* STD window size */
#define WINY 27
#define STARTGAD 10		/* Gadget ID's */
#define STOPGAD 11
#define BLACK 1			/* Color definitions */
#define WHITE 2
#define GREY 0
#define PURPLE 3
#define GADWIDTH 45		/* Gadget Dims */
#define GADHEIGHT 10
#define LOGNAME "S:Timer.log"	/* Log file name */
static struct timerequest Time_Req;
static struct MsgPort *Timer_Port = NULL, *CreatePort ();

/******************************************************************
  Command line Options and their respective default values
  autostart: NO
  interval:  8 mins
  activescreen: NO
  logging: NO
  x = 10
  y = 10
******************************************************************/

ULONG AUTOSTART = 0, INTERVAL = 8, ACTIVE_SCREEN = 0, LOGGING = 0;
UWORD x = 10, y = 10;

ULONG seconds = 0;		/* total time elapsed */
struct NewWindow nw =
{00, 11,
 /* start LeftEdge, TopEdge */
 WINX, WINY,			/* start Width, Height */
 AUTOFRONTPEN, AUTOBACKPEN,	/* DetailPen, BlockPen */
 GADGETUP | CLOSEWINDOW,	/* IDCMP FLAGS */
 WINDOWDRAG | WINDOWDEPTH | WINDOWCLOSE,
 /* Flags (can be NULL) */
 NULL,				/* Pointer to FirstGadget */
 NULL,				/* no pointer to first CheckMark */
 (UBYTE *) "Timer V1.04 © HooverSoft",      /* Title (can be NULL) */
 NULL,				/* no Pointer to Screen */
 NULL,				/* no Pointer to BitMap */
 -1, -1,
 -1, -1,
 WBENCHSCREEN			/* Type of screen window appears in */
};

 /* Signal masks for our ports */
#define WINMASK 1L << window -> UserPort -> mp_SigBit
#define TMASK 1L << Timer_Port -> mp_SigBit
 /* Global variables */
struct Window *window = NULL;
struct IntuitionBase *IntuitionBase = NULL;
SHORT WHITEvecs[] =
{0, 0, GADWIDTH, 0, 0, 0, 0, GADHEIGHT, 0, 0};
SHORT BLACKvecs[] =
{1, GADHEIGHT, GADWIDTH, GADHEIGHT,
 GADWIDTH, 1, GADWIDTH, GADHEIGHT, 1, GADHEIGHT};
/*
  Borders to achieve 3d lighting effect
  I know this can be done with Gadtools.library, but I still haven't
  got the RKM's
  downside to this method: Font independency becomes a bit harder to
  implement (when it will be done :).
*/
struct Border wb =
{-1, -1, WHITE, BLACK, JAM1, 5, WHITEvecs, NULL};
struct Border bb =
{-1, -1, BLACK, BLACK, JAM1, 5, BLACKvecs, &wb};
struct IntuiText start_t =
{BLACK, WHITE, JAM1, 1, 1, NULL, "Start", NULL};
struct IntuiText stop_t =
{BLACK, WHITE, JAM1, 6, 1, NULL, "Stop", NULL};
struct IntuiText total_t =
{BLACK, GREY, JAM2, 0, 0, NULL, NULL, NULL};
 /* Gadgets */
struct Gadget startg =
{NULL, 6, WINY - 14, GADWIDTH - 1,
 GADHEIGHT, GADGHCOMP, RELVERIFY, BOOLGADGET, (APTR) & bb, NULL, &start_t,
 NULL, NULL, STARTGAD, 0};
struct Gadget stopg =
{(struct Gadget *) &startg, WINX - GADWIDTH - 6,
 WINY - 14, GADWIDTH - 1, GADHEIGHT, GADGHCOMP, RELVERIFY, BOOLGADGET, (APTR)
 & bb, NULL, (struct IntuiText *) &stop_t, NULL, NULL, STOPGAD, NULL};

/***********************************************************************
  close everything and exit
***********************************************************************/
void
clean_exit (int status)
{
  FILE *logfp;		/* Log file pointer */

  if (LOGGING)
    {
      if (!(logfp = fopen (LOGNAME, "a+")))
	{
	  puts ("could not open log file!\n");
	  goto error;
	}
      else
	{
	  static long date = 0, store;

	  date = time (&store);
	  if (seconds > 0)
	    fprintf (logfp, "%4ld mins = %ld unit(s) on %s", (seconds / 60) ? (seconds / 60) : 1, 1 + ((seconds / 60) / INTERVAL), ctime (&date));
	}

      fclose (logfp);
    }				/* end if(LOGGING) */

error:
/*
  Killing the timer device might be done in a more system friendly
  way, but this code is courtesy of Mike Meyer, not me. 8-P
  Main thing is we get "Total used: 0" on "mem -r" in Csh.
*/
  AbortIO ((char *) &Time_Req.tr_node);
  if (Time_Req.tr_node.io_Message.mn_ReplyPort)
    CloseDevice (&Time_Req);
  if (Timer_Port)
    DeletePort (Timer_Port);
  if (window)
    CloseWindow (window);
  if (IntuitionBase)
    CloseLibrary (IntuitionBase);
  exit (status);
}
/***********************************************************************
  Parse command line arguments
***********************************************************************/
void
parse_options (int count, char *args[])
{
  register int i;
  for (i = 1; i < count; i++)
    {
      if (*args[i] == '-')	/* check for option introducer */
	{
	  args[i]++;
	  if (!(strcmp (args[i], "autostart")))
	    {
	      AUTOSTART = 1;
	      total_t.FrontPen = PURPLE;
	      continue;
	    }
	  if (!(strcmp (args[i], "firstscreen")))
	    {
	      ACTIVE_SCREEN = 1;
	      continue;
	    }

	  if (!(strcmp (args[i], "interval")))
	    {
	      INTERVAL = atoi (args[i + 1]);
	      INTERVAL = (INTERVAL == 0) ? 12L : INTERVAL;
	      i++;
	      continue;
	    }

	  if (!(strcmp (args[i], "log")))
	    {
	      LOGGING = 1;
	      continue;
	    }

	  if (!(strcmp (args[i], "x")))
	    {
	      x = atoi (args[i + 1]);
	      i++;
	      continue;
	    }

	  if (!(strcmp (args[i], "y")))
	    {
	      y = atoi (args[i + 1]);
	      i++;
	      continue;
	    }

	  
	  exit (RETURN_WARN);
	}			/* end if(option_introducer) */
    }				/* end for */
  return;
}
/***********************************************************************
  Print current data
**********************************************************************/
void
show_data (long time)		/* Passed: seconds */
{
  static char string[80];	/* Time string */
  register char secs[3], mins[3], hours[3];	/* place to hold components */
  register ULONG s, m, h;
  /* Calculate current time */
  s = time % 60;
  m = (time / 60) % 60;
  h = time / 3600;
  /* convert to string  */
  sprintf (secs, (s < 10) ? "0%1ld" : "%2ld", s);
  sprintf (mins, (m < 10) ? "0%1ld" : "%2ld", m);
  sprintf (hours, (h < 10) ? "0%1ld" : "%2ld", h);
  sprintf (string, "%2ld/%2ld  %2s:%2s:%2s", INTERVAL,
	   1 + ((time / 60) / INTERVAL), hours, mins, secs);
  total_t.IText = string;
  PrintIText (window->RPort, &total_t, 6 + GADWIDTH + 12, 14);
  return;
}
/***********************************************************************
 Init timerequest structure
***********************************************************************/
void
start_timer ()
{
  if ((Timer_Port = CreatePort ("Timer Port", 0)) == NULL)
    {
      puts ("Can't create timer port\n");
      clean_exit (50);
    }

  if (OpenDevice (TIMERNAME, UNIT_VBLANK, (char *) &Time_Req, 0) != NULL)
    {
      puts ("Can't open timer device!");
      clean_exit (20);
    }


  Time_Req.tr_node.io_Message.mn_ReplyPort = Timer_Port;
  Time_Req.tr_node.io_Command = TR_ADDREQUEST;
  Time_Req.tr_node.io_Flags = 0;
  Time_Req.tr_node.io_Error = 0;
  return;
}
/***********************************************************************
  Set up a new timer request
***********************************************************************/
void
restart_timer ()
{
  Time_Req.tr_time.tv_secs = 1;
  Time_Req.tr_time.tv_micro = 0;
  SendIO ((char *) &Time_Req.tr_node);
  return;
}
/***********************************************************************
  main program
***********************************************************************/
void
main (int argc, char **argv)
{
  int class;
  struct IntuiMessage *msg, *GetMsg (struct MsgPort *);
  long RUNNING = 0;

  if (argc > 1)			/* did we get additional parameters? */
    parse_options (argc, argv);
  /* Open libs */
  if (!(IntuitionBase = (struct IntuitionBase *) OpenLibrary ("intuition.library", 0L)))
    {
      puts ("Can't open intuition.library!\n");
      clean_exit (10);
    }

  if (ACTIVE_SCREEN)
    {
      seconds = LockIBase (0);	/* Lock out all access to IntuitionBase */
      nw.Screen = IntuitionBase->FirstScreen;
      UnlockIBase (seconds);	/* and unlock again */
      seconds = 0;		/* set seconds back to zero */
      nw.Type = CUSTOMSCREEN;
    }

  if (AUTOSTART)
    RUNNING = 1;
  /* init gadgets  */
  nw.FirstGadget = &stopg;
  nw.LeftEdge = x;
  nw.TopEdge = y;

  start_timer ();

  if (!(window = (struct Window *) OpenWindow ((struct NewWindow *) &nw)))
    {
      puts ("Can't open window!\n");
      clean_exit (20);
    }

  for (;;)
    {
      long signals;
      static struct Gadget *gad = NULL;

      if (RUNNING)
	restart_timer ();

      signals = Wait (WINMASK | TMASK);

      if (signals & WINMASK)
	{
	  while ((msg = GetMsg (window->UserPort)))
	    {
	      class = msg->Class;
	      gad = (struct Gadget *) (msg->IAddress);
	      ReplyMsg (msg);

	      switch (class)
		{
		case CLOSEWINDOW:
		  clean_exit (0);
		  break;

		case GADGETUP:
		  if (gad->GadgetID == STARTGAD && RUNNING)
		    break;

		  if (gad->GadgetID == STOPGAD && !RUNNING)
		    {
		      seconds = 0;
		      show_data (seconds);
		    }

		  if (gad->GadgetID == STOPGAD && !RUNNING)
		    {
		      seconds = 0;
		      show_data (seconds);
		    }

		  if (gad->GadgetID == STARTGAD)
		    {
		      RUNNING = 1;
		      total_t.FrontPen = PURPLE;
		    }
		  if (gad->GadgetID == STOPGAD)
		    {
		      RUNNING = 0;
		      total_t.FrontPen = BLACK;
		    }

		default:
		  break;
		}		/* end switch */
	    }
	}
      else if (signals & TMASK)	/* We got a message from timer.device */
	{
	  GetMsg (Timer_Port);
	  seconds++;
	  show_data (seconds);
	}
    }				/* end for(;;) */
}				/* end main() */
