/*---------------------------------------------------*
 | File: VLTIMER.c - Small timer for the Amiga.      |
 | First release: MLO 900513 - Rev. 1.02 MLO 910619  |
 +---------------------------------------------------+-----*
 | Author:  Maurizio Loreti, aka MLO or I3NOO.             |
 | Address: University of Padova - Department of Physics   |
 |          Via F. Marzolo, 8 - 35131 PADOVA - Italy       |
 | Phone:   (39)(49) 844-313         FAX: (39)(49) 844-245 |
 | E-Mail:  LORETI at IPDINFN (BITNET); or VAXFPD::LORETI  |
 |         (DECnet) - VAXFPD is node 38.257 i.e. 39169; or |
 |          LORETI@PADOVA.INFN.IT (INTERNET).              |
 | Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy |
 *---------------------------------------------------------*/

/**
 | This program opens a small window with a digital clock/timer in
 | the VLT custom screen (or in the Workbench screen); the timer can
 | be reset and you can switch between clock and timer using two
 | window gadgets.
 | This program requires the screenshare.library, from Willy Langeveld,
 | in your LIBS: directory: you can find it on the Fred Fish collection.
**/

/**
 | #include's
**/

#include <stdio.h>                           /* Standard library */
#include <string.h>
#include <exec/types.h>                      /* Amiga stuff */
#include <exec/memory.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <graphics/gfxbase.h>
#include <libraries/dos.h>
#include "mlo.h"                             /* My own stuff */

/**
 | Global variables to detach from CLI
**/

long  _stack        = 4000;
char *_procname     = "VLTimer";
long  _priority     = 0;
long  _BackGroundIO = 0;

/**
 | Global variables to be addressed by Cleanup()
**/

struct IntuitionBase  *IntuitionBase  = NULL;
struct GfxBase        *GfxBase        = NULL;
struct Library        *ScrSharBase    = NULL;
struct Screen         *pScreen        = NULL;
struct Window         *Window         = NULL;

/**
 | Other global variables
**/

struct RastPort *pRP;                 /* VLTimer window raster port */

/**
 | #define's
**/

#define REVISION        33            /* Library revision (Kickstart 1.2) */
#define W_LEFT          470           /* Window left edge */
#define W_TOP           0             /* Window top edge */
#define W_WIDTH         117           /* Window width */
#define W_HEIGHT        22            /* Window height */
#define DETAIL_PEN      0             /* Window detail pen */
#define BLOCK_PEN       1             /* Window block pen */
#define TEXT_X          27            /* Text baseline (x-coordinate) */
#define TEXT_Y          18            /* Text baseline (y-coordinate) */
#define TEXT_FG         3             /* Text foreground pen */
#define TEXT_BG         0             /* Text background pen */
#define NO_TIME         0xFFFFFFFF    /* Flag 'no time on screen' */
#define SLATE_DIM       10            /* 'slate' array dimension */
#define DELTA           10            /* Sleep time (DELTA/50 seconds) */
#define W_OFFSET_X      (640-W_LEFT)  /* Pixels to end of WB screen */
#define ZERO            '0'           /* ASCII zero */
#define COLON           ':'           /* ASCII colon */

/**
 | Gadget definition.
**/

USHORT __chip zgData[] = {                    /* "Zero gadget" nibbles */
  0xFFFF, 0xFC3F, 0xF99F, 0xF91F, 0xF81F,
  0xF89F, 0xF99F, 0xF99F, 0xFC3F, 0xFFFF
};

USHORT __chip cgData[] = {
  0xFFFF, 0xFE7F, 0xFE7F, 0xFE7F, 0xFE7F,     /* "Cycle gadget" nibbles */
  0xFE7F, 0xF81F, 0xFC3F, 0xFE7F, 0xFFFF
};

struct Image zgImage = {                      /* "Zero gadget" image */
  0, 0, 16, 10, 1, zgData, 1, 0, NULL
};

struct Image cgImage = {                      /* "Cycle gadget" image */
  0, 0, 16, 10, 1, cgData, 1, 0, NULL
};

struct Gadget zg = {                          /* "Zero gadget" structure */
  NULL, 2, 11, 16, 10,
  GADGHCOMP | GADGIMAGE,
  RELVERIFY, BOOLGADGET,
  (APTR) &zgImage,
  NULL, NULL, 0, NULL, 0, NULL
};

struct Gadget cg = {                          /* "Cycle gadget" structure */
  &zg, 98, 11, 16, 10,
  GADGHCOMP | GADGIMAGE,
  RELVERIFY, BOOLGADGET,
  (APTR) &cgImage,
  NULL, NULL, 0, NULL, 0, NULL
};

/**
 | Timer window definition
**/

struct NewWindow NWind = {                    /* VLTimer window */
  W_LEFT, W_TOP,
  W_WIDTH, W_HEIGHT,
  DETAIL_PEN, BLOCK_PEN,
  CLOSEWINDOW | GADGETUP,
  WINDOWCLOSE | WINDOWDRAG | WINDOWDEPTH | SMART_REFRESH | NOCAREREFRESH,
  &cg, NULL,
  (UBYTE *) "MT",                         /* i.e. Maurizio's Timer ... */
  NULL, NULL,
  W_WIDTH, W_HEIGHT,
  W_WIDTH, W_HEIGHT,
  CUSTOMSCREEN
};

/**
 | ANSI procedures prototypes
**/

void  Cleanup(int ReturnCode);
void *LibOpen(char *Name, long Rev);
int   NtoA(char *buf, int h, int m, int s);
void  Setup(void);

struct Screen *LockPubScreen();
void           UnlockPubScreen();

struct IntuiMessage *GetMsg();
struct Window       *OpenWindow();
struct Library      *OpenLibrary();

/*--------------*
 | MAIN PROGRAM |
 *--------------*/

void _main()
{
  struct IntuiMessage *IMsg;          /* Intuition message pointer */
  register ULONG SysSecs[2];          /* System time: now, */
  register ULONG Start[2];            /*   and when the timer was started */
  ULONG Last = NO_TIME;               /* Last printed time */
  char slate[SLATE_DIM];              /* Printing array */
  short n;                            /* Number of characters to print */
  ULONG MClass;                       /* Intuition message class */
  struct Gadget *pG;                  /* Pointer to selected gadget */
  register short hour, mins, secs;    /* Time */
  register short oldSec;              /* Last time clock was updated */
  register Boolean clock = False;     /* Clock/Timer flag */
  struct DateStamp ds;                /* System time */

/**
 | First: setup window and timer base
**/

  Setup();
  timer(Start);

/**
 | Infinite loop (exit closing the window).
 | Check for Intuition event (window closed, or gadget hit) and do
 | the appropriate action; (reset timer if zero-gadget hit, switch
 | between timer and clock if cycle-gadget hit). Then ask for current
 | time and, if the display has changed, update it; then suspend for
 | a short time.
**/

  FOREVER {

    while ((IMsg = GetMsg(Window->UserPort)) != NULL) {
      MClass = IMsg->Class;
      pG = (struct Gadget *) IMsg->IAddress;
      ReplyMsg(IMsg);

      switch (MClass) {
        case CLOSEWINDOW:
          Cleanup(SYS_NORMAL_CODE);
        case GADGETUP:
          if (pG == &zg) {
            timer(Start);
          } else if (pG == &cg) {
            if (clock = !clock) {
              oldSec = -1;
            }
          }
          break;
      }
    }

    if (clock) {
      DateStamp(&ds);
      secs = ds.ds_Tick / TICKS_PER_SECOND;
      if (secs != oldSec) {
        hour = ds.ds_Minute / 60;
        mins  = ds.ds_Minute % 60;
        oldSec = secs;
        n = NtoA(slate, hour, mins, secs);
        Move(pRP, TEXT_X, TEXT_Y);
        Text(pRP, slate, n);
      }
    } else {
      timer(SysSecs);
      *SysSecs -= *Start;
      if (SysSecs[1] < Start[1]) (*SysSecs)--;
      if (*SysSecs != Last) {
        secs = (Last = *SysSecs) % 60;
        mins = (Last / 60) % 60;
        hour = (Last / 3600) % 24;
        n = NtoA(slate, hour, mins, secs);
        Move(pRP, TEXT_X, TEXT_Y);
        Text(pRP, slate, n);
      }
    }

    Delay(DELTA);
  }
}

void Cleanup(
  int ReturnCode            /* Exit code */
)
{

/**
 | Releases all system resources, then exits.
**/

  struct IntuiMessage *IMsg;

  if (Window != NULL) {
    while ((IMsg = GetMsg(Window->UserPort))
            != NULL) {
      ReplyMsg(IMsg);
    }
    CloseWindow(Window);
  }

  if (pScreen != NULL)        UnlockPubScreen("VLT");

  if (ScrSharBase   != NULL)  CloseLibrary(ScrSharBase);
  if (GfxBase       != NULL)  CloseLibrary(GfxBase);
  if (IntuitionBase != NULL)  CloseLibrary(IntuitionBase);

  exit(ReturnCode);
}

void *LibOpen(
  char *Name,                     /* Library name */
  long  Rev                       /* Revision level (or 0) */
)
{

/**
 | Opens required library, at the needed revision level.
**/

  void *p;

  if ((p = OpenLibrary(Name, Rev)) == NULL) {
    Cleanup(SYS_ABORT_CODE);
  }
  return p;
}

int NtoA(
  char *buf,                      /* Output buffer */
  int h,                          /* Hours */
  int m,                          /* Minutes */
  int s                           /* Seconds */
)
{/*-------------------------------------------------------*
  | Instead of sprintf(... "%02d:%02d:%02d" ...); to save |
  | memory. Returns the number of characters written.     |
  *-------------------------------------------------------*/

  char *z = buf;

  *buf++ = ZERO + ((h / 10) % 10);
  *buf++ = ZERO + (h % 10);
  *buf++ = COLON;
  *buf++ = ZERO + (m / 10);
  *buf++ = ZERO + (m % 10);
  *buf++ = COLON;
  *buf++ = ZERO + (s / 10);
  *buf++ = ZERO + (s % 10);

  return (buf - z);
}

void Setup(void)
{

/**
 | Opens required libraries.
**/

  IntuitionBase = LibOpen("intuition.library",    REVISION);
  GfxBase       = LibOpen("graphics.library",     REVISION);
  ScrSharBase   = LibOpen("screenshare.library",  0);

/**
 | Open timer window in the VLT custom screen, or in the
 | Workbench screen (if VLT is on the Workbench, or not
 | running).
**/

  if ((pScreen = LockPubScreen("VLT")) == NULL) {
      NWind.Type = WBENCHSCREEN;
  } else {
    NWind.Screen = pScreen;
  }

  if ((Window = OpenWindow(&NWind)) == NULL) {
    Cleanup(SYS_ABORT_CODE);
  }

  SetAPen((pRP = Window->RPort), TEXT_FG);
  SetBPen(pRP, TEXT_BG);
  SetDrMd(pRP, JAM2);
}
