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

 Reversed API client, ©1995 Dietmar Eilert. Dice:

 dcc main.c -// -proto -mi -l reqtoolssr.lib -r -2.0 -o ram:reversed

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

/// "includes"

#define Prototype extern

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

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

Prototype void   main(int, char **);
Prototype int    wbmain(struct WBStartup *);
Prototype void   HandleAPI(char *, struct Settings *);
Prototype ULONG *SendRexxCommand(char *, char *, struct MsgPort *, char *);
Prototype void   Dispatch(struct APIMessage *, struct Settings *);
Prototype LONG   CommandReversedHandler(ULONG *, struct APIMessage *, struct Settings *);
Prototype void   HandleUserInput(struct APIMessage *, struct Settings *);
Prototype BOOL   EvaluateBool(UBYTE *, BOOL);
Prototype void   DoBackspaceReversed(struct APIMessage *, struct EditConfig *);
Prototype void   DoDeleteReversed(struct APIMessage *, struct EditConfig *);

// globals

struct Library *ReqToolsBase;

struct Settings {

    BOOL Active;
};

///
/// "main"

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);
    }
}

void
main(int argc, char **argv)
{
    static UBYTE version[] = "$VER: Reversed 1.5 (" __COMMODORE_DATE__ ")";

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

    struct RDArgs *rdArgs;

    if (rdArgs = ReadArgs("H=HOST/K/A", argArray, NULL)) {

        struct Settings settings = { TRUE };

        HandleAPI((UBYTE *)argArray[0], &settings);

        FreeArgs(rdArgs);
    }

    exit(0);
}

///
/// "api management"

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

 Register with GoldED & handle incoming API messages.

*/

void
HandleAPI(host, settings)

char            *host;
struct Settings *settings;
{
    struct MsgPort *apiPort, *rexxPort;

    if (apiPort = CreateMsgPort()) {

        if (rexxPort = CreateMsgPort()) {

            UBYTE command[255];
            ULONG *result;

            sprintf(command, "API PORT=%ld CLASS=%ld", apiPort, API_CLASS_ROOT | API_CLASS_KEY | API_CLASS_REXX);

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

                if (*result == RC_OK) {

                    BOOL active = TRUE;

                    do {

                        struct APIMessage *apiMsg, *nextMsg;

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

                        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;
                                                    break;

                                                case API_ACTION_INTRODUCE:

                                                    static struct TagItem tags[] = {

                                                        API_Client_Name,      "Reversed",
                                                        API_Client_Copyright, "Reversed ©1995 Dietmar Eilert",
                                                        API_Client_Purpose,   "Right-To-Left input handler",
                                                        API_Client_Template,  "REVERSED USE/K,BACK/S,DEL/S",
                                                        API_Client_Template,  "NEWLINE",
                                                        TAG_DONE
                                                    };

                                                    nextMsg->api_Data = tags;
                                                    break;

                                                default:

                                                    nextMsg->api_Error = API_ERROR_UNKNOWN;
                                            }

                                            break;

                                        case API_CLASS_KEY:

                                            switch (nextMsg->api_Action) {

                                                case API_ACTION_VANILLAKEY:

                                                    HandleUserInput(nextMsg, settings);
                                                    break;

                                                default:

                                                    nextMsg->api_Error = API_ERROR_UNKNOWN;
                                            }
                                            break;

                                        case API_CLASS_REXX:

                                            switch (nextMsg->api_Action) {

                                                case API_ACTION_COMMAND:

                                                    Dispatch(nextMsg, settings);
                                                    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(apiPort));

                    } while (active);
                }
            }

            DeleteMsgPort(rexxPort);
        }

        DeleteMsgPort(apiPort);
    }
}


///
/// "command dispatcher"

/* --------------------------------- Dispatch ----------------------------------

 Dispatch incoming command: examine command string (command part is uppercase
 already), look for handler function related to command, parse arguments (if 
 command supports arguments), call handler.

*/

void
Dispatch(apiMsg, settings)

struct APIMessage *apiMsg;
struct Settings   *settings;
{
    struct RDArgs *rdArgs, *args;

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

        static char buffer[1024];

        // table of supported commands, associated handlers & template strings

        static struct parser { char *command; LONG (*handler)(ULONG *, struct APIMessage *, struct Settings *); char *template; } parser[] = {

            "REVERSED",  (APTR)CommandReversedHandler,  "USE/K,BACK/S,DEL/S",
             NULL
        };

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

        struct APIRexxNotify *notify = (struct APIRexxNotify *)apiMsg->api_Data;

        // make LF-terminated copy of command string (required by dos/readArgs):

        strcpy(buffer, notify->arn_Command);

        strcat(buffer, "\12");

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

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

                char *arguments = buffer + strlen(parser[n].command);

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

                if (parser[n].template) {

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

                        notify->arn_RC = (*parser[n].handler)(argArray, apiMsg, settings);

                        FreeArgs(args);
                    }
                    else {

                        static UBYTE errorText[81];

                        notify->arn_RC           = RC_WARN;
                        notify->arn_CommandError = errorText;

                        Fault(IoErr(), "IoErr()", errorText, 80);
                    }
                }
                else
                    notify->arn_RC = (*parser[n].handler)(argArray, apiMsg, settings);
            }
        }

        FreeDosObject(DOS_RDARGS, rdArgs);
    }
}

/* -------------------------- CommandReversedHandler ---------------------------

 Handle REVERSED command

 template: USE/K,BACK/S,DEL/S

*/

LONG
CommandReversedHandler(argArray, apiMsg, settings)

struct APIMessage *apiMsg;
struct Settings   *settings;
ULONG             *argArray;
{
    struct EditConfig *config = apiMsg->api_Config;

    apiMsg->api_State = API_STATE_CONSUMED;

    if (argArray[0])                                 // USE/K
        settings->Active = EvaluateBool((char *)argArray[0], settings->Active);

    if (argArray[1])                                 // BACK/S
        DoBackspaceReversed(apiMsg, config);

    if (argArray[2])                                 // DEL/S
        DoDeleteReversed(apiMsg, config);

    return(RC_OK);
}

/* ---------------------------- DoBackspaceReversed ----------------------------

 Backspace in reversed-input mode

*/

void
DoBackspaceReversed(apiMsg, config)

struct APIMessage *apiMsg;
struct EditConfig *config;
{
    static struct APIModifyRequest modifyRequest;

    static UBYTE buffer[4096];

    UWORD len;

    if (len = config->CurrentLen)
        movmem(config->CurrentBuffer, buffer, len);

    ++config->Column;

    // expand line in case we moved beyond the end of line

    while (len <= config->Column)
        buffer[len++] = 32;

    if (config->LineNotFixed) {

        movmem(buffer, buffer + 1, config->Column);

        buffer[0] = 32;
    }

    modifyRequest.mr_Next   = NULL;
    modifyRequest.mr_Line   = config->Line;
    modifyRequest.mr_Column = config->Column;
    modifyRequest.mr_Data   = buffer;
    modifyRequest.mr_Size   = len;

    apiMsg->api_Modify = &modifyRequest;
}

/* ------------------------------ DoDeleteReversed -----------------------------

 Delete in reversed-input mode

*/

void
DoDeleteReversed(apiMsg, config)

struct APIMessage *apiMsg;
struct EditConfig *config;
{
    static struct APIModifyRequest modifyRequest;

    static UBYTE buffer[4096];

    UWORD len;

    if (len = config->CurrentLen)
        movmem(config->CurrentBuffer, buffer, len);

    if (config->LineNotFixed) {

        if (config->Column)
            movmem(config->CurrentBuffer, buffer + 1, config->Column);

        *buffer = 32;
    }

    modifyRequest.mr_Next   = NULL;
    modifyRequest.mr_Line   = config->Line;
    modifyRequest.mr_Column = config->Column;
    modifyRequest.mr_Size   = len;
    modifyRequest.mr_Data   = buffer;

    apiMsg->api_Modify = &modifyRequest;
}

///
/// "input handler"

/* ------------------------------ HandleUserInput ------------------------------

 Handle user input

*/

void
HandleUserInput(apiMsg, settings)

struct APIMessage *apiMsg;
struct Settings   *settings;
{
    if (settings->Active) {

        struct EditConfig *editConfig = apiMsg->api_Config;

        if (editConfig->LineNotFixed) {

            static struct APIModifyRequest modifyRequest;

            static UBYTE buffer[4096];

            UWORD column, len;

            editConfig = apiMsg->api_Config;

            column = editConfig->Column;
            len    = editConfig->CurrentLen;

            // get contents of current line

            movmem(editConfig->CurrentBuffer, buffer, len);

            // shift text to the left (i.e. delete first character)

            if (column)
                movmem(buffer + 1, buffer, column);

            // insert character

            while (len <= column)
                buffer[len++] = 32;

            buffer[column] = (UBYTE)apiMsg->api_Data;

            // move cursor to the left

            if (column)
                --column;

            // send modify request to the editor

            modifyRequest.mr_Next   = NULL;
            modifyRequest.mr_Line   = editConfig->Line;
            modifyRequest.mr_Column = column;
            modifyRequest.mr_Size   = len;
            modifyRequest.mr_Data   = buffer;

            apiMsg->api_Modify = &modifyRequest;
        }

        // remove input event from input stream

        apiMsg->api_State = API_STATE_CONSUMED;
    }
}

/// "arexx"
/// "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);
}

///
/// "misc"

/* ---------------------------------- EvaluateBool -----------------------------

 Translate boolean string
 
*/

BOOL
EvaluateBool(arg, value)

UBYTE *arg;
BOOL   value;
{
    if (arg) {

        if (stricmp(arg, "TOGGLE"))
            value = (stricmp(arg, "TRUE") == 0);
        else
            value = !value;
    }

    return(value);
}


///
