/*
**       alarming_clock             -- by Brian Neal   6/24/89
**
**    A simple clock program whoose alarm packs a punch!  Be sure to
**    crank up the stereo for this one.  Illustrates the use of the
**    timer and digitized sound playback.
**    Compiled with Manx Aztec C v3.6
**           cc -n alarming_clock
**           ln -g alarming_clock isup -lc
*/

#include <intuition/intuition.h>
#include <functions.h>
#include <devices/audio.h>
#include <devices/timer.h>
#include <exec/memory.h>
#include <libraries/dos.h>

/*  useful macros */
#define CHAR_WIDTH  (font -> tf_XSize)
#define CHAR_HEIGHT (font -> tf_YSize)

#define WIN_WIDTH 200      /* window constants */
#define WIN_HEIGHT 60

#define BUTT_WIDTH  50     /* gadget constants */
#define BUTT_HEIGHT 14

#define TEST    1          /* GadgetIDs */
#define ALARM   2
#define UP      3
#define DOWN    4
#define HRS_TEN 5
#define HRS_ONE 6
#define MIN_TEN 7
#define MIN_ONE 8

#define ARROW_WIDTH  16     /* arrow dimensions */
#define ARROW_HEIGHT 8

#define MAPRIGHT 0x00000006      /* masks for channel allocation */
#define MAPLEFT  0x00000009

#define SCREAM_PERIOD 400
#define SCREAM_LENGTH 12536      /* length (in bytes) of data file */
                                 /* used CLI's List to get this    */
#define POW_PERIOD 475
#define POW_LENGTH 23632

#define CLOCK_LEFT  ((WIN_WIDTH - (5 * CHAR_WIDTH)) / 2)
#define CLOCK_TOP   12

#define ALARM_LEFT  CLOCK_LEFT
#define ALARM_TOP   24


extern BOOL open_libraries();
extern struct Window *make_window();
extern void init_itext(), *copy_chip();

void shut_down(), init_sounds(), shut_down_audio(), display_time(),
     shut_down_timer(), send_time_request(), init_arrow(), init_image(),
     set_up_display(), sound_alarm(), display_alarm(),
     turn_off_alarm(), init_digit(), adjust_alarm();
UBYTE init_audio();
BOOL read_data(), init_timer(), set_up_window(), time_to_sound(),
     set_up_gadgets();

struct IntuiText test_text, alarm_text, clock_face, alarm_face;
struct Gadget arrow[2];
struct Gadget digit[4];
struct Image  arrow_image[2];

#define ARROW_BYTES 16
USHORT up_image[] = { 0x0000, 0x0180, 0x03c0, 0x07e0,
                      0x0ff0, 0x1ff8, 0x7ffe, 0x0000 };

USHORT down_image[] = { 0x0000, 0x7ffe, 0x1ff8, 0x0ff0,
                        0x07e0, 0x03c0, 0x0180, 0x0000 };


SHORT points[] = { -1, -1,     BUTT_WIDTH, -1,
                   BUTT_WIDTH, BUTT_HEIGHT,
                   -1, BUTT_HEIGHT,    -1, -1 };

struct Border butt_border =
{  0, 0,                                  /*  LeftEdge, TopEdge */
   3, 0,                                  /*  FrontPen, BackPen */
   JAM1,                                  /*  DrawMode          */
   5,                                     /*  Count             */
   &points[0],                            /*  XY                */
   NULL };                                /*  NextBorder        */


struct Gadget test_button =
{  NULL,                                  /*  NextGadget        */
   20, -(BUTT_HEIGHT + 5),                /*  LeftEdge, TopEdge */
   BUTT_WIDTH, BUTT_HEIGHT,               /*  Width, Height     */
   GADGHCOMP | GRELBOTTOM,                /*  Flags             */
   GADGIMMEDIATE | RELVERIFY,             /*  Activation        */
   BOOLGADGET,                            /*  GadgetType        */
   (APTR) &butt_border,                   /*  GadgetRender      */
   NULL,                                  /*  SelectRender      */
   &test_text,                            /*  GadgetText        */
   0L,                                    /*  MutualExclude     */
   NULL,                                  /*  SpecialInfo       */
   TEST,                                  /*  GadgetID          */
   NULL };                                /*  UserData          */

struct Gadget alarm_button =
{  &test_button,
   -(BUTT_WIDTH + 20), -(BUTT_HEIGHT + 5),
   BUTT_WIDTH, BUTT_HEIGHT,
   GADGHCOMP | GRELRIGHT | GRELBOTTOM,
   GADGIMMEDIATE | TOGGLESELECT,
   BOOLGADGET,
   (APTR) &butt_border,
   NULL,
   &alarm_text,
   0L,
   NULL,
   ALARM,
   NULL };

/*  We are going to use topaz.font 80  */
struct TextAttr our_font =
{  (STRPTR) "topaz.font",              /*      ta_Name      */
   8,                                  /*      ta_YSize     */
   FS_NORMAL,                          /*      ta_Style     */
   FPB_ROMFONT };                      /*      ta_Flags     */


struct my_time             /* hours in range of 0..24, minutes in 0..59 */
{
   UBYTE hours, minutes;
};

struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase       *GfxBase       = NULL;
struct TextFont      *font          = NULL;




void main()
{
   struct Window *win = NULL;
   struct IntuiMessage *msg;
   struct IOAudio scream, pow;
   UBYTE channel_mask;
   UBYTE *scream_data = NULL, *pow_data = NULL;
   struct timerequest time_req;
   struct my_time current, alarm;
   struct Gadget *gad;
   ULONG class;
   USHORT active_digit;
   BOOL alarm_set, gad_images = FALSE;

      if (!(open_libraries(33L, 33L)))
         shut_down(10, win, scream_data, pow_data, gad_images);

      if (!(font = (struct TextFont *) OpenFont(&our_font)))
         shut_down(20, win, scream_data, pow_data, gad_images);

      if (!(read_data(&scream_data, &pow_data)))
         shut_down(30, win, scream_data, pow_data, gad_images);

      if (!(gad_images = set_up_gadgets()))
         shut_down(40, win, scream_data, pow_data, gad_images);

      if (!set_up_window(&win))
         shut_down(50, win, scream_data, pow_data, gad_images);

      if ((channel_mask = init_audio(&scream)) == 0)
         shut_down(60, win, scream_data, pow_data, gad_images);

      init_sounds(&scream, &pow, channel_mask, scream_data, pow_data);

      if (!(init_timer(&time_req)))
      {
         shut_down_audio(&scream, channel_mask);
         shut_down(70, win, scream_data, pow_data, gad_images);
      }

      set_up_display(win, &clock_face, &alarm_face);

      /*  Give us a boost in priority  */
      SetTaskPri(FindTask(NULL), 20L);

      alarm_set = FALSE;
      active_digit = MIN_ONE;
      alarm.hours   = 0;
      alarm.minutes = 0;
      display_time(win, &clock_face, &current);
      display_alarm(win, &alarm_face, alarm);
      send_time_request(&time_req);

      FOREVER
      {
         /*
         * if our time request is back, update clock and restart the
         * timer.  If alarm is set, and it is that magic time, sound
         * the alarm.
         */
         if (GetMsg(time_req.tr_node.io_Message.mn_ReplyPort))
         {
            display_time(win, &clock_face, &current);
            send_time_request(&time_req);

            if ((time_to_sound(current, alarm)) && alarm_set)
               sound_alarm(win, &scream, &pow);
         }

         /*  Wait on timer and input from user via window */

         Wait(1L << win -> UserPort -> mp_SigBit |
              1L << time_req.tr_node.io_Message.mn_ReplyPort -> mp_SigBit);

         while (msg = (struct IntuiMessage *) GetMsg(win -> UserPort))
         {
            class = msg -> Class;
            gad = (struct Gadget *) msg -> IAddress;
            ReplyMsg(msg);

            switch (class)
            {
               case CLOSEWINDOW:
                  shut_down_timer(&time_req);
                  shut_down_audio(&scream, channel_mask);
                  shut_down(0, win, scream_data, pow_data, gad_images);
                  break;

               case GADGETUP:    /*  has no meaning; set in gadgets  */
                  break;         /*  only to let user see GADGHCOMP  */

               case GADGETDOWN:
                  switch(gad -> GadgetID)
                  {
                     case ALARM:
                        if (alarm_set)
                        {
                           turn_off_alarm(&alarm_button, win);
                           alarm_set = FALSE;
                        }
                        else
                           alarm_set = TRUE;
                        break;

                     case TEST:
                        sound_alarm(win, &scream, &pow);
                        break;

                     case UP:
                     case DOWN:
                        adjust_alarm(&alarm, active_digit, gad -> GadgetID);
                        display_alarm(win, &alarm_face, alarm);
                        break;

                     case HRS_TEN:
                     case HRS_ONE:
                     case MIN_TEN:
                     case MIN_ONE:
                        active_digit = gad -> GadgetID;
                        break;
                  }
                  break;

               default:
                  break;
            }                 /*  switch */
         }                    /*  while  */
      }                       /* for(;;) */
}                             /*   main  */


/*
**       shut_down()
**          Frees up everything but audio and timer related activiy.
**/

void shut_down(code, win, scream, pow, gad_images)
WORD code;
struct Window *win;
UBYTE *scream, *pow;
BOOL gad_images;
{
   struct IntuiMessage *msg;

      if (gad_images)
      {
         FreeMem(arrow_image[0].ImageData, (ULONG) ARROW_BYTES);
         FreeMem(arrow_image[1].ImageData, (ULONG) ARROW_BYTES);
      }
      if (pow)
         FreeMem(pow, (ULONG) POW_LENGTH);
      if (scream)
         FreeMem(scream, (ULONG) SCREAM_LENGTH);
      if (win)
      {
         while(msg = (struct IntuiMessage *) GetMsg(win -> UserPort))
            ReplyMsg(msg);
         CloseWindow(win);
      }
      if (font)
         CloseFont(font);
      if (GfxBase)
         CloseLibrary(GfxBase);
      if (IntuitionBase)
         CloseLibrary(IntuitionBase);
      Exit((LONG) code);
}

/*
**    shut_down_audio()
**          Cleans up after using audio.device.
*/

void shut_down_audio(scream, channels_used)
struct IOAudio *scream;
UBYTE channels_used;
{
      scream -> ioa_Request.io_Unit = (struct Unit *) channels_used;
      CloseDevice(scream);
      DeletePort(scream -> ioa_Request.io_Message.mn_ReplyPort);
      /* remember, pow and scream shared the same reply port */
}


/*
**    init_audio()
**          Opens audio device and allocates a stereo pair of channels.
**       Returns a mask of channels allocated, or 0 if error.
*/
UBYTE init_audio(iob)
struct IOAudio *iob;
{
   UBYTE channel_map[4];
   LONG error;
   ULONG mask;

      channel_map[0] = 0x03;     /* channels 0 and 1 */
      channel_map[1] = 0x05;     /* channels 0 and 2 */
      channel_map[2] = 0x0a;     /* channels 3 and 1 */
      channel_map[3] = 0x0c;     /* channels 3 and 2 */

      /* Get a reply port so the audio device can communicate with us */

      if ((iob -> ioa_Request.io_Message.mn_ReplyPort
                                        = CreatePort("ac", 0L)) == NULL)
         return (0);

/* We are now going to be pigs and set our priority to the max.  If
*  successful, we hold exclusive access to our stereo pair of channels.
*  Why don't we just set our priority lower, allocate channels, and then
*  lock them?  Because we don't intend to give up our access until
*  we quit.
*/
      iob -> ioa_Request.io_Message.mn_Node.ln_Pri = ADALLOC_MAXPREC;
      iob -> ioa_Data = &channel_map[0];
      iob -> ioa_Length = 4L;

      error = OpenDevice(AUDIONAME, 0L, iob, 0L);
      if (error == 0)
      {
         mask = (ULONG) iob -> ioa_Request.io_Unit;
         return ((UBYTE) mask);
      }
      else
      {
         DeletePort(iob -> ioa_Request.io_Message.mn_ReplyPort);
         return (0);
      }
}

/*
**    init_sounds()
**          Initializes our IOAudio request blocks for sounding
**    once.  Attaches waveform data.
*/

void init_sounds(scream, pow, channel_mask, scream_data, pow_data)
struct IOAudio *scream, *pow;
UBYTE channel_mask;
UBYTE *scream_data, *pow_data;
{

      scream -> ioa_Request.io_Unit = (struct Unit *)
                                      ((ULONG) channel_mask & MAPRIGHT);
      scream -> ioa_Request.io_Command = CMD_WRITE;
      scream -> ioa_Request.io_Flags = IOF_QUICK | ADIOF_PERVOL;
      scream -> ioa_Data = scream_data;
      scream -> ioa_Cycles = 1;
      scream -> ioa_Period = SCREAM_PERIOD;
      scream -> ioa_Length = (ULONG) SCREAM_LENGTH;
      scream -> ioa_Volume = 64;

      /* copy keys, ports, and above stuff */
      *pow = *scream;

      /* Now, more specifically... */
      pow -> ioa_Request.io_Unit = (struct Unit *)
                                   ((ULONG) channel_mask & MAPLEFT);
      pow -> ioa_Data = pow_data;
      pow -> ioa_Period = POW_PERIOD;
      pow -> ioa_Length = (ULONG) POW_LENGTH;
}

/*
**       read_data()
**             Read in digitized sound files, placing them into
**    Chip memory.  Note that the files are IFF, but treated as
**    raw data.  Hopefully, won't be able to hear the 'pop' as the
**    header information is played.  NOTE:  Would be simple
**    to skip over the header.  A job for a future revision.
**    Call AmigaDos directly, bypassing all that "ffunction" stuff.
**    shut_down() is responsible for freeing up the allocated Chip
**    memory, so don't have to worry about that here.
*/

BOOL read_data(screamptr, powptr)
UBYTE **screamptr, **powptr;
{
   struct FileHandle *scream_file, *pow_file;
   LONG scream_actual, pow_actual;

      *screamptr = AllocMem((ULONG) SCREAM_LENGTH, MEMF_CHIP);
      *powptr = AllocMem((ULONG) POW_LENGTH, MEMF_CHIP);

      pow_file    = Open("explosion.sound", MODE_OLDFILE);
      scream_file = Open("scream.sound", MODE_OLDFILE);

      if ((!scream_file) || (!pow_file) || (!(*screamptr)) || (!(*powptr)))
      {
         if (scream_file)
                         Close(scream_file);
         if (pow_file)
                         Close(pow_file);
         return (FALSE);
      }

      scream_actual = Read(scream_file, *screamptr, (LONG) SCREAM_LENGTH);
      pow_actual = Read(pow_file, *powptr, (LONG) POW_LENGTH);

      Close(scream_file);
      Close(pow_file);

      if ((scream_actual != SCREAM_LENGTH) || (pow_actual != POW_LENGTH))
         return (FALSE);
      else
         return (TRUE);
}

/*
**       init_timer()
**             Initializes our time request and opens the timer
**    device.
*/

BOOL init_timer(time_req)
struct timerequest *time_req;
{
      /* obtain reply port so timer can signal us */
      if (!(time_req -> tr_node.io_Message.mn_ReplyPort =
                  CreatePort("ac_timer", 0L)))
         return (FALSE);

      if (OpenDevice(TIMERNAME, UNIT_VBLANK, time_req, 0L) != NULL)
      {
         DeletePort(time_req -> tr_node.io_Message.mn_ReplyPort);
         return (FALSE);
      }
      else
      {
         /* set up our time request so the timer will time time for us */
         time_req -> tr_node.io_Command = TR_ADDREQUEST;
         time_req -> tr_node.io_Flags   = IOF_QUICK;
         return (TRUE);
      }
}


/*
**       display_time()
**          Outputs the current time to our window.
*/

void display_time(win, clock_face, current)
struct Window *win;
struct IntuiText *clock_face;
struct my_time *current;
{
   struct DateStamp now;
   SHORT hours, minutes;
   static char time_buffer[7];

         DateStamp(&now);        /* get system time from AmigaDos */

         hours   = now.ds_Minute / 60;
         minutes = now.ds_Minute % 60;

         /* format our time display in the time_buffer */

         sprintf(&time_buffer[0], "%02d:%02d", hours, minutes);

         /* attach it to our IntuiText structure, and display it */

         clock_face -> IText = (UBYTE *) &time_buffer[0];
         PrintIText(win -> RPort, clock_face, 0L, 0L);

         current -> hours   = hours;
         current -> minutes = minutes;
}


/*
**       display_alarm()
**          Outputs the current setting of the alarm to our window.
*/
void display_alarm(win, alarm_face, alarm)
struct Window *win;
struct IntuiText *alarm_face;
struct my_time alarm;
{
   static char alarm_buffer[7];

      sprintf(&alarm_buffer[0], "%02d:%02d", alarm.hours, alarm.minutes);
      alarm_face -> IText = (UBYTE *) &alarm_buffer[0];
      PrintIText(win -> RPort, alarm_face, 0L, 0L);
}


/*
**       shut_down_timer()
**          Releases access to the timer device.
*/

void shut_down_timer(time_req)
struct timerequest *time_req;
{
      AbortIO(time_req);
      CloseDevice(time_req);
      DeletePort(time_req -> tr_node.io_Message.mn_ReplyPort);
}

/*
**       send_time_request()
**          Sends a message to the timer, telling it to signal us
**    in five seconds.  Thus our clock can never be off from the system
**    clock by more than five seconds.
**
*/

void send_time_request(time_req)
struct timerequest *time_req;
{

      time_req -> tr_time.tv_secs  = 5L;
      time_req -> tr_time.tv_micro = 0L;
      BeginIO(time_req);
}


/*
**    init_arrow()
**          Initializes the alarm's set buttons.
*/

void init_arrow(arrow, id)
struct Gadget *arrow;
COUNT id;
{
   arrow -> NextGadget    = (id == 0) ? &arrow[1]
                                      : &alarm_button;
   arrow -> LeftEdge      = (id == 0) ? (ALARM_LEFT - (ARROW_WIDTH + 6))
                                      : (ALARM_LEFT + (CHAR_WIDTH * 5) + 6);
   arrow -> TopEdge       = ALARM_TOP;
   arrow -> Width         = ARROW_WIDTH;
   arrow -> Height        = ARROW_HEIGHT;
   arrow -> Flags         = GADGHCOMP | GADGIMAGE;
   arrow -> Activation    = GADGIMMEDIATE | RELVERIFY;
   arrow -> GadgetType    = BOOLGADGET;
   arrow -> GadgetRender  = (APTR) &arrow_image[id];
   arrow -> SelectRender  = NULL;
   arrow -> GadgetText    = NULL;
   arrow -> MutualExclude = 0L;
   arrow -> SpecialInfo   = NULL;
   arrow -> GadgetID      = UP + id;
   arrow -> UserData      = NULL;
}


/*
**       init_image()
**          Initializes the alarm set button's images.
*/

void init_image(arrow, id)
struct Image *arrow;
COUNT id;
{
   arrow -> LeftEdge   = 0;
   arrow -> TopEdge    = 0;
   arrow -> Width      = ARROW_WIDTH;
   arrow -> Height     = ARROW_HEIGHT;
   arrow -> Depth      = 1;
   arrow -> ImageData  = (id == 0) ? &up_image[0] : &down_image[0];
   arrow -> PlanePick  = 2;
   arrow -> PlaneOnOff = 0;
   arrow -> NextImage  = NULL;
}


/*
**       init_digit()
**          Initializes the alarm's digit gadgets.
*/
void init_digit(d, id)
struct Gadget *d;
UCOUNT id;
{
   d -> NextGadget    = (id < 3) ? &digit[id + 1] : &arrow[0];

   if (id < 2)
      d -> LeftEdge   = ALARM_LEFT + (id * CHAR_WIDTH);
   else
      d -> LeftEdge   = ALARM_LEFT + CHAR_WIDTH + (id * CHAR_WIDTH);

   d -> TopEdge       = ALARM_TOP;
   d -> Width         = CHAR_WIDTH;
   d -> Height        = CHAR_HEIGHT;
   d -> Flags         = GADGHCOMP;
   d -> Activation    = GADGIMMEDIATE | RELVERIFY;
   d -> GadgetType    = BOOLGADGET;
   d -> GadgetRender  = NULL;
   d -> SelectRender  = NULL;
   d -> GadgetText    = NULL;
   d -> MutualExclude = 0L;
   d -> SpecialInfo   = NULL;
   d -> GadgetID      = HRS_TEN + id;
   d -> UserData      = NULL;
}


/*
**       set_up_window()
**          Hides the details of opening the window from
**    main().
*/
BOOL set_up_window(win)
struct Window **win;
{

      if (!(*win = make_window(100, 60, WIN_WIDTH, WIN_HEIGHT, -1, -1,
                               CLOSEWINDOW | GADGETUP | GADGETDOWN,
                                 WINDOWCLOSE | ACTIVATE | WINDOWDRAG |
                                 WINDOWDEPTH | SMART_REFRESH,
                               NULL, NULL, NULL,
                               NULL, NULL, 0, 0, 0, 0)))
         return (FALSE);

      SetFont((*win) -> RPort, font);
      SetWindowTitles(*win, (UBYTE *) "Alarming Clock", NULL);
      AddGList(*win, &digit[0], -1L, -1L, NULL);
      RefreshGList(&digit[0], *win, NULL, -1L);
      return (TRUE);
}

/*
**          set_up_gadgets()
**             Hides the details of initializing all of our gadgets
**    from main().  Our gadget list looks like:
**         digits -> arrows -> alarm_button -> test_button
*/
BOOL set_up_gadgets()
{
   COUNT i;

      init_itext(&test_text, 1, 9, 3, "TEST");
      init_itext(&alarm_text, 1, 5, 3, "ALARM");

      for (i = 0; i < 4; ++i)
         init_digit(&digit[i], i);

      for (i = 0; i < 2; ++i)
      {
         init_arrow(&arrow[i], i);
         init_image(&arrow_image[i], i);
      }
      arrow_image[0].ImageData = copy_chip(&up_image[0], ARROW_BYTES);
      arrow_image[1].ImageData = copy_chip(&down_image[0], ARROW_BYTES);

      if ((arrow_image[0].ImageData) && (arrow_image[1].ImageData))
         return (TRUE);
      else
      {
         if (arrow_image[0].ImageData)
            FreeMem(arrow_image[0].ImageData, (ULONG) ARROW_BYTES);
         if (arrow_image[1].ImageData)
            FreeMem(arrow_image[1].ImageData, (ULONG) ARROW_BYTES);
         return (FALSE);
      }
}

/*
**          set_up_display()
**             Hides the details of "drawing" our clock face from
**    main().
*/
void set_up_display(win, clock_face, alarm_face)
struct Window *win;
struct IntuiText *clock_face, *alarm_face;
{

      init_itext(clock_face, 1, CLOCK_LEFT, CLOCK_TOP, "");
      init_itext(alarm_face, 3, ALARM_LEFT, ALARM_TOP, "");

      clock_face -> DrawMode = JAM2;     /* use both pens, so old junk  */
      alarm_face -> DrawMode = JAM2;     /* gets erased when written on */
}


/*
**          sound_alarm()
**    Beeps screen, and brings window to the front.
**    Sends off a scream request, waits for it to finish,
**    then fires off the pow request.  Waits for it to finish, then
**    returns.
*/
void sound_alarm(win, scream, pow)
struct Window *win;
struct IOAudio *scream, *pow;
{

      DisplayBeep(NULL);    /*  Beep everyone's screen, impolite */
      WindowToFront(win);   /*  but hey, someone needs reminding */
      BeginIO(scream);
      WaitIO(scream);
      BeginIO(pow);
      WaitIO(pow);
}


/*
**          time_to_sound()
**             Returns true is current time == time alarm is supposed
**    to go off.
*/
BOOL time_to_sound(current, alarm)
struct my_time current, alarm;
{
   return ((current.hours   == alarm.hours) &&
           (current.minutes == alarm.minutes));
}


/*
**          turn_off_alarm()
**             Shuts off the alarm by deselecting the alarm gadget.
**    Note that you can't simply change the gadget's flags and refresh,
**    you have to remove it, change it's flags, add it, then refresh.
*/
void turn_off_alarm(alarm_button, win)
struct Gadget *alarm_button;
struct Window *win;
{
      RemoveGadget(win, alarm_button);
      alarm_button -> Flags &=  ~SELECTED;
      AddGadget(win, alarm_button, -1L);
      RefreshGList(alarm_button, win, NULL, 1L);
}


/*
**          adjust_alarm()
**             When user clicks on arrows, we come here and adjust
**    our alarm display.  The variable active_digit keeps track of the
**    digit being changed.  This is a big mess!!
*/
void adjust_alarm(alarm, active_digit, direction)
struct my_time *alarm;
USHORT active_digit, direction;
{

      switch(active_digit)
      {
         case HRS_TEN:
            if (direction == UP)
            {
               if (alarm -> hours >= 14)
                  alarm -> hours = 0;
               else
                  alarm -> hours += 10;
            }
            else
            {
               if (alarm -> hours < 9)
                  alarm -> hours = 23;
               else
                  alarm -> hours -= 10;
            }
            break;

         case HRS_ONE:
            if (direction == UP)
               alarm -> hours = (alarm -> hours + 1) % 24;
            else
            {
               if (alarm -> hours == 0)
                  alarm -> hours = 23;
               else
                  --alarm -> hours;
            }
            break;

         case MIN_TEN:
            if (direction == UP)
            {
               if (alarm -> minutes > 49)
                  alarm -> minutes = 0;
               else
                  alarm -> minutes += 10;
            }
            else
            {
               if (alarm -> minutes < 10)
                  alarm -> minutes = 59;
               else
                  alarm -> minutes -= 10;
            }
            break;

         case MIN_ONE:
            if (direction == UP)
               alarm -> minutes = (alarm -> minutes + 1) % 60;
            else
            {
               if (alarm -> minutes == 0)
                  alarm -> minutes = 59;
               else
                  --alarm -> minutes;
            }
            break;
         }
}
