/* XENOSPELL.C based on XENOLINK Spell Checker REXX program */

#include "Include.h"

#define SPELLCACHE 100
#define SPELLSIZE 8

struct RxsLib  *RexxSysBase;
struct MsgPort *RexxPort;

char *SpellPortName;
char *SpellQuitCmd;
char *SpellCheckCmd;
char *SpellExecCmd;
char *Zsp1 = "Performing spell check...";
char *Zsp2 = "Enter new word or RETURN to keep, ^G to stop";
char *Zsp3 = "No spelling errors found.";
char *Zsp4 = "Spelling check finished.  %d unrecognized word(s), %d replaced.";

static BUFFER *hint_buffer;

static char *hints;

/// local functions

int SpellCmd(char *, char *);
int HandleRexxEvents(void);
int REG isdigit(unsigned int);

void highlite(char *, int);

static int waitforspell(void);

static void clear_hintwindow(void);
static void hintwindow(char *, char *);
static void close_hintwindow(void);


/// xenospell()
int xenospell(int f, int n)
{
   LINE *ptline, *wdline;
   int ptoff, wdoff;
   int incorrect = 0;
   int replaced = 0;
   int finished = 0;
   int rc = 0;
   char wd[200];
   char prompt[250];
   char inbuf[NSTRING];
   char cache[SPELLCACHE*SPELLSIZE];
   int next_cache = 0;
   int wd_length;
   char *wp;

   if (!SpellPortName || !(*SpellPortName))
   {
      mlwrite("Spelling checker not installed");
      return TRUE;
   }

   if (!RexxSysBase)
   {
      if (!(RexxSysBase = (struct RxsLib *) OpenLibrary(RXSNAME, 0)))
      {
         mlwrite("Where's AREXX?");
         return TRUE;
      }
   }

   if (!RexxPort)
   {
      if (!(RexxPort = CreatePort(0, 0)))
      {
         mlwrite("Can't create port");
         return TRUE;
      }
   }

   mlwrite(Zsp1);

   if (SpellExecCmd && *SpellExecCmd)
   {
      system(SpellExecCmd);

      if (waitforspell() == FALSE)
      {
         mlwrite("No response from spelling checker");
         return TRUE;
      }
   }

   /* save the original point */
   ptline = curwp->w_dotp;
   ptoff = curwp->w_doto;

   /* go to beginning of buffer */
   gotobob(0, 0);

   while (!finished)
   {
      wp = wd;

      /* skip between words */

      while (inword() == FALSE)
      {
         if (forwchar(FALSE, 1) == FALSE)
         {
            finished = 1;
            break;
         }
      }

      /* scan through the current word */
      if (!finished)
      {
         int all_digits;

         wdline = curwp->w_dotp;
         wdoff = curwp->w_doto;
         wd_length = 0;
         all_digits = 1;

         while (inword() == TRUE)
         {
            *wp = lgetc(curwp->w_dotp, curwp->w_doto);

            if (!isdigit(*wp))
            {
               all_digits = 0;
            }

            ++wp;

            wd_length++;

            if (forwchar(FALSE, 1) == FALSE)
            {
               finished = 1;
               break;
            }
         }

         // ignore numbers

         if (all_digits) continue;

         // ignore things like "AB>" since they are quote markers

         if (lgetc(curwp->w_dotp, curwp->w_doto) == '>')
         {
            continue;
         }
      }

      *wp++ = 0;

      if (*wd)
      {

         // if word is in the cache, dont bother with arexx

         if (wd_length <= SPELLSIZE)
         {
            int i;

            for (i=0; i<next_cache; ++i)
            {
               if (strncmp(wd, &cache[i*SPELLSIZE], wd_length) == 0)
               {
                  break;
               }
            }

            if (i < next_cache)
            {
               mlwrite(wd);
               continue;
            }

            // not in cache, try arexx
         }

         rc = SpellCmd(SpellCheckCmd, wd);

         if (rc == 0)
         {
            ++incorrect;

            if (hints)
            {
               hintwindow(wd, hints);
            }

            highlite(wd, TRUE);

            strcpy(prompt, "[");
            strcat(prompt, wd);
            strcat(prompt, "] ");
            strcat(prompt, Zsp2);
            strcat(prompt, ": ");

            if (getstring(prompt, inbuf, NSTRING, ctoec('\r')) == ABORT)
            {
               finished = 1;
               highlite(wd, FALSE);
               break;
            }

            highlite(wd, FALSE);

            if (*inbuf)
            {
               curwp->w_dotp = wdline;
               curwp->w_doto = wdoff;
               ldelete(strlen(wd), FALSE);
               linstr(inbuf);
               update(FALSE);
               ++replaced;
            }

            if (hints)
            {
               clear_hintwindow();
               free(hints);
               hints = NULL;
            }
         }
         else if (rc == 1)
         {
            // word is correct, add to cache if any room left
            mlwrite(wd);

            if (wd_length <= SPELLSIZE)
            {
               if (next_cache < SPELLCACHE)
               {
                  strncpy(&cache[next_cache*SPELLSIZE], wd, wd_length);
                  next_cache++;
               }
            }
         }
         else
         {
            incorrect = -1;
            finished = 1;
         }
      }
   }

   if (SpellQuitCmd && *SpellQuitCmd)
   {
      SpellCmd(SpellQuitCmd, NULL);
   }

   close_hintwindow();

   if (incorrect == 0)
   {
      // return to original point if no errors
      curwp->w_dotp = ptline;
      curwp->w_doto = ptoff;
      mlwrite(Zsp3);
   }
   else if (incorrect < 0)
   {
      mlwrite("Spelling check failed (%d)", rc);
   }
   else
   {
      mlwrite(Zsp4, incorrect, replaced);
   }

   return TRUE;
}
//-

// Local functions

/// isdigit()
int REG isdigit(unsigned int ch)
{
   return ((ch >= '0' && ch <= '9'));
}
//-

/// clear_hintwindow)
static void clear_hintwindow(void)
{
    nextwind(FALSE, 1);
    swbuffer(hint_buffer);
    gotobob(1, 1);
    setmark(FALSE, 1);
    gotoeob(1, 1);
    killregion(FALSE, 1);
    prevwind(FALSE, 1);
}
//-

/// hintwindow()
static void hintwindow(char *word, char *hintlist)
{
    char *cp;

    if (!hint_buffer) {
   splitwind(TRUE, 2);
   resize(TRUE, 5);
   hint_buffer = bfind("Spell", TRUE, BFINVS);
   prevwind(FALSE, 1);
    }
    clear_hintwindow();
    nextwind(FALSE, 1);
    h_instr("Suggested replacements: ");
    for (cp = hints; *cp; ++cp) lowercase(cp);
    h_instr(hints);
    gotobob(1, 1);
    prevwind(FALSE, 1);
}
//-

/// close_hintwindow()
static void close_hintwindow(void)
{
    if (hints) {
   free(hints);
   hints = NULL;
    }
    if (hint_buffer) {
   nextwind(FALSE, 1);
   delwind(FALSE, 1);
   zotbuf(hint_buffer);
   hint_buffer = NULL;
    }
}
//-

/// waitforspell()
static int waitforspell(void)
{
    struct MsgPort *rp;
    int retry;

    for (retry = 0; retry < 10; ++retry) {
   rp = FindPort(SpellPortName);
   if (rp) return TRUE;
   Delay(50);
    }
    return FALSE;
}
//-

/// highlite()
void highlite(char *wd, int hi)
{
    int n = strlen(wd);
    static short cl = -1;
    short sv;
    extern int cfcolor; // defined and initialized in ansi.c

    // display word and put cursor after it
    update(FALSE);

    // move to beginning of word
    while (n--) {
   ttputs("\033[D");
    }

    // save the current foreground color
    sv = cfcolor;

    if (cl < 0) cl = lookup_color(Xmsgcolor);
    TTforg(cl);
    if (hi) {
   ttputs("\033[7m");
    }
    ttputs(wd);
    if (hi) {
   ttputs("\033[0m");
    }

    // restore the foreground color
    TTforg(sv);
}
//-

/// SpellCmd()
int SpellCmd(char *cmd, char *arg)
{
   ULONG RSignal;
   ULONG CSignal;
   struct RexxMsg *msg;
   char fullcommand[250];
   struct MsgPort *rexxServerPort;
   char *p;

   if (!(msg = CreateRexxMsg(RexxPort, NULL, SpellPortName))) {
      return -10;
   }

   p = fullcommand;
   while (*cmd) *p++ = *cmd++;
   if (arg) {
      *p++ = ' ';
      while (*arg) *p++ = *arg++;
   }
   *p = 0;

   if (!(msg->rm_Args[0] = CreateArgstring(fullcommand, p-fullcommand))) {
      DeleteRexxMsg(msg);
      return -11;
   }

   msg->rm_Action = RXFUNC;
   msg->rm_Args[1] = NULL;
   msg->rm_Action |= RXFF_STRING;
// added this for i-spell
   msg->rm_Action |= RXFF_RESULT;

   Forbid();
   if (rexxServerPort = FindPort(SpellPortName))
      PutMsg(rexxServerPort, (struct Message *) msg);
   Permit();

   if (rexxServerPort) {
      /* wait for response */
      RSignal = 1L << RexxPort->mp_SigBit;
      CSignal = SIGBREAKF_CTRL_C;
      if (Wait(RSignal | CSignal) & CSignal) {
          game_over();
      }
      return HandleRexxEvents();
   }
   else {
      if (msg->rm_Args[1])
         DeleteArgstring(msg->rm_Args[1]);
      DeleteArgstring(msg->rm_Args[0]);
      DeleteRexxMsg(msg);
      return -14;
   }
}
//-

/// HandleRexxEvents()
int HandleRexxEvents(void)
{
    struct RexxMsg *msg;
    int rc = -20;
    char *text;

    while ((msg = (struct RexxMsg *) GetMsg(RexxPort))) {
   if (msg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG) {

       rc = msg->rm_Result1;
       text = (char *) msg->rm_Result2;  // %% NEED TO DELETE THIS?
       if (!text) text = "";
       if (*text == 'o' && *(text+1) == 'k') rc = 1;

       // extended ispell interface
       if (*text == '+' || *text == '*') rc = 1;
       if (*text == '&') {
      // caller must free hints and set it to null when done
      hints = malloc(strlen(text)-2 + 1);
      if (hints) strcpy(hints, text+2);
       }

       if (msg->rm_Args[1]) {
      DeleteArgstring(msg->rm_Args[1]);
       }
       DeleteArgstring(msg->rm_Args[0]);
       DeleteRexxMsg(msg);
   }
    }
    return rc;
}
//-


