#define __USE_SYSBASE
long    __oslibversion = 37L;

#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <intuition/gadgetclass.h>
#include <workbench/workbench.h>
#include <libraries/easyrexx.h>

#include <proto/exec.h>
#include <proto/icon.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <clib/easyrexx_protos.h>
#include <pragmas/easyrexx_pragmas.h>

#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#include "/_StartMenu/messages.h"

UBYTE ver[] = {"$VER: SMRexx v1.0 "__AMIGADATE__};

#define XADD_TBGAD  1
#define XADD_CLGAD  2
#define XREM_TBGAD  3
#define XREM_CLGAD  4
#define XTOGGLE     5

struct ARexxCommandTable cTable[] = {
    XADD_TBGAD, "ADD_TASKBAR",  "PORT/A,NAME/A,TEXT/K,IFF/K,ICON/K",  NULL,
    XADD_CLGAD, "ADD_CLOCK",    "PORT/A,NAME/A,IFF/K,ICON/K",         NULL,
    XREM_TBGAD, "REM_TASKBAR",  "NAME/A",                             NULL,
    XREM_CLGAD, "REM_CLOCK",    "NAME/A",                             NULL,
    XTOGGLE,    "TOGGLE",       "NAME/A",                             NULL,
    TABLE_END,
};

typedef struct SMGadgetList {
    struct SMGadgetList *gl_Next;
    struct Gadget       *gl_Gadget;
    STRPTR               gl_PortName;
    STRPTR               gl_GadgetID;
    UBYTE                gl_Text[21];
    UBYTE                gl_Flag;
} GL_LIST;
GL_LIST *gl_head, *gl_tail;

struct Library *EasyRexxBase;
struct MsgPort *myport;
struct Window *smwin;

void ShutDown(STRPTR, int);
void GetRXMsg(struct ARexxContext *);
void SendRXMsg(struct Gadget *, STRPTR, struct ARexxContext *);
LONG AddTB(STRPTR, STRPTR, STRPTR, STRPTR, STRPTR);
LONG AddCL(STRPTR, STRPTR, STRPTR, STRPTR);
LONG FreeTB(STRPTR);
LONG FreeCL(STRPTR);
void GetImage(struct BitMap **, struct BitMap **, STRPTR, STRPTR);
GL_LIST * AddGad(struct Gadget *, STRPTR, STRPTR, UBYTE, STRPTR);
void UnLink(GL_LIST *);
GL_LIST *FindGad(struct Gadget *);
GL_LIST *FindGadID(STRPTR);
void KillAllGads(struct ARexxContext *);
BOOL FindStartMenu(void);
void SendStartMsg(APTR, UBYTE);

main()
{
    START_MSG      *rep_smsg;
    GADGET_STATS    gs;
    struct ARexxContext *contxt;
    LONG sig, my_sig, rx_sig;
    BOOL done = FALSE;

    if (NULL == (EasyRexxBase = OpenLibrary(EASYREXXNAME, EASYREXXVERSION)))
        ShutDown("This program requires \"easyrexx.library\"", 5);

    if (NULL == (myport = CreatePort(0,0)))
        ShutDown("Could not create port", 5);

    if (!FindStartMenu())
        ShutDown("Could not find StartMenu\n", 5);

    if (NULL == (contxt = AllocARexxContext(
            ER_Author,          "John Corigliano",
            ER_Copyright,       "© 1996 John Corigliano",
            ER_Version,         "1.0",
            ER_Portname,        "START_REXX",
            ER_CommandTable,    cTable,
            TAG_DONE)))
        ShutDown("Could not create ARexx Context", 5);

    my_sig = 1L << myport->mp_SigBit;
    rx_sig = ER_SIGNAL(contxt);
    while (!done || (FALSE == ER_SAFETOQUIT(contxt))) {
        sig = Wait(my_sig | rx_sig | SIGBREAKF_CTRL_C);
        ER_SETSIGNALS(contxt, sig);
        if (sig & my_sig) {
            while (rep_smsg = (START_MSG *)GetMsg(myport)) {
                if (rep_smsg->sm_Command == GADGET_STATUS) {
                    memcpy(&gs, rep_smsg->sm_Data, sizeof(GADGET_STATS));
                    ReplyMsg((struct Message *)rep_smsg);
                    SendRXMsg(gs.gs_Gadget, "HIT", contxt);
                }
                else {
                    RE_GADGET re;
                    memcpy(&re, rep_smsg->sm_Data, sizeof(RE_GADGET));
                    ReplyMsg((struct Message *)rep_smsg);
                    SendRXMsg(re.re_Gadget, "REMOVE", contxt);
                    UnLink(FindGad(re.re_Gadget));
                    if (NULL == gl_head) done = TRUE;
                }
            }
        }
        else if (sig & rx_sig) {
            GetRXMsg(contxt);
        }
        else {
            KillAllGads(contxt);
            done = TRUE;
        }
    }
    FreeARexxContext(contxt);
    ShutDown(NULL, 0);
}

void ShutDown(STRPTR s, int rc)
{
    if (myport) DeletePort(myport);
    if (EasyRexxBase) CloseLibrary(EasyRexxBase);
    if (s) fprintf(stderr, "%s\n", s);
    exit(rc);
}

void GetRXMsg(struct ARexxContext *c)
{
    LONG res = RC_ERROR;
    GL_LIST *work;

    while (GetARexxMsg(c)) {
        switch (c->id) {
            case XADD_TBGAD:
                res = AddTB(ARGSTRING(c,0), ARGSTRING(c,1), ARGSTRING(c,2), ARGSTRING(c,3), ARGSTRING(c,4));
                break;

            case XADD_CLGAD:
                res = AddCL(ARGSTRING(c,0), ARGSTRING(c,1), ARGSTRING(c,2), ARGSTRING(c,3));
                break;

            case XREM_TBGAD:
                res = FreeTB(ARGSTRING(c,0));
                break;

            case XREM_CLGAD:
                res = FreeCL(ARGSTRING(c,0));
                break;

            case XTOGGLE:
                if (work = FindGadID(ARGSTRING(c, 0)))
                    SetGadgetAttrs(work->gl_Gadget, smwin, NULL,
                                           GA_Selected, FALSE, TAG_END);
                break;
        }
        ReplyARexxMsg(c, ER_ReturnCode, res, TAG_DONE);
    }
}

void SendRXMsg(struct Gadget *gad, STRPTR s, struct ARexxContext *c)
{
    GL_LIST *work;
    static UBYTE ar[256];

    if (NULL != (work = FindGad(gad))) {
        sprintf(ar, "ADDRESS %s %s %s", work->gl_PortName, work->gl_GadgetID, s);
        SendARexxCommand(ar, ER_String, TRUE,
                             ER_Asynch, TRUE,
                             ER_Context, c, TAG_DONE);
    }
}

LONG AddTB(STRPTR port, STRPTR name, STRPTR text, STRPTR iff, STRPTR icon)
{
    START_MENU_GADGET smg = {0};
    GL_LIST *work;
    struct BitMap *bm = NULL, *mask = NULL;

    if ((NULL == port) || (NULL == name)) return(RC_ERROR);

    GetImage(&bm, &mask, iff, icon);
    if ((NULL == text) && (NULL == bm)) return(RC_ERROR);

    if (work = AddGad(smg.smg_Gadget, port, name, REM_TASKBAR_GADGET, text)) {
        smg.smg_Title  = work->gl_Text;
        smg.smg_BitMap = bm;
        smg.smg_Mask   = mask;
        smg.smg_Port   = myport;
        smg.smg_FrontPen = smg.smg_BackPen = -1;
        smg.smg_PrivateBM = FALSE;
        SendStartMsg((APTR)(&smg), ADD_TASKBAR_GADGET);

        if (NULL == smg.smg_Gadget) {
            UnLink(work);
            return(RC_ERROR);
        }
        else {
            work->gl_Gadget = smg.smg_Gadget;
            return(RC_OK);
        }
    }
    return(RC_ERROR);
}

LONG AddCL(STRPTR port, STRPTR name, STRPTR iff, STRPTR icon)
{
    START_MENU_GADGET smg = {0};
    GL_LIST *work;
    struct BitMap *bm = NULL, *mask = NULL;

    if ((NULL == port) || (NULL == name)) return(RC_ERROR);

    GetImage(&bm, &mask, iff, icon);
    if (NULL == bm) return(RC_ERROR);

    if (work = AddGad(smg.smg_Gadget, port, name, REM_CLOCK_GADGET, NULL)) {
        smg.smg_Title  = NULL;
        smg.smg_BitMap = bm;
        smg.smg_Mask   = mask;
        smg.smg_Port   = myport;
        smg.smg_PrivateBM = FALSE;
        SendStartMsg((APTR)(&smg), ADD_CLOCK_GADGET);

        if (NULL == smg.smg_Gadget) {
            UnLink(work);
            return(RC_ERROR);
        }
        else {
            work->gl_Gadget = smg.smg_Gadget;
            return(RC_OK);
        }
    }
    return(RC_ERROR);
}

LONG FreeTB(STRPTR id)
{
    GL_LIST *work;
    RE_GADGET re;

    if (work = FindGadID(id)) {
        if (work->gl_Gadget) {
            re.re_Gadget = work->gl_Gadget;
            SendStartMsg((APTR)(&re), REM_TASKBAR_GADGET);
        }
        UnLink(work);
        return(RC_OK);
    }
    return(RC_ERROR);
}

LONG FreeCL(STRPTR id)
{
    GL_LIST *work;
    RE_GADGET re;

    if (work = FindGadID(id)) {
        if (work->gl_Gadget) {
            re.re_Gadget = work->gl_Gadget;
            SendStartMsg((APTR)(&re), REM_CLOCK_GADGET);
        }
        UnLink(work);
        return(RC_OK);
    }
    return(RC_ERROR);
}

void GetImage(struct BitMap **bm, struct BitMap **mask, STRPTR iff, STRPTR icon)
{
    if (NULL != iff) {
        IFF_SCALED iffs = {0};
        iffs.iffs_FileName = iff;
        SendStartMsg((APTR)(&iffs), SCALE_IFF);
        *bm = iffs.iffs_ScaledBM;
        *mask = iffs.iffs_Mask;
    }
    if ((NULL == *bm) && icon) {
        struct DiskObject *d_obj;
        IMAGE_SCALED ims = {0};
        if (d_obj = GetDiskObject(icon)) {
            if (d_obj->do_Gadget.GadgetRender) {
                ims.ims_Image = d_obj->do_Gadget.GadgetRender;
                SendStartMsg((APTR)(&ims), SCALE_IMAGE);
            }
            FreeDiskObject(d_obj);
        }
        *bm = ims.ims_ScaledBM;
        *mask = ims.ims_Mask;
    }
}

GL_LIST * AddGad(struct Gadget *gad, STRPTR port, STRPTR name, UBYTE flg, STRPTR text)
{
    GL_LIST *work;

    if (work = AllocMem(sizeof(GL_LIST), MEMF_CLEAR)) {
        if (NULL != (work->gl_PortName = strdup(port))) {
            if (NULL != (work->gl_GadgetID = strdup(name))) {
                work->gl_Flag = flg;
                if (text) strncpy(work->gl_Text, text, 20);
                if (NULL == gl_head)
                    gl_head = work;
                else
                    gl_tail->gl_Next = work;
                gl_tail = work;
                return(work);
            }
            free(work->gl_PortName);
        }
        FreeMem(work, sizeof(GL_LIST));
    }

    return(NULL);
}

void UnLink(GL_LIST *work)
{
    GL_LIST *temp = gl_head, *prev = NULL;

    while (temp != work) {
        prev = temp;
        temp = temp->gl_Next;
    }

    if (NULL == temp) return;

    if (gl_head == work) {
        if (gl_tail == work) gl_head = gl_tail = NULL;
        else gl_head = work->gl_Next;
    }
    else if (gl_tail = work) {
        prev->gl_Next = NULL;
        gl_tail = prev;
    }
    else
        prev->gl_Next = work->gl_Next;

    free(work->gl_PortName);
    free(work->gl_GadgetID);
    FreeMem(work, sizeof(GL_LIST));
}

GL_LIST *FindGad(struct Gadget *gad)
{
    GL_LIST *work = gl_head;

    while (work) {
        if (work->gl_Gadget == gad) return(work);
        work = work->gl_Next;
    }
    return(NULL);
}

GL_LIST *FindGadID(STRPTR id)
{
    GL_LIST *work = gl_head;

    while (work) {
        if (0 == strcmp(work->gl_GadgetID, id)) return(work);
        work = work->gl_Next;
    }
    return(NULL);
}

void KillAllGads(struct ARexxContext *c)
{
    GL_LIST *work = gl_head, *next;
    RE_GADGET re;

    while (work) {
        next = work->gl_Next;
        if (work->gl_Gadget) {
            re.re_Gadget = work->gl_Gadget;
            SendStartMsg((APTR)(&re), work->gl_Flag);
            SendRXMsg(re.re_Gadget, "REMOVE", c);
        }
        UnLink(work);
        work = next;
    }
}

BOOL FindStartMenu(void)
{
    WORD x = 0;
    struct MsgPort *tport;
    WIN_INFO sm_win = {0};

    while (NULL == (tport = FindPort(STARTMENU_PORT_NAME))) {
        if (++x == 10) break;
        Delay(250);
    }
    if (NULL == tport)
        return(FALSE);

    SendStartMsg((APTR)(&sm_win), GET_WINDOW_INFO);
    if (NULL == (smwin = sm_win.wi_Window)) return(FALSE);

    return(TRUE);
}

void SendStartMsg(APTR data, UBYTE command)
{
    START_MSG msg = {0};
    struct MsgPort *port;

    if (port = FindPort(STARTMENU_PORT_NAME)) {
        msg.sm_Msg.mn_Node.ln_Type = NT_MESSAGE;
        msg.sm_Msg.mn_Length = START_MSG_SIZE;
        msg.sm_Msg.mn_ReplyPort = myport;
        msg.sm_Command = command;
        msg.sm_Data    = data;
        PutMsg(port, (struct Message *)(&msg));
        WaitPort(myport);
        GetMsg(myport);
    }
}
