/* AtermClock.c */
/*
 * ATerm 7.0 Clock/Date by Jeff Lydiatt, Vancouver Canada
 *
 * This program runs as a sub-process called from the main terminal
 * program.  The clock is located on the top line of the current window,
 * and displays the current time, updated every second.  On the 15 and
 * 45 second mark, the weekday is displayed.  The date is displayed on
 * the even minute and half minute. This code is designed to mimic my favorite
 * clock/calendar, "Klock" by Jack Radigan.  Wish I had his code when I did
 * this, but as far as I know he hasn't released it.
 *
 *    This code is a separate process that must be loaded and initialized
 * by the Aterm "ClockLoader" module.  This process waits for a message passed
 * by the loader giving the address of a window where the clock is displayed.
 * When the message from the loader is replied to, AtermClock then opens
 * two separate timer devices.  The first is used to capture the current
 * system date/time which AtermClock decodes for the display.  The second
 * timer wakes AtermClock up every second so it can update the clock/calendar
 * display.  A subsequent message passed from the ClockLoader module is
 * assumed to be a shut down message which will cause the clock to wind up
 * it's operation and release any resources it uses before "falling off the
 * end of the world."
 *
 * Note: This code was compiled and tested under Aztec C version 3.02.   I've
 * tried to keep the code clean so that it can also be compiled and linked
 * with the Lattice Compiler and Amiga Loader.  I hasn't been tested under
 * Lattice however.
 */

#include 	<exec/types.h>
#include 	<exec/memory.h>
#include 	<exec/ports.h>
#include	<exec/tasks.h>
#include	<exec/execbase.h>
#include	<libraries/dosextens.h>
#include 	<functions.h>
#include	<devices/timer.h>
#include	<intuition/intuition.h>
#include	<time.h>
#include	"Clock.h"

#define RED		  3 /* The color red		    */
#define BLACK		  2 /* The color Black		    */
#define FPEN		 RED /* Color of Clock Characters   */
#define BPEN	       BLACK /* BackGround Color for Clock  */
#define STRLEN		 14 /* Length of Clock Text String */
#define CLOCKX		264 /* Pixel Column to start clock */
#define CLOCKY		  0 /* Top left Corner for the Clock */
#define INTUITION_REV 	 31L /* V1.1 */
#define GRAPHICS_REV  	 31L /* V1.1 */

struct GfxBase *GfxBase = NULL;
struct IntuitionBase *IntuitionBase = NULL;

static struct timerequest timeReq, dateReq;
static struct MsgPort *timePort = NULL;
static struct MsgPort *datePort = NULL;
static BOOL   timeOpened = FALSE;
static BOOL   dateOpened = FALSE;
static struct Window *myWindow; 
static UBYTE  displayStr[STRLEN+1];
static struct IntuiText iText =
   { FPEN, BPEN, JAM2, 0, 0, NULL, displayStr, NULL };
static struct Gadget ClockGadget =
  {
     NULL,		/* *NextGadget */
     CLOCKX, CLOCKY, 0, 0,/* Leftedge, TopEdge, Width, Height */
     NULL,		/* Flags */
     TOPBORDER,		/* Activation Flags */
     BOOLGADGET,	/* Gadget Type - This is the simplest */
     NULL,		/* Gadget Render */
     NULL,		/* Select Render */
     &iText,		/* *GadgetText   */
     NULL,		/* Mutual Exclude */
     NULL,		/* SpecialInfo */
     0,			/* Gadget Id */
     NULL		/* Ptr to UserData */
  };
static BOOL gadgetON = FALSE;

/*-------------------------------------------------------------*/
/*	OpenTimer: return TRUE if timer opened OK	       */
/*-------------------------------------------------------------*/

static BOOL OpenTimer()
{
   BOOL error;
   register struct timerequest *t;
   register struct MsgPort *port;
	  
   /*---- Timer to let me wake up every second ----*/

   if ( (port = CreatePort( "Second.Timer", 0L)) == NULL )
      return FALSE;
   else
      timePort = port;
   t = &timeReq;
   if (OpenDevice(TIMERNAME, UNIT_VBLANK, t, 0L) != 0)
     {
	DeletePort( port );
	timePort = NULL;
	return FALSE;
     }
   timeOpened = TRUE;

   /*---- Timer get the date ----*/

   if ( (port = CreatePort( "Date.Timer", 0L)) == NULL )
      return FALSE;
   else
      datePort = port;

   t = &dateReq;
   if (OpenDevice(TIMERNAME, UNIT_VBLANK, t, 0L) != 0)
     {
	DeletePort( port );
	datePort = NULL;
	return FALSE;
     }
   dateOpened = TRUE;

   return TRUE;
}

/*-------------------------------------------------------------*/
/*	CloseTimer: All Done with the timer.		       */
/*-------------------------------------------------------------*/

static void CloseTimer()
{
   if ( timeOpened )
      CloseDevice( &timeReq );
   if ( timePort != NULL )
	DeletePort( timePort );
   if ( dateOpened )
      CloseDevice( &dateReq );
   if ( datePort != NULL )
	DeletePort( datePort );
}

   /*------------------------------------------------------------*/
   /*	display: display the given string in the current window. */
   /*------------------------------------------------------------*/

static void display( str )
register char *str;
{
   register int i;

   for ( i=0; i < STRLEN; i++ )
       displayStr[i] = *str++;
   displayStr[i] = '\0';

   if ( gadgetON == FALSE )
     {
	AddGadget( myWindow, &ClockGadget, (SHORT)(-1) );
	gadgetON = TRUE;
     } 
   RefreshGadgets( &ClockGadget, myWindow, NULL );
}

   /*------------------------------------------------------------*/
   /*	DisplayCLock: Show date, time, or weekday from date.	 */
   /*------------------------------------------------------------*/

static void DisplayClock()
{
   char str[ STRLEN ];

   static char *month[] =
     {
	"???",
	"Jan", "Feb", "Mar", "Apr", "May", "Jun",
	"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};

   static char *weekday[] =
      { "    Sunday    ",
        "    Monday    ",
        "   Tuesday    ",
        "   Wednesday  ",
        "   Thursday   ",
        "    Friday    ",
        "   Saturaday  "};

   unsigned long clock;
   short mon, day, year, sec, min, hour, n;
   register struct timerequest *t = &dateReq;
   
   t->tr_node.io_Command = TR_GETSYSTIME;
   t->tr_node.io_Flags = IOF_QUICK;
   t->tr_node.io_Error = 0;
   t->tr_node.io_Message.mn_ReplyPort = datePort;
   DoIO( t );
   clock = t->tr_time.tv_secs + (t->tr_time.tv_micro + 500000) / 1000000;
   sec = clock % 60; clock /= 60;
   min = clock % 60; clock /= 60;
   hour= clock % 24; clock /= 24;
    
   switch( sec )
     {
       case  1:
       case 16:
       case 31:   
       case 46: break; /* do nothing */

       case 15:
       case 45: /* Display day of Week */
		display( weekday[ (int)(clock % 7) ] );
		   break;

       case  0:
       case 30: /* Display Current Date */
		n = (int)clock - 2251;
		year = (4 * n + 3) / 1461;
		n -= ( 1461 * year ) / 4;
		year += 1984;
		mon = ( 5 * n + 2 ) / 153;
		day = n - ( 153 * mon + 2 ) / 5 + 1;
		mon += 3;
		if ( mon > 12) 
		   year++;
		if ( !( (1 <= mon) && (mon <= 12) ) )
		   mon = 0;  
		sprintf( str, " %s %02d, %4d ", month[ mon ],
		         day, year );
		display( str );
		break;

       default: /* Display the current Time */	 
		sprintf( str, "   %02d:%02d:%02d   ",
		hour, min, sec);
  		display( str );
      }
}

/*-------------------------------------------------------------*/
/*	StartTimer: launch the timer.			       */
/*-------------------------------------------------------------*/

static void StartTimer()
{
   register struct timerequest *t = &timeReq;

   t->tr_time.tv_secs = 1;
   t->tr_time.tv_micro = 0;
   t->tr_node.io_Command = TR_ADDREQUEST;
   t->tr_node.io_Error = 0;
   t->tr_node.io_Message.mn_ReplyPort = timePort;
   SendIO( t );
}

   /*------------------------------------------------------------*/
   /*		The Clock Task itself.				 */
   /*------------------------------------------------------------*/

main()
{
   ULONG waitMask;
   struct ParmMsg *msg;
   struct MsgPort *myPort;
   register struct Process *myProcess;
   BOOL timerOpened = FALSE;

   /*--------Get my Process location, and tell it's Port. ----------*/

    myProcess = (struct Process *) FindTask(NULL); /* Addr of My Process */ 
    myPort = &myProcess->pr_MsgPort;

   /*-------- Wait For Caller to Send Address of his window --------*/

    WaitPort( myPort );
    msg = (struct ParmMsg * ) GetMsg( myPort );
    myWindow = msg->windowPtr;
    ReplyMsg( msg ); /* Tell Caller I got the window address */
 
    waitMask = 1L << myPort->mp_SigBit;
    GfxBase = ( struct GfxBase * ) OpenLibrary("graphics.library",
		GRAPHICS_REV);
    IntuitionBase = ( struct IntuitionBase * ) OpenLibrary(
		"intuition.library", INTUITION_REV );

    if ( GfxBase != NULL && IntuitionBase != NULL && OpenTimer() )
      {
	waitMask |= 1L << timePort->mp_SigBit;
	StartTimer();
	timerOpened = TRUE;
      }

    while( (msg = (struct ParmMsg *)GetMsg( myPort )) == NULL )
      {
	Wait( waitMask );
	if ( timerOpened && (CheckIO( &timeReq.tr_node ) != NULL) )
	  {
	     (void)GetMsg( timePort );
	     StartTimer();
	     DisplayClock();
	  }
       }

   if ( GfxBase != NULL )
      CloseLibrary( GfxBase );
   if ( IntuitionBase != NULL )
      CloseLibrary( IntuitionBase );
   CloseTimer();
   ReplyMsg( msg );
   exit( 0 );
}
