#include <dos/dos.h>
#include <dos/notify.h>
#include <dos/dostags.h>
#include <exec/ports.h>
#include <workbench/icon.h>
#include <workbench/startup.h>
#include <workbench/workbench.h>

#include <clib/wb_protos.h>
#include <clib/dos_protos.h>
#include <clib/exec_protos.h>
#include <clib/alib_protos.h>
#include <clib/icon_protos.h>
#include <clib/wb_protos.h>


#include <stdio.h>
#include <signal.h>

/*
 * Version String
 */

STRPTR Version = "$VER: FileWatcher 0.2 (25.11.95) © Tom Hayko";

/*
 * Number of Args
 */

#define NUM_OF_ARGS     5

/*
 * Workbench Args Template
 */

STRPTR WbArgTemplate = "FILE,FILEICON,NOFILEICON,NAME,CMD";

/*
 * ReadArgs() Template
 */

STRPTR ArgTemplate = "FILE/AK,FILEICON/AK,NOFILEICON/AK,NAME/K,CMD/K";

/*
 * Define Name under AppIcon
 */

#define DEF_NAME    "FileWatcher"

/*
 * Local Prototypes
 */

ULONG WatchFile(BYTE *ProgName, LONG *Args, FILE *fp);
BOOL GetFileSize(STRPTR FileName, ULONG * FileSize);

main(int argc, char **argv)
{
    int i;
    struct RDArgs *rdargs;
    LONG Args[NUM_OF_ARGS];
    ULONG   ulRC;

    for (i = 0; i < (sizeof(Args) / sizeof(LONG)); i++)
    {
        Args[i] = 0;
    }

    Args[3] = (LONG) DEF_NAME;
    Args[4] = (LONG) "";

    rdargs = ReadArgs(ArgTemplate, Args, NULL);

    if (rdargs == NULL)
    {
        printf("%s: required argument missing\n", argv[0]);
        exit(RETURN_FAIL);
    }

    ulRC = WatchFile(argv[0], Args, NULL);

    FreeArgs(rdargs);

    exit(0);
}

wbmain(struct WBStartup *wbs)
{
    FILE               *fp;
    struct DiskObject  *pDiskObject;
    char              **pcToolArray;
    char               *ToolType;
    int                 i = 0;
    LONG                Args[NUM_OF_ARGS];
    STRPTR              NextArg;
    ULONG   ulRC;

    fp = fopen("ram:test.out", "w");

    fprintf(fp, "got in\n");

    pDiskObject = (struct DiskObject *)GetDiskObject(wbs->sm_ArgList[0].wa_Name);

    if(pDiskObject == NULL)
    {
        fprintf(fp, "GetDiskObject failed\n");
    }
    else
    {
        fprintf(fp, "passed GetDiskObject\n");
    }

    pcToolArray = pDiskObject->do_ToolTypes;

    NextArg = (STRPTR)strtok(WbArgTemplate, ",");

    i = 0;

    while(NextArg != (STRPTR) NULL)
    {
        ToolType = FindToolType(pcToolArray, NextArg);

        if(ToolType != NULL)
        {
            Args[i] = (LONG)strdup(ToolType);
        }

        NextArg = (STRPTR)strtok(NULL, ",");
        i++;
    }

    FreeDiskObject(pDiskObject);

    fprintf(fp, "calling WatchFile\n");

    ulRC = WatchFile(wbs->sm_ArgList[0].wa_Name, Args, fp);

    fprintf(fp, "called WatchFile, rc = %ld\n", ulRC);

    for(i = 0 ; i < 5 ; i++)
    {
        free(Args[i]);
    }

    fclose (fp);
}

ULONG WatchFile(BYTE *ProgName, LONG *Args, FILE *fp)
{
    BOOL bSuccess;
    struct MsgPort *msgport;
    struct MsgPort *appmsgport;
    struct NotifyRequest notifyreq;
    struct Message *msg;
    struct DiskObject *diskobjFile;
    struct DiskObject *diskobjNoFile;
    struct AppIcon *appicon;
    ULONG filesize;
    ULONG newfilesize;
    ULONG sig;
    ULONG sigcntrlc;
    ULONG sigmsg;
    ULONG sigappmsg;
    ULONG lRC;

    signal(SIGINT, SIG_IGN);
    signal(SIGTERM, SIG_IGN);

    diskobjFile = GetDiskObject((char *) Args[1]);

    if (diskobjFile == NULL)
    {
        fprintf(fp, "%s: couldn't load icon '%s'\n", ProgName, Args[1]);
        exit(RETURN_FAIL);
    }

    diskobjNoFile = GetDiskObject((char *) Args[2]);

    if (diskobjNoFile == NULL)
    {
        fprintf(fp, "%s: couldn't load icon '%s'\n", ProgName, Args[2]);
        FreeDiskObject(diskobjFile);
        exit(RETURN_FAIL);
    }

    msgport = CreatePort((STRPTR) NULL, 0);

    if (msgport == NULL)
    {
        fprintf(fp, "%s: couldn't CreatePort()\n", ProgName);
        FreeDiskObject(diskobjNoFile);
        FreeDiskObject(diskobjFile);
        exit(RETURN_FAIL);
    }

    appmsgport = CreatePort((STRPTR) NULL, 0);

    if (appmsgport == NULL)
    {
        fprintf(fp, "%s: couldn't CreatePort()\n", ProgName);
        DeletePort(msgport);
        FreeDiskObject(diskobjNoFile);
        FreeDiskObject(diskobjFile);
        exit(0);
    }

    bSuccess = GetFileSize((STRPTR) Args[0], &filesize);

    if (!bSuccess)
    {
        fprintf(fp, "%s: couldn't get size of file '%s'\n", ProgName, (char *) Args[0]);
        DeletePort(msgport);
        FreeDiskObject(diskobjNoFile);
        FreeDiskObject(diskobjFile);
        exit(0);
    }

    notifyreq.nr_Name = (UBYTE *) Args[0];
    notifyreq.nr_Flags = NRF_SEND_MESSAGE;
    notifyreq.nr_stuff.nr_Msg.nr_Port = msgport;

    bSuccess = StartNotify(&notifyreq);

    if (!bSuccess)
    {
        fprintf(fp, "StartNotify() failed\n");
        DeletePort(msgport);
        FreeDiskObject(diskobjNoFile);
        FreeDiskObject(diskobjFile);
        exit(0);
    }

    if (filesize != 0)
    {
        appicon = AddAppIcon(0L,
                             0L,
                             (char *) Args[3],
                             appmsgport,
                             NULL,
                             diskobjFile,
                             TAG_DONE);
    }
    else
    {
        appicon = AddAppIcon(0L,
                             0L,
                             (char *) Args[3],
                             appmsgport,
                             NULL,
                             diskobjNoFile,
                             TAG_DONE);
    }

    if (appicon == NULL)
    {
        fprintf(fp, "AddAppIcon() failed\n");
        EndNotify(&notifyreq);

        while (msg = GetMsg(msgport))
            ReplyMsg(msg);

        DeletePort(appmsgport);
        DeletePort(msgport);
        exit(0);
    }

    sigcntrlc = 1L << SIGBREAKB_CTRL_C;
    sigmsg = 1L << msgport->mp_SigBit;
    sigappmsg = 1L << appmsgport->mp_SigBit;

    while (sig = Wait(sigcntrlc | sigmsg | sigappmsg))
    {
        if (sig & sigcntrlc)
        {
            break;
        }

        if (sig & sigmsg)
        {
            while (msg = GetMsg(msgport))
            {
                ReplyMsg(msg);
            }

            bSuccess = GetFileSize((STRPTR)Args[0], &newfilesize);

            RemoveAppIcon(appicon);

            if (newfilesize != 0)
            {
                appicon = AddAppIcon(0L,
                                     0L,
                                     (char *) Args[3],
                                     appmsgport,
                                     NULL,
                                     diskobjFile,
                                     TAG_DONE);
            }
            else
            {
                appicon = AddAppIcon(0L,
                                     0L,
                                     (char *) Args[3],
                                     appmsgport,
                                     NULL,
                                     diskobjNoFile,
                                     TAG_DONE);
            }

            filesize = newfilesize;
        }

        if (sig & sigappmsg)
        {
            while (msg = GetMsg(appmsgport))
            {
                ReplyMsg(msg);
            }

            if(strcmp((STRPTR) Args[4], "") != 0)
            {
                fprintf(fp, "spawning command '%s'\n", (STRPTR) Args[4]);

                lRC = SystemTags((STRPTR) Args[4], SYS_Asynch, 0, TAG_DONE, 0);

                if(lRC != 0)
                {
                    fprintf(fp,
                            "Error spawning command '%s', rc = %ld\n",
                            (STRPTR) Args[4],
                            lRC);
                    break;
                }
            }
        }
    }

    EndNotify(&notifyreq);

    RemoveAppIcon(appicon);

    while (msg = GetMsg(appmsgport))
        ReplyMsg(msg);

    DeletePort(appmsgport);

    while (msg = GetMsg(msgport))
        ReplyMsg(msg);

    DeletePort(msgport);

    FreeDiskObject(diskobjNoFile);
    FreeDiskObject(diskobjFile);
}

BOOL GetFileSize(STRPTR FileName, ULONG * FileSize)
{
    BPTR bptrLock;
    struct FileInfoBlock *pfileinfoblock;
    BOOL fSuccess;

    bptrLock = Lock(FileName, ACCESS_READ);

    if (bptrLock == 0)
    {
        *FileSize = 0L;
        return (TRUE);
    }

    pfileinfoblock = (struct FileInfoBlock *) AllocDosObjectTags(DOS_FIB, TAG_DONE);

    if (pfileinfoblock == NULL)
    {
        UnLock(bptrLock);
        return (FALSE);
    }

    fSuccess = Examine(bptrLock, pfileinfoblock);

    if (!fSuccess)
    {
        FreeDosObject(DOS_FIB, pfileinfoblock);
        UnLock(bptrLock);
        return (FALSE);
    }

    *FileSize = pfileinfoblock->fib_Size;

    FreeDosObject(DOS_FIB, pfileinfoblock);
    UnLock(bptrLock);

    return (TRUE);
}
