/* Rexx support for db */

/**********************************************************************/
/*                              Includes                              */
/**********************************************************************/

#include <proto/rexxsyslib.h>
#include <proto/utility.h>                      /* Stricmp() */
#include <proto/exec.h>
#include <string.h>
#include <stdlib.h>     /* atoi() */

/* Included for compatibility */
#include <libraries/gadtools.h>
#include <intuition/intuition.h>                /* struct EasyStruct */
#include <proto/gadtools.h>
#include <proto/intuition.h>
#include <proto/dos.h>                  /* Unlock() */

#include "dbGUI.h"
#include "db.h"
#include "dbRexx.h"
// #include "dbParser.h"

/* Included for compatibility */
#include "dbtexts.h"

/**********************************************************************/
/*                              Defines                               */
/**********************************************************************/

#define REXXPORTNAMELENGTH 50

/**********************************************************************/
/*                              Globals                               */
/**********************************************************************/

/* ARexx specifics */
struct MsgPort *MyRexxPort = NULL;
char MyRexxPortName[REXXPORTNAMELENGTH] ="\0";

int OutstandingRexxCommands = 0;
/* End of Rexx specifics */


/**********************************************************************/
/*                             Prototypes                             */
/**********************************************************************/

extern int DB_SAVE( void );     /* I don't want to include dbGUI.h for this one line */

typedef struct {
        char *arg;
        struct RexxMsg *rxmessage;
        int RC;
        char *Result;
        char buf[10];   /* Sometimes Result points here */
} RXInfo;

/**********************************************************************/
/*                             Functions                              */
/**********************************************************************/

char *NextWord(const char *s)
{
        /* Just steps forward all non space characters + one space character */
        if (!s) return NULL;
        while (*s && (*s++ != ' '));
        return s;
}

int GetFldIndex(struct Pro *Pr, char *s)
{
        /* Returns the fieldindex of the matching field or -1 if not found */
        int i=0;
        struct FldInfo *f = Pr->FirstFldInfo;

        for (; f; f=f->Next, i++) if (!Stricmp(f->Name, s)) return i;
        return -1;
}

Field RXGetFld(struct Pro *Pr, char *fieldname)
{
        /* Returning NULL indicates an error */
        int i;
        if ((i = GetFldIndex(Pr, fieldname)) == -1) return NULL;
        return GetFld(*Pr->Recpp, i);
}

LONG EasyRexxRequest(struct Window *w, struct EasyStruct *es, ULONG *idcmp,
 char *string)
{
        BOOL blocked;
        /* Displays an EasyRequest where the strings are locale string-indexes */
        LONG ret_val;
        struct EasyStruct nes;
        nes.es_StructSize = sizeof(struct EasyStruct);
        nes.es_Flags = 0;
        nes.es_Title = GetAppStr((LONG)es->es_Title);
        nes.es_TextFormat = string;
        nes.es_GadgetFormat = GetAppStr((LONG)es->es_GadgetFormat);
        
        blocked = IsInputBlocked();
        if (w) BlockInput(w, TRUE);
        ret_val = EasyRequestArgs(w, &nes, idcmp, NULL);
        if (w && !blocked) FreeInput(w);
        return ret_val;
}


struct MsgPort *SetupRexxPort(char *basename)
{
        struct MsgPort *port;
        int num=1;

        Forbid();       

        do {    /* Make a unique portname db.<num> */
                sprintf(MyRexxPortName, "%s.%d", basename, num++);
        } while (FindPort(MyRexxPortName));
        port = CreatePort(MyRexxPortName, 0L);

        Permit();
        return port;
}


void ReplyRexxCommand(struct RexxMsg *rxmessage, long rc, char *result)
{
        long rc2 = 0;
        if (!rc && (rxmessage->rm_Action & 1L<<RXFB_RESULT))
                if (result) rc2 = (long)CreateArgstring(result, strlen(result));
        rxmessage->rm_Result1 = rc;
        rxmessage->rm_Result2 = rc2;
        ReplyMsg((struct Message *)rxmessage);
}


/**********************************************************************/
/*                       ARexx command handlers                       */
/**********************************************************************/

extern struct SortInfo {
        struct SortOrder *so;
        size_t norders;
        char SortDir;
        BOOL UnSort;    /* Sort the database based upon each record's address instead */
}SI;


static void Rx_SortOrder(RXInfo *ri)
{
int order;


        if(*ri->arg)
          {
          order = atoi(ri->arg);

          switch(order)
            {
            case 0:
               SI.SortDir = SORT_DIR_AZ;
               break;

            case 1:
                SI.SortDir = SORT_DIR_ZA;
                break;

            default:
                ri->RC = RC_ERROR;
                break;
            }
          }
        else
          {
          stci_d(ri->buf, SI.SortDir);
          ri->Result = ri->buf;
          }

}



static void Rx_Add(RXInfo *ri)
{
        DB_ADD();
}

static void Rx_Kill(RXInfo *ri)
{
        DB_KILL();
}

static void Rx_FirstRecord(RXInfo *ri)
{
        UpdateRecord(CurrentPro);
        if (CurrentPro->Mode == MODE_NORMAL) UpdateRecord(CurrentPro);
        JumpList(CurrentPro, -CurrentPro->RecNum);
        if (CurrentPro->Mode == MODE_NORMAL) {
                UpdateWindow(CurrentPro);
                UpdateDragBar(CurrentPro);
        }
        if (ReactivateGad) ActivateGadget(LastGad, CurrentPro->CurrentLayout->Window, NULL);

        stci_d(ri->buf, CurrentPro->RecNum+1);
        ri->Result = ri->buf;
}

static void Rx_NextRecord(RXInfo *ri)
{
        if (CurrentPro->RecNum < (CurrentPro->RecSum-1)) {
                if (CurrentPro->Mode == MODE_NORMAL) UpdateRecord(CurrentPro);
                JumpList(CurrentPro, 1);
                if (CurrentPro->Mode == MODE_NORMAL) {
                        UpdateWindow(CurrentPro);
                        UpdateDragBar(CurrentPro);
                }
                if (ReactivateGad) ActivateGadget(LastGad, CurrentPro->CurrentLayout->Window, NULL);
        }
        else ri->RC = RC_WARN;
        stci_d(ri->buf, CurrentPro->RecNum+1);
        ri->Result = ri->buf;
}

static void Rx_CurrentRecord(RXInfo *ri)
{
        int recnum;
        if (*ri->arg) {
                recnum = atoi(ri->arg)-1;
                if (recnum >= 0 && recnum < CurrentPro->RecSum) {
                        if (CurrentPro->Mode == MODE_NORMAL) UpdateRecord(CurrentPro);
                        JumpList(CurrentPro, -CurrentPro->RecNum+recnum);
                        if (CurrentPro->Mode == MODE_NORMAL) {
                                UpdateWindow(CurrentPro);
                                UpdateDragBar(CurrentPro);
                        }
                        if (ReactivateGad) ActivateGadget(LastGad, CurrentPro->CurrentLayout->Window, NULL);
                }
                else ri->RC = RC_WARN;
        }
        stci_d(ri->buf, CurrentPro->RecNum+1);
        ri->Result = ri->buf;
}

static void Rx_RecordSum(RXInfo *ri)
{
        stci_d(ri->buf, CurrentPro->RecSum);
        ri->Result = ri->buf;
}

static void Rx_FindFirst(RXInfo *ri)
{
        if (!FindRecord(CurrentPro,0,1)) ri->RC = RC_WARN;
        stci_d(ri->buf, CurrentPro->RecNum+1);
        ri->Result = ri->buf;
}

static void Rx_FindNext(RXInfo *ri)
{
        if (!FindRecord(CurrentPro,CurrentPro->RecNum+1,1)) ri->RC = RC_WARN;
        stci_d(ri->buf, CurrentPro->RecNum+1);
        ri->Result = ri->buf;
}

static void Rx_Cut(RXInfo *ri)
{
        DB_CUT();
}

static void Rx_Copy(RXInfo *ri)
{
        DB_COPY();
}

static void Rx_Paste(RXInfo *ri)
{
        DB_PASTE();
}

static void Rx_Merge(RXInfo *ri)
{
        UpdateRecord(CurrentPro);
        PasteFromClipboard(CurrentPro, *CurrentPro->Recpp);
        CurrentPro->Modified |= PROMODIFIED;
        UpdateGadgets(CurrentPro);
}

static void Rx_FirstField(RXInfo *ri)
{
        struct FldInfo *fi;
        if (fi = GetFldInfo(CurrentPro, 0)) {
                CurrentPro->CurrentFldOffset = 0;
                ri->Result = fi->Name;
        }
        else ri->RC = RC_WARN;  /* No fields at all */
}

static void Rx_NextField(RXInfo *ri)
{
        struct FldInfo *fi;
        if (fi = GetFldInfo(CurrentPro, CurrentPro->CurrentFldOffset+1)) {
                CurrentPro->CurrentFldOffset++;
                ri->Result = fi->Name;
        }
        else ri->RC = RC_WARN;
}

static void Rx_CurrentField(RXInfo *ri)
{
        int offset;
        struct FldInfo *fi;

        if (*ri->arg) {
                if ((offset = GetFldIndex(CurrentPro, ri->arg)) == -1) {
                        ri->RC = RC_WARN;
                        return;
                }
                CurrentPro->CurrentFldOffset = offset;
        }
        if (fi = GetFldInfo(CurrentPro, CurrentPro->CurrentFldOffset)) ri->Result = fi->Name;
        else ri->RC = RC_WARN;  /* Should never happen */
}


static void Rx_GetField(RXInfo *ri)                                                     /* ri->arg is fieldname */
{
        UpdateRecord(CurrentPro);
        if (*ri->arg) ri->Result = RXGetFld(CurrentPro, ri->arg);
        else ri->Result = GetFld(*CurrentPro->Recpp, CurrentPro->CurrentFldOffset);
        if (!ri->Result) ri->RC = RC_WARN;
}


static void Rx_PutField(RXInfo *ri)
{
        UpdateRecord(CurrentPro);
        if (UpdateFld(*CurrentPro->Recpp, CurrentPro->CurrentFldOffset, ri->arg) &&
         CurrentPro->Mode == MODE_NORMAL)
                CurrentPro->Modified |= PROMODIFIED;
        UpdateGadgets(CurrentPro);
}

static void Rx_Mode(RXInfo *ri)
{
        if (*ri->arg) {
                if (!Stricmp(ri->arg, "Normal")) SetProMode(CurrentPro, MODE_NORMAL);
                else if (!Stricmp(ri->arg, "Find")) SetProMode(CurrentPro, MODE_FIND);
                else if (!Stricmp(ri->arg, "Sort")) SetProMode(CurrentPro, MODE_SORT);
                else ri->RC = RC_ERROR;
        }

        switch (CurrentPro->Mode) {
                case MODE_NORMAL:
                        ri->Result = "Normal";
                        break;
                case MODE_FIND:
                        ri->Result = "Find";
                        break;
                case MODE_SORT:
                        ri->Result = "Sort";
                        break;
        }
}

static void Rx_Save(RXInfo *ri)
{
        DB_SAVE();
}

static void Rx_Sort(RXInfo *ri)
{
        DoSort(CurrentPro, -1);
}

static void Rx_Quit(RXInfo *ri)
{
        ReplyRexxCommand(ri->rxmessage, RC_OK, 0);
        ByeBye();
}

static void Rx_UpdateGUI(RXInfo *ri)
{
        BOOL save = CurrentPro->Quiet;
        CurrentPro->Quiet = FALSE;
        SetProMode(CurrentPro, CurrentPro->Mode);
        CurrentPro->Quiet = save;
}

static void Rx_WindowToFront(RXInfo *ri)
{
        if (CurrentPro->CurrentLayout->Window)
                WindowToFront(CurrentPro->CurrentLayout->Window);
        else ri->RC = RC_WARN;

}

static void Rx_ScreenToFront(RXInfo *ri)
{
        ScreenToFront(Scr);
}

static void Rx_ActivateWindow(RXInfo *ri)
{
        if (CurrentPro->CurrentLayout->Window)
                ActivateWindow(CurrentPro->CurrentLayout->Window);
        else ri->RC = RC_WARN;
}

static void Rx_BlockInput(RXInfo *ri)
{
        BlockInput(CurrentPro->CurrentLayout->Window, TRUE);
        UpdateRecord(CurrentPro);
        CurrentPro->Quiet = TRUE;       /* Don't do any GUI stuff from now on */
}

static void Rx_FreeInput(RXInfo *ri)
{
        FreeInput(CurrentPro->CurrentLayout->Window);
        CurrentPro->Quiet = FALSE;              /* Release GUI now */
        Rx_UpdateGUI(ri);
}

static void Rx_Dial(RXInfo *ri)
{
        ri->RC = DoDial(ri->arg);
}

static void Rx_DisplayBeep(RXInfo *ri)
{
        DisplayBeep(Scr);
}

static void Rx_Okay1(RXInfo *ri)
{
        EasyRexxRequest(CurrentPro->CurrentLayout->Window, &ES_RexxOkay1, NULL, ri->arg);
}

static void Rx_Okay2(RXInfo *ri)
{
        stci_d(ri->buf, EasyRexxRequest(CurrentPro->CurrentLayout->Window, &ES_RexxOkay2, NULL, ri->arg));
        ri->Result = ri->buf;
}

static void Rx_CurrentGadget(RXInfo *ri)
{
        int gadindex;
        struct VisFldInfo *vf;

        NextGad = NULL;
        if (*ri->arg) {
                gadindex = atoi(ri->arg);
                if (!gadindex--) {      /* string argument, not a number */
                        for(vf=CurrentPro->CurrentLayout->FirstVisFldInfo; vf; vf = vf->Next)
                                if (!Stricmp(ri->arg, vf->Name)) break;
                }
                else for (vf = CurrentPro->CurrentLayout->FirstVisFldInfo; vf && gadindex > 0; vf = vf->Next, gadindex--);
                if (vf) LastGad = vf->Gadget;
        }
        if (LastGad) {
                for(gadindex=1, vf=CurrentPro->CurrentLayout->FirstVisFldInfo; vf; vf = vf->Next, gadindex++)
                        if (vf->Gadget == LastGad) break;

                stci_d(ri->buf, gadindex);
                ri->Result = ri->buf;

        }
        else ri->RC = RC_WARN;
}

static void Rx_RetryInput(RXInfo *ri)
{
                NextGad = NULL; /* Don't activate NextGad if tabcycling */
                ActivateGadget(LastGad, CurrentPro->CurrentLayout->Window, NULL);
}

static void Rx_CurrentView(RXInfo *ri)
{
        int viewindex;
        struct Layout *lay = CurrentPro->FirstLayout;

        if (*ri->arg) {
                viewindex = atoi(ri->arg);
                if (!viewindex--) {     /* string argument, not a number */
                        for (; lay; lay = lay->NextLayout)
                                if (!Stricmp(ri->arg, lay->Name)) break;
                } else for (; lay && viewindex; lay = lay->NextLayout, viewindex--);
                ChangeView(lay);
        }
        else for (viewindex=1; lay != CurrentPro->CurrentLayout; lay = lay->NextLayout, viewindex++);

        stci_d(ri->buf, viewindex);
        ri->Result = ri->buf;
}

static void Rx_UndoBuffer(RXInfo *ri)
{
        if (LastGad && LastGad->GadgetID == STRING_KIND)
                ri->Result = ((struct StringInfo *)LastGad->SpecialInfo)->UndoBuffer;
        else ri->RC = RC_WARN;
}

static void Rx_GetPortName(RXInfo *ri)  /* Undocumented. The ARexx function Address() should do this job */
{
        ri->Result = MyRexxPortName;
}


/**********************************************************************/
/*                      ARexx command dispatcher                      */
/**********************************************************************/

typedef struct {
        char *name;
        void (*cmd)(RXInfo *);
} Comm;

static Comm Commands[] = {
        { "Add", Rx_Add },
        { "Kill", Rx_Kill }, 
        { "FirstRecord", Rx_FirstRecord, },
        { "NextRecord", Rx_NextRecord },
        { "CurrentRecord", Rx_CurrentRecord },
        { "RecordSum", Rx_RecordSum },
        { "FindFirst", Rx_FindFirst },
        { "FindNext", Rx_FindNext },
        { "Cut", Rx_Cut },
        { "Copy", Rx_Copy },
        { "Paste", Rx_Paste },
        { "Merge", Rx_Merge },
        { "FirstField", Rx_FirstField },
        { "NextField", Rx_NextField },
        { "CurrentField", Rx_CurrentField },
        { "GetField", Rx_GetField },
        { "PutField", Rx_PutField },
        { "Mode", Rx_Mode },
        { "Save", Rx_Save },
        { "Sort", Rx_Sort },
        { "SortOrder", Rx_SortOrder },
        { "Quit", Rx_Quit },
        { "UpdateGUI", Rx_UpdateGUI },
        { "WindowToFront", Rx_WindowToFront },
        { "ScreenToFront", Rx_ScreenToFront },
        { "ActivateWindow", Rx_ActivateWindow },
        { "BlockInput", Rx_BlockInput },
        { "FreeInput", Rx_FreeInput },
        { "Dial", Rx_Dial },
        { "DisplayBeep", Rx_DisplayBeep },
        { "Okay1", Rx_Okay1 },
        { "Okay2", Rx_Okay2 },
        { "CurrentGadget", Rx_CurrentGadget },
        { "UndoBuffer", Rx_UndoBuffer },
        { "RetryInput", Rx_RetryInput },
        { "CurrentView", Rx_CurrentView },
        { "GetPortName", Rx_GetPortName },
        { NULL, NULL }
};


void HandleRexxCommand(struct RexxMsg *rxmessage)
{
        int i, namelen;
        char name[40];
        char *cmdline = ARG0(rxmessage);
        RXInfo ri;

        ri.rxmessage = rxmessage;       /* QUIT needs to answer itself */
        ri.RC = RC_OK;
        ri.Result = NULL;       

        /* Split cmdline into name and arg without affecting cmdline */
        for (i=0; i<30 && cmdline[i] && cmdline[i] != ' '; i++)
                name[i] = cmdline[i];
        name[i] = '\0';
        namelen = strlen(name);
        ri.arg = NextWord(cmdline);

        /* Do the dispatch */
        for (i=0; Commands[i].name; i++)        {
                if (!Strnicmp(Commands[i].name, name, namelen)) {
                        Commands[i].cmd(&ri);   /* Run this command */
                        ReplyRexxCommand(rxmessage, ri.RC, ri.Result);
                        return;
                }
        }
        ReplyRexxCommand(rxmessage,RC_ERROR,NULL);              /* Command not found */
}


void FreeRexxCommand(struct RexxMsg *rxmessage)
{
        struct Window *win = (CurrentPro->CurrentLayout) ?
         CurrentPro->CurrentLayout->Window : NULL;

        if (rxmessage->rm_Result1) {
                if (rxmessage->rm_Result2 == ERR10_001)         /* Program not found */
                        EasyLocRequest(win, &ES_RexxProgNotFound, NULL, ARG0(rxmessage));
                else EasyLocRequest(win, &ES_RexxError, NULL, ARG0(rxmessage), rxmessage->rm_Result2);
        }       
        DeleteArgstring(rxmessage->rm_Args[0]); /* The command name */

        /* In SendRexxCommand we cd to project's dir, cd back and unlock now */
        UnLock(CurrentDir((BPTR)rxmessage->rm_Args[15]));
        DeleteRexxMsg(rxmessage);
        OutstandingRexxCommands--;
}


void CloseRexxPort(struct MsgPort *rexxport)
{
        struct RexxMsg *rxmessage;
        
        do {
                while (rxmessage = (struct RexxMsg *)GetMsg(rexxport)) {
                        if (rxmessage->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
                                FreeRexxCommand(rxmessage);      /* A command finished */
                        else ReplyRexxCommand(rxmessage, ERR10_013, NULL); /* 'Host not found' */
                }
                if (OutstandingRexxCommands) WaitPort(rexxport);
        } while (OutstandingRexxCommands);              /* Wait out all commands we've sent */

        DeletePort(rexxport);
}


BOOL SendRexxCommand(char *name)
{
        struct MsgPort *rexxport;       /* System's Rexx port */
        struct RexxMsg *rxmessage;

        Forbid();
        if (!(rexxport = FindPort(RXSDIR))) {
                Permit();
                return FALSE;
        }
        if (!(rxmessage = CreateRexxMsg(MyRexxPort,
         NULL, MyRexxPort->mp_Node.ln_Name))) {
                Permit();
                return FALSE;
        }

        if (!(rxmessage->rm_Args[0] = CreateArgstring(name, strlen(name)))) {
                Permit();
                return FALSE;
        }
        rxmessage->rm_Action = RXCOMM | RXFB_NOIO;              /* OR a stringcommand here */

        /* Change to the directory of the project */
        /* We put the old lock in Args[15] for FreeRexxCommand() */
        rxmessage->rm_Args[15] =
         (STRPTR)CurrentDir(Lock(CurrentPro->Drawer,ACCESS_READ));
        PutMsg(rexxport, (struct Message *)rxmessage);  /* Send it */
        OutstandingRexxCommands++;                                                              /* For graceful cleanup */
        Permit();
        return TRUE;
}


BOOL SendRexxStrCommand(char *name)
{
        char *str;
        BOOL success;
        int bufsize = strlen(name)+3;

        if (!(str = AllocMem(bufsize, 0))) {
                return FALSE;
        }
        sprintf(str,"\"%s\"", name);
        
        success = SendRexxCommand(str);
        
        FreeMem(str, bufsize);
        return success;
}


BOOL HandleRexxMessage(struct MsgPort *rexxport)
{
        BOOL finished = FALSE;
        struct RexxMsg *rxmessage;
        /* returns TRUE when command is finished */

        /* Is this a reply? ie has a command finished? */
        while (rxmessage = (struct RexxMsg *)GetMsg(rexxport)) {
                if (rxmessage->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {
                        FreeRexxCommand(rxmessage);
                        finished = TRUE;
                        CurrentPro->Quiet = FALSE;
                        if (CurrentPro->CurrentLayout)
                                FreeInput(CurrentPro->CurrentLayout->Window);
                }
                else HandleRexxCommand(rxmessage);
        }
        return finished;
}

