/* -----------------------------------------------------------------------------

 GoldED API client example code, ©1995 Dietmar Eilert. Dice:

 dcc main.c -// -mRR -r -proto -2.0 -l tmsr.lib -l reqtoolssr.lib -o ram:dock

 Note: Compiling this code requires ToolManger includes & ToolManager linker
       libraries. ToolManager is ©1990-1995 Stefan Becker.

 The following  example  uses  synchronous  ARexx  communication:  Requests  are
 PutMsg()'ed  to  GoldED's  port,  followed  by a WaitPort() to get the editor's
 response. This works fine since we need no ARexx communication  after  the  API
 link has been established. If there were ARexx communication AFTER the link has
 been established (i.e. after sending the 'API  PORT=...'  command  to  register
 with  GoldED),  we  would  have to use an asynchronous design beeing capable of
 answering incoming API messages while waiting for completion of ARexx  requests
 sent to GoldED.

  ------------------------------------------------------------------------------
*/

/// "includes"

#define Prototype extern

#include <exec/exec.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <intuition/intuition.h>
#include <workbench/startup.h>
#include <rexx/errors.h>
#include <rexx/rxslib.h>
#include <libraries/reqtools.h>
#include <utility/tagitem.h>
#include <clib/toolmanager_protos.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/intuition_protos.h>
#include <clib/rexxsyslib_protos.h>
#include <clib/reqtools_protos.h>

#include "golded:api/include/golded.h"

// ToolManager objects are a combination of action/image/sound:
// Les objets ToolManager sont une combinaison d'objets action/image/son:

struct tmTool {

    APTR exec;
    APTR icon;
    APTR sound;
};

struct Library *ReqToolsBase;

Prototype void   main(int, char **);
Prototype void   HandleAPI(void *, char *, struct TagItem *, UWORD, UWORD);
Prototype ULONG *SendRexxCommand(char *, char *, struct MsgPort *, char *);
Prototype char  *xsprintf(char *, char *);
Prototype struct TagItem *ReadConfig(char *, void *, char *, UWORD *, UWORD *);

///
/// "main"

void
main(int argc, char **argv)
{
    const char *version = "$VER: dock 1.6 (" __COMMODORE_DATE__ ")";

    struct RDArgs *rdArgs;

    ULONG args[] = { 0, 0 };

    if (rdArgs = ReadArgs("CONFIG,HOST/K/A", args, NULL)) {

        UBYTE *prefs;
        UWORD  try;
        void  *handle;

        prefs = args[0] ? (char *)args[0] : "progdir:dock.prefs";

        // wait for ToolManager startup

        for (try = 20; !(handle = AllocTMHandle()) && try; try--)
            Delay(10);

        if (handle) {

            struct TagItem *dock;

            UWORD dockX = ~0, dockY = ~0;

            if (dock = ReadConfig(prefs, handle, (char *)args[1], &dockX, &dockY))
                HandleAPI(handle, (char *)args[1], dock, dockX, dockY);

            FreeTMHandle(handle);
        }
        else
            puts("got no TM handle ?!");

        FreeArgs(rdArgs);
    }
    else
        puts("syntax error: dock [<config>] HOST=<host>");

    exit(0);
}

int
wbmain(struct WBStartup *startup)
{
    if (ReqToolsBase = OpenLibrary("reqtools.library", 37)) {

        rtEZRequestTags("No executable - to be used as API client", "OK", NULL, NULL, TAG_DONE);

        CloseLibrary(ReqToolsBase);
    }
}


///
/// "read config"

/* -------------------------------- ReadConfig ---------------------------------

 Read config file. Return ready-to-use tag array for dock creation or NULL.
 Write dock screen position to <dockX>/<dockY>.

*/

struct TagItem *
ReadConfig(prefs, handle, host, dockX, dockY)

void  *handle;
char  *prefs, *host;
UWORD *dockX, *dockY;
{
    struct TagItem *dock = NULL;

    BPTR config;

    if (config = Open(prefs, MODE_OLDFILE)) {

        struct RDArgs *rdArgs;

        if (rdArgs = AllocDosObject(DOS_RDARGS, NULL)) {

            static char buffer[256];

            UWORD columns, entries, orientation;

            columns     = 1;
            orientation = 0;
            entries     = 0;

            while (FGets(config, buffer, 255)) {

                char *cmd;

                for (strcat(buffer, "\12"), cmd = buffer; *cmd && (*cmd != ';'); ++cmd) {

                    if ((*cmd != ' ') && (*cmd != 10)) {

                        ULONG n, argArray[] = { 0, 0, 0, 0, 0, 0 };

                        struct RDArgs *args;

                        static struct parser { char *command; char *template; } parser[] = {

                            "ADD",  "COMMAND/K/A,AREXX/S,EXEC/S,DIR/K,OUTPUT/K,ICON/K/A",
                            "DOCK", "X/N,Y/N,HORIZONTAL/S,COLUMNS/N",
                             NULL
                        };

                        for (n = 0; parser[n].command; ++n) {

                            if (!memcmp(cmd, parser[n].command, strlen(parser[n].command))) {

                                cmd += strlen(parser[n].command);

                                rdArgs->RDA_Source.CS_Buffer = cmd;
                                rdArgs->RDA_Source.CS_Length = strlen(cmd);
                                rdArgs->RDA_Source.CS_CurChr = 0;
                                rdArgs->RDA_DAList           = NULL;
                                rdArgs->RDA_Buffer           = NULL;

                                if (args = ReadArgs(parser[n].template, argArray, rdArgs)) {

                                    if (n == 0) {

                                        char execName[8], iconName[8];

                                        struct TagItem iconTags[] = { TMOP_File, strdup((char *)argArray[5]), TMOP_Data, NULL, TAG_DONE };

                                        struct TagItem execTags[] = { 

                                            TMOP_Command,    xsprintf((char *)argArray[0], host),
                                            TMOP_ExecType,   argArray[1] ? TMET_ARexx : TMET_CLI,
                                            TMOP_Stack,      8192,
                                            TMOP_CurrentDir, argArray[3] ? strdup((char *)argArray[3]) : NULL,
                                            TMOP_Output,     argArray[4] ? strdup((char *)argArray[4]) : NULL,
                                            TAG_DONE
                                        };

                                        sprintf(execName, "e%ld", entries);
                                        sprintf(iconName, "i%ld", entries++);

                                        CreateTMObjectTagList(handle, strdup(execName), TMOBJTYPE_EXEC,  execTags);
                                        CreateTMObjectTagList(handle, strdup(iconName), TMOBJTYPE_IMAGE, iconTags);
                                    }

                                    else if (n == 1) {

                                        if (argArray[0])
                                            *dockX = *(ULONG *)argArray[0];

                                        if (argArray[1])
                                            *dockY = *(ULONG *)argArray[1];

                                        if (argArray[2])
                                            orientation = 1;

                                        if (argArray[3])
                                            columns = *(ULONG *)argArray[3];
                                    }

                                    FreeArgs(args);
                                }
                            }
                        }

                        break;
                    }
                }

                if (entries) {

                    if (dock = (struct TagItem *)calloc((entries + 9) * sizeof(struct TagItem), 1)) {

                        UWORD n;

                        dock[0].ti_Tag  = TMOP_PubScreen;
                        dock[1].ti_Tag  = TMOP_LeftEdge;
                        dock[2].ti_Tag  = TMOP_TopEdge;
                        dock[3].ti_Tag  = TMOP_Activated;
                        dock[4].ti_Tag  = TMOP_Centered;
                        dock[5].ti_Tag  = TMOP_Columns;
                        dock[6].ti_Tag  = TMOP_Vertical;
                        dock[7].ti_Tag  = TMOP_Text;
                        dock[8].ti_Tag  = TAG_DONE;

                        dock[3].ti_Data = TRUE;
                        dock[5].ti_Data = columns;
                        dock[6].ti_Data = orientation;

                        for (n = 0; n < entries; ++n) {

                            struct tmTool *tool;

                            if (tool = (struct tmTool *)calloc(sizeof(struct tmTool), 1)) {

                                if ((tool->exec = calloc(8, 1)) && (tool->icon = calloc(8, 1))) {

                                    sprintf((char *)tool->exec, "e%ld", n);
                                    sprintf((char *)tool->icon, "i%ld", n);

                                    dock[8 + n].ti_Tag  = TMOP_Tool;
                                    dock[8 + n].ti_Data = tool;
                                }
                            }
                        }
                    }
                }

            }

            FreeDosObject(DOS_RDARGS, rdArgs);
        }

        Close(config);
    }
    else
        puts("dock.prefs missing");

    return(dock);
}


///
/// "API management"

/* --------------------------------- HandleAPI ---------------------------------

 Register with GoldED & handle incoming API messages.

*/

void
HandleAPI(handle, host, dock, dockX, dockY)

struct TagItem *dock;
char           *host;
void           *handle;
UWORD          dockX, dockY;
{
    struct MsgPort *replyPort;

    if (replyPort = CreateMsgPort()) {

        char  command[255];
        ULONG *result;

        sprintf(command, "API PORT=%ld CLASS=%ld", replyPort, API_CLASS_ROOT | API_CLASS_SCREEN);

        if (result = SendRexxCommand(host, command, replyPort, NULL)) {

            if (*result == RC_OK) {

                BOOL active = TRUE;

                do {

                    struct APIMessage *apiMsg, *nextMsg;

                    // API messages might already have arrived since SendRexxCommand() only pulls one message of the port

                    while (!(apiMsg = (struct APIMessage *)GetMsg(replyPort)))
                        WaitPort(replyPort);

                    do {

                        for (nextMsg = apiMsg; nextMsg; nextMsg = nextMsg->api_Next) {

                            if (nextMsg->api_State == API_STATE_NOTIFY) {

                                switch (nextMsg->api_Class) {

                                    case API_CLASS_ROOT:

                                        switch (nextMsg->api_Action) {

                                            case API_ACTION_DIE:

                                                active = FALSE;

                                                DeleteTMObject(handle, "dock1");
                                                break;

                                            case API_ACTION_INTRODUCE:

                                                static struct TagItem tags[] = {

                                                    API_Client_Name,      "dock",
                                                    API_Client_Copyright, "©1995 Dietmar Eilert",
                                                    API_Client_Purpose,   "ToolManager dock for GoldED",
                                                    TAG_DONE
                                                };

                                                nextMsg->api_Data = tags;
                                                break;

                                            default:

                                                nextMsg->api_Error = API_ERROR_UNKNOWN;
                                        }

                                        break;

                                    case API_CLASS_SCREEN:

                                        switch (nextMsg->api_Action) {

                                            case API_ACTION_HIDE:

                                                DeleteTMObject(handle, "dock1");
                                                break;

                                            case API_ACTION_SHOW:

                                                struct Screen *screen;

                                                if (screen = LockPubScreen(nextMsg->api_Global->F_ScrnName)) {

                                                    dock[0].ti_Data = nextMsg->api_Global->F_ScrnName;
                                                    dock[1].ti_Data = (dockX == ~0) ? screen->Width         : dockX;
                                                    dock[2].ti_Data = (dockY == ~0) ? screen->BarHeight + ((screen->BitMap.Depth == 1) ? 2 : 1) : dockY;

                                                    UnlockPubScreen(NULL, screen);
                                                }

                                                CreateTMObjectTagList(handle, "dock1", TMOBJTYPE_DOCK, dock);

                                                break;

                                            default:

                                                nextMsg->api_Error = API_ERROR_UNKNOWN;
                                        }
                                        break;

                                    default:

                                        nextMsg->api_Error = API_ERROR_UNKNOWN;
                                }
                            }
                        }

                        ReplyMsg((struct Message *)apiMsg);

                    } while (apiMsg = (struct APIMessage *)GetMsg(replyPort));

                } while (active);
            }
        }

        DeleteMsgPort(replyPort);
    }
}


///
/// "misc"

/* --------------------------------- xsprintf ----------------------------------

 sprintf frontend (malloc buffer); limited to string insertion into string

*/

char *
xsprintf(mask, var)

char *mask, *var;
{
    char *buffer;

    if (buffer = malloc(strlen(mask) + strlen(var)))
        sprintf(buffer, mask, var);

    return(buffer);
}

///
/// "ARexx"

/* ---------------------------------- SendRexxCommand -------------------------

 Send ARexx message & wait for answer. Return pointer to result or NULL.

*/

ULONG *
SendRexxCommand(port, cmd, replyPort, buffer)

char   *cmd, *port, *buffer;
struct MsgPort *replyPort;
{
    struct MsgPort *rexxport;

    Forbid();

    if (rexxport = FindPort(port)) {

        struct RexxMsg *rexxMsg, *answer;

        if (rexxMsg = CreateRexxMsg(replyPort, NULL, NULL)) {

            if (rexxMsg->rm_Args[0] = CreateArgstring(cmd, strlen(cmd))) {

                static ULONG result;

                rexxMsg->rm_Action = RXCOMM | RXFF_RESULT;

                PutMsg(rexxport, &rexxMsg->rm_Node);

                do {
                    
                    WaitPort(replyPort);

                    if (answer = (struct RexxMsg *)GetMsg(replyPort))
                        result = answer->rm_Result1;

                } while (!answer);

                Permit();

                if (answer->rm_Result1 == RC_OK) {

                    if (answer->rm_Result2) {

                        if (buffer)
                            strcpy(buffer, (char *)answer->rm_Result2);

                        DeleteArgstring((char *)answer->rm_Result2);
                    }
                }

                DeleteArgstring((char *)ARG0(answer));

                DeleteRexxMsg(answer);

                return(&result);
            }
        }
    }

    Permit();

    return(NULL);
}

///
