/*
 *  RemoteRequestChoice
 *
 *  Funktioniert wie das AmigaDOS-Kommando RequestChoice,
 *  ist aber Envoy-Netz-faehig.
 *
 *  Template: HOSTNAME/A,TITLE/A,BODY/A,GADGETS/A/M
 *  Beispiel: RemoteRequestChoice Amiga1 "Hallo!" "Wollen wir essen gehen?" "Ja|Nein"
 *
 *  Diese Version benutzt den GPServer.
 */

#define VERSION     "1.0"
#define DATE        "12.9.94"
#define PROGNAME    "RemoteRequestChoice"

/*
 *  Include-Dateien
 */
#include <stdlib.h>
#define USE_BUILTIN_MATH
#include <string.h>

#include <envoy/envoy.h>
#include <envoy/errors.h>
#include <exec/memory.h>

#include <clib/dos_protos.h>
#include <pragmas/dos_pragmas.h>
#include <clib/exec_protos.h>
#include <pragmas/exec_sysbase_pragmas.h>
#include <clib/nipc_protos.h>
#include <pragmas/nipc_pragmas.h>

#include "GPServer.h"

/*
 *  globale Datan
 */

/* libraries */
extern struct Library *DOSBase;
extern struct Library *SysBase;
static struct Library *NIPCBase;

/* version string */
static const char version[] = "\0$VER: " PROGNAME " " VERSION " (" DATE ")";

/* Daten & Definitionen für die Parameter */
#define TEMPLATE "HOSTNAME/A,TITLE/A,BODY/A,GADGETS/A/M"
#define OPTN_HOSTNAME   0
#define OPTN_TITLE      1
#define OPTN_BODY       2
#define OPTN_GADGETS    3
#define OPTN_COUNT      4
static long options[OPTN_COUNT];
#define HOSTNAME    ((unsigned char *)options[OPTN_HOSTNAME])
#define TITLE       ((unsigned char *)options[OPTN_TITLE])
#define BODY        ((unsigned char *)options[OPTN_BODY])
#define GADGETS     ((unsigned char **)options[OPTN_GADGETS])

/*
 *  Prototypen für Funktionen in dieser Datei
 */
static ULONG remoterequest(void);
static BOOL makedatapacket(struct Transaction *ta);

void main()
{
    struct RDArgs *args;
    ULONG rc = RETURN_OK;

    if (SysBase->lib_Version < 37) exit(RETURN_ERROR); /* unter Kick 2.04 geht nichts! */

    if (NIPCBase = OpenLibrary("nipc.library", 39))
    {
        /* Parameter auswerten */
        if (args = ReadArgs(TEMPLATE, options, NULL))
        {
            rc = remoterequest();
            FreeArgs(args);
        }
        else
        {
            /* Fehler bei den Parametern! */
            PrintFault(IoErr(), NULL);
            rc = RETURN_WARN;
        }

        CloseLibrary(NIPCBase);
    }
    else PutStr("Kann nipc.library V39 oder neuer nicht öffnen!\n");

    exit(rc);
}

static ULONG remoterequest(void)
{
    struct Entity *ausgangs_entity;
    struct Entity *ziel_entity;
    struct Transaction *ta;
    struct Transaction *rta;
    ULONG entitysigbit;
    ULONG error = ENVOYERR_NOERROR;
    ULONG rc = RETURN_ERROR;
    ULONG sigs;
    BOOL quit = FALSE;

    /*  Private Entity anlegen. Dabei lassen wir für die Entity gleich ein
     *  SignalBit belegen. Dieses wird in entitysignal abgelegt.
     */
    if (ausgangs_entity = CreateEntity(ENT_AllocSignal, (ULONG)&entitysigbit, TAG_END))
    {
        /*  Kommunikationspfad errichten */
        if (ziel_entity = FindEntity(HOSTNAME, "GPServer", ausgangs_entity, &error))
        {
            /*  Nun allozieren wir die Transaction; den Puffer für die
             *  Antwort lassen wir gleich mitallozieren
             */
            if (ta = AllocTransaction(TRN_AllocRespBuffer, 4, TAG_END))
            {
                /*  Das Datenpaket für die Anfrage schnüren ... */
                if (makedatapacket(ta))
                {
                    /*  Die Transaction losschicken ... */
                    ta->trans_Command = TACMD_REQUEST;
                    BeginTransaction(ziel_entity, ausgangs_entity, ta);

                    /*  Nun warten wir auf die Antwort ... */
                    while (!quit)
                    {
                        sigs = Wait((1 << entitysigbit) | SIGBREAKF_CTRL_C);

                        if (sigs & (1 << entitysigbit))
                        {
                            /*  Bei unserer Entity ist was angekommen ... */
                            while (rta = GetTransaction(ausgangs_entity))
                            {
                                if (rta->trans_Type == TYPE_RESPONSE)
                                {
                                    /* Unsere Transaction ist zurück ... */
                                    quit = TRUE;
                                }
                                else
                                {
                                    /* Das ist nicht unsere Transaction,
                                     * die wir losgeschickt haben; zur
                                     * Sicherheit schicken wie sie zurück
                                     * Dieser Fall darf eigentlich nie auf-
                                     * treten.
                                     */
                                    ReplyTransaction(rta);
                                }
                            }
                        }

                        if (sigs & SIGBREAKF_CTRL_C)
                        {
                            /* Bei CTRL-C sollen wir abbrechen:
                             * dazu müsen wir die Transaction erst abbrechen.
                             */
                            AbortTransaction(ta);
                            WaitTransaction(ta);
                            quit = TRUE;
                        }
                    }

                    /* Ist ein Fehler aufgetreten? */
                    if (ta->trans_Error == ENVOYERR_NOERROR)
                    {
                        /* Hat die Antwort die richtige Länge? */
                        if (ta->trans_RespDataActual == sizeof(LONG))
                        {
                            /* Ergebnis ausgeben */
                            Printf("%lu\n", *((ULONG *)ta->trans_ResponseData));
                            rc = RETURN_OK;
                        }
                        else PutStr("Antwortpaket hat falsche Länge!\n");
                    }
                    else
                    {
                        Printf("Envoy-Fehler %lu!\n", ta->trans_Error);
                    }

                    /* Datenbereich wieder freigeben */
                    FreeMem(ta->trans_RequestData, ta->trans_ReqDataLength);
                }
                /* Transaction freigeben */
                FreeTransaction(ta);
            }
            /*  Kommunikationspfad freigeben */
            LoseEntity(ziel_entity);
        }
        else
        {
            Printf("GPServer auf Rechner '%s' nicht erreichbar:\n", HOSTNAME);
            Printf("Envoy-Fehler %lu\n", error);
        }

        DeleteEntity(ausgangs_entity);
    }
    else PutStr("Kann Entity nicht erzeugen!\n");

    return(rc);
}

static BOOL makedatapacket(struct Transaction *ta)
{
    /*  Diese Funktion erstellt aus den Kommandozeilenparametern,
     *  die uns der Benutzer angegeben hat, ein Datenpaket, wie es
     *  der Server versteht.
     */
    ULONG datalen;
    ULONG gadgetnum;
    ULONG i;
    unsigned char *data;

    /*  Wie viele Gadgets sind es? */
    for (gadgetnum = 0; GADGETS[gadgetnum] != NULL; gadgetnum++);

    /*  Wieviel Bytes an Daten wollen wir an den Server schicken? */
    datalen = strlen(TITLE) + strlen(BODY) + gadgetnum + 2;
    for (i = 0; i < gadgetnum; i++) datalen += strlen(GADGETS[i]);

    if (data = AllocMem(datalen, MEMF_ANY))
    {
        ta->trans_RequestData = data;
        ta->trans_ReqDataLength = datalen;
        ta->trans_ReqDataActual = datalen;

        /*  Nun müssen wir die Daten noch in der richtigen Form in den
         *  allozierten Speicher kopieren:
         *  Der GPServer erwartet zuerst den TITLE-String,
         *  dann den BODY-String, dann einen String mit den Gadgets;
         *  alle drei Strings sind Null-terminiert; je zwei Gadgets sind
         *  durch einen '|' getrennt. Beispiel: Titel\0Body\0Gad1|Gad2\0
         */
        strcpy(data, TITLE);        /* Titel kopieren   */
        data += strlen(data) + 1;   /* Zeiger vor       */
        strcpy(data, BODY);         /* Body kopieren    */
        data += strlen(data) + 1;   /* Zeiger vor       */

        for (i = 0; i < gadgetnum - 1; i++)
        {
            /* Nun alle Gadgets außer dem letzten kopieren  */
            strcpy(data, GADGETS[i]);   /* Gadget kopieren  */
            data += strlen(data);       /* Zeiger vor       */
            *data = '|';                /* | anhängen       */
            data++;                     /* Zeiger vor       */
        }
        /* Nun das letzte Gadgets kopieren  */
        strcpy(data, GADGETS[gadgetnum - 1]);

        return(TRUE);
    }
    return(FALSE);
}
