/******************************************************************************
** Michael Bauer's Watch - © April 1995 by Michael Bauer
******************************************************************************/

/// "*** Includes ***"
#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/graphics.h>
#include <proto/gadtools.h>
#include <proto/timer.h>
#include <proto/intuition.h>
#include <proto/utility.h>
#include <exec/libraries.h>
#include <exec/memory.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <graphics/gfxmacros.h>
#include <devices/timer.h>
#include <libraries/gadtools.h>
#include <utility/date.h>
#include <intuition/intuitionbase.h>
#include <dos/rdargs.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <lib/rexx.h>
#include <dos/dos.h>
#include <easysound.h>
///
/// "*** Variablen ***"
const char Versiontag[] = "$VER: MBWatch 1.1 (25.6.95)";

extern struct Library       *UtilityBase;
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase       *GfxBase;
extern struct Library       *GadToolsBase;

char *RexxHostName = "MBWATCH";

char ArgStr[] = "X=XPOS/N,Y=YPOS/N,SCREEN/K,S=START/S,LOGTIME/N,SEC/N,MIN/N,HOUR/N,ALARM/N,SAMPLE/K,LOGFILE/S";
LONG ArgArray[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};

ULONG signal;
int running = FALSE, StartMe = FALSE, done = FALSE;
static struct timeval time_val0, time_val1;
UBYTE t_string[32];
ULONG secoffset = 0, xpos = 0, ypos = 0;

struct Library      *TimerBase;
struct MsgPort      *TimerMP;
struct timerequest  *TimerIO;

char *PubScreenName = NULL;
char *foofile = NULL;
char soundfile[100];

struct timerequest *tr;
struct Window *w;
struct ClockData clockdata0, clockdata1, clockoffset;
struct RDArgs *rda = NULL;
int outReq = 0, alarm = 0, alarmtimer;
int foo = 0, logtime = 60, min,sec,hour, minoff = 0, secoff = 0, houroff = 0;
int wantlog = 0;
///
/// "*** Prototypes ***"
static void DisposeTimerIO(int);
int InitTimerIO(void);
int SendTimerIO(int, ULONG);
struct Window *InitWindow(void);
void _main(void);
long DoRexxCommand(void *, struct MsgPort *, char *, char **);
int text_width(char *);
void ParseArgs(void);
void HandleIDCMP(void);
void CheckFormerTime(void);
///
/// "DisposeTimerIO"
static void DisposeTimerIO (int n) {

    switch (n) {
        case 0:
            CloseDevice ((struct IORequest *) TimerIO);
        case 1:
            DeleteIORequest (TimerIO);
        case 2:
            DeleteMsgPort (TimerMP);
        default:
            break;
    }
    remove ("s:.mbwatch");
}
///
/// "InitTimerIO"
int InitTimerIO (void) {

    TimerMP = CreateMsgPort();
    if (!TimerMP)
        DisposeTimerIO(3);

    TimerIO = CreateIORequest(TimerMP, sizeof(struct timerequest));
    if (!TimerIO)
        DisposeTimerIO(2);

    if (OpenDevice(TIMERNAME, UNIT_VBLANK, (struct IORequest *)TimerIO, 0L) != 0L)
        DisposeTimerIO(1);

    TimerBase = (struct Library *)TimerIO->tr_node.io_Device;

    return (TRUE);
}
///
/// "SendTimerIO"
int SendTimerIO (int type, ULONG secs) {

    struct timerequest *tr;

    if (tr = (struct timerequest *)AllocMem(sizeof (struct timerequest), MEMF_PUBLIC)) {
        *tr = *TimerIO;

        tr->tr_node.io_Command  = type;
        tr->tr_time.tv_secs     = secs;
        tr->tr_time.tv_micro    = 50000L;

        SendIO (&tr->tr_node);

        return (1);
    }
    return (0);
}
///
/// "InitWindow"
struct Window *InitWindow (void) {

    struct Screen *screen;
    struct Window *window;
    int height, width;

    if (screen = LockPubScreen (PubScreenName)) {
    if (!screen)
        screen = LockPubScreen (NULL);

        width = text_width("00:00:00") + 70;
        height = screen->BarHeight + 1;

        window = OpenWindowTags(NULL,
            WA_Left, xpos,
            WA_Top,  ypos,
            WA_Title,"00:00:00",
            WA_Flags, (ULONG)WFLG_CLOSEGADGET|WFLG_DEPTHGADGET|WFLG_DRAGBAR,
            WA_IDCMP, (ULONG)IDCMP_CLOSEWINDOW,
            WA_Height, (ULONG)height,
            WA_Width, (ULONG)width,
            WA_PubScreen, screen,
            WA_PubScreenFallBack, TRUE,
            TAG_DONE);

        if (!window)
            exit(20);

        UnlockPubScreen (NULL, screen);
        return (window);
    }
}
///
/// "DoRexxCommand"
long DoRexxCommand(void *msg, struct MsgPort *port, char *arg0, char **pres) {

    int rc;

    if (stricmp(arg0, "start") == 0) {
        running = TRUE;
        return;
    }

    if (stricmp(arg0, "stop") == 0) {
        running = FALSE;
        return;
    }

    if (stricmp(arg0, "gettime") == 0) {
        *pres = t_string;
        rc = 0;
        return (rc);
    }

    if (stricmp(arg0, "reset") == 0) {
        time_val0 = time_val1 = TimerIO->tr_time;
        SubTime(&time_val1,&time_val0);
        secoffset = 0;
        return;
    }

    if (stricmp(arg0, "quit") == 0) {
        done = TRUE;
        return;
    }

    return (0);
}
///
/// "text_width"
int text_width(char *str) {

    struct IntuiText itext;
    itext.FrontPen  = 1;
    itext.BackPen   = 0;
    itext.DrawMode  = JAM1;
    itext.LeftEdge  = 0;
    itext.TopEdge   = 1;
    itext.ITextFont = NULL;
    itext.NextText  = NULL;
    itext.IText     = str;

    return( IntuiTextLength(&itext) );
}
///
/// "ParseArgs"
void ParseArgs(void) {
    rda = ReadArgs(ArgStr, ArgArray, rda);

    if (ArgArray[0])
        xpos = *(LONG *)ArgArray[0];

    if (ArgArray[1])
        ypos = *(LONG *)ArgArray[1];

    if (ArgArray[2]) {
        PubScreenName = (STRPTR)ArgArray[2];
        printf("%s\n",PubScreenName);
    }
    else
        PubScreenName = NULL;

    if (ArgArray[3]) {
        StartMe = TRUE;
        running = TRUE;
    }

    if (ArgArray[4])
        logtime = *(LONG *)ArgArray[4];

    if (ArgArray[5])
        secoff = *(LONG *)ArgArray[5];

    if (ArgArray[6])
        minoff = *(LONG *)ArgArray[6];

    if (ArgArray[7])
        houroff = *(LONG *)ArgArray[7];

    if (ArgArray[8])
        alarm = *(LONG *)ArgArray[8];

    if (ArgArray[9]) {
        foofile = (STRPTR)ArgArray[9];
        strcpy(soundfile, foofile);
    }

    if (ArgArray[10])
        wantlog = 1;

    FreeArgs(rda);
}
///
/// "HandleIDCMP"
void HandleIDCMP(void) {
    struct IntuiMessage *imsg;
    ULONG im_code, im_class;
    APTR im_address;

    while (imsg = GT_GetIMsg (w->UserPort)) {

        im_class = imsg->Class;
        im_code = imsg->Code;
        im_address = imsg->IAddress;

        GT_ReplyIMsg (imsg);
            switch (im_class) {

                case CLOSEWINDOW:
                    done = TRUE;
                    break;
                default:
                    break;
        }
    }
}
///
/// "CheckFormerTime"
void CheckFormerTime(void) {
    FILE *file;

    /**
    *** Open s:.mbwatch and read the data if the file exists, set the data to
    *** 0 if the file doesn't exist.
    **/
    file = fopen("s:.mbwatch","r");
    if (file) {
        fscanf(file,"%d:%d:%d",&hour,&min,&sec);
        fclose(file);
    }
    else {
        hour = 0;
        min = 0;
        sec = 0;
    }

    /**
    *** Fill in the clockdata structure
    **/
    clockoffset.sec = sec + secoff;
    clockoffset.min = min + minoff;
    clockoffset.hour = hour + houroff;
    clockoffset.mday = 1;
    clockoffset.month = 1;
    clockoffset.year = 1995;
    clockoffset.wday = 1;

    /**
    *** Calculate the seconds from the given data
    **/
    secoffset = Date2Amiga(&clockoffset);
}
///
/// "_main"
void _main (void) {

    ParseArgs();        // Pass Arguments
    if (wantlog == 1)
        CheckFormerTime();  // Check if there's a s:.mbwatch file

    if (w = InitWindow ()) {

        if (InitTimerIO ()) {

            outReq += SendTimerIO (TR_ADDREQUEST, 0L);

            while (!done || outReq) {

                signal = Wait ( 1L << TimerMP->mp_SigBit | SIGBREAKF_CTRL_C |
                                1L << w->UserPort->mp_SigBit | 1L << RexxSigBit);

                TimerIO->tr_node.io_Command = TR_GETSYSTIME;
                DoIO (&TimerIO->tr_node);

                /**
                *** StartMe ist nur am Anfang wahr -> Zeit wird auf 00:00:00
                *** gesetzt.
                **/
                if (running == FALSE || StartMe == TRUE) {
                    struct timeval tvdiff = TimerIO->tr_time;
                    SubTime (&tvdiff, &time_val0);
                    SubTime (&tvdiff, &time_val1);
                    AddTime (&time_val0, &tvdiff);
                    StartMe = FALSE;
                }

                time_val1 = TimerIO->tr_time;
                SubTime (&time_val1, &time_val0);

                /**
                *** Handle CTRL-C
                **/
                if ((signal & SIGBREAKF_CTRL_C) == SIGBREAKF_CTRL_C)
                    done = TRUE;

                /**
                *** Handle Incoming Rexx Commands
                **/
                if (signal & (1 << RexxSigBit))
                    ProcessRexxCommands(NULL);

                /**
                *** Handle IDCMP Messages
                **/
                if (signal & (1L << w->UserPort->mp_SigBit))
                    HandleIDCMP();

                /**
                *** Timer Event handling
                **/
                if (signal & (1L << TimerMP->mp_SigBit)) {

                    static ULONG oldTSec = 0, oldSSec = 0;

                    while (tr = (struct timerequest *) GetMsg(TimerMP)) {
                        outReq--;
                        FreeMem ((UBYTE *) tr, sizeof (struct timerequest));

                        if (oldTSec < TimerIO->tr_time.tv_secs || oldSSec < time_val1.tv_secs) {
                            oldSSec = time_val1.tv_secs;
                            oldTSec = TimerIO->tr_time.tv_secs;

                            Amiga2Date (time_val1.tv_secs + secoffset, &clockdata1);
                            sprintf (t_string, "%02d:%02d:%02d", clockdata1.hour, clockdata1.min, clockdata1.sec);
                            SetWindowTitles(w, t_string, NULL);

                            /**
                            *** Save the actual time to s:.mbwatch
                            **/
                            if (wantlog == 1) {
                                foo++;
                                if (foo >= logtime*2) {
                                    FILE *file = fopen("s:.mbwatch","w");
                                    if (file) {
                                        fprintf(file,"%s",t_string);
                                        fclose(file);
                                        foo = 0;
                                    }
                                }
                            }

                            /**
                            *** Alarm
                            **/
                            if (alarm != 0) {
                                alarmtimer++;
                                if (alarmtimer >= alarm*2) {
                                    if (soundfile) {
                                        struct SoundInfo *sound;
                                        sound = (struct SoundInfo *)LoadIff(soundfile);
                                        if (sound) {
                                            PlayIff(sound,64,L0,-35,0,1,0,0,1);
                                            StopIff(L0);
                                            RemoveIff(sound);
                                            alarmtimer = 0;
                                        }
                                        else {
                                            DisplayBeep(NULL);
                                            alarmtimer = 0;
                                        }
                                    }
                                    else {
                                        DisplayBeep(NULL);
                                        alarmtimer = 0;
                                    }
                                }
                            }
                        }

                        if (!done)
                            outReq += SendTimerIO (TR_ADDREQUEST, 0L);
                    }
                }
            }
            DisposeTimerIO (0);
        }
        CloseWindow(w);
    }
}
///

/** The End **/
