#error By license, you aren't permitted to compile this.  Read GUISpell.doc.
#error You may obtain a license from the author,
#error Loren J. Rittle (l-rittle@uiuc.edu).  Again, read GUISpell.doc.
#include <string.h>
#include <devices/clipboard.h>
#include <proto/all.h>
#include "libraries.h"
#include "error.h"
#include "minrexx.h"
#include "textclip.h"

extern long __builtin_getreg (int);
extern void __builtin_putreg (int, long);
#define REG_A4 12

extern char *_ProgramName;

#define VERSION "GUISpell 1.0 (26.3.92)"
#define NICE_VERSION "GUISpell-1.0 (" __DATE__ " " __TIME__ ")"

static char Version_ID[] = "$VER: " VERSION;

char *listText[512];
char *initListText[] =
{
  NICE_VERSION,
  "This is a freely distributable version",
  "Copyright \xa9 1991, 1992  Loren J. Rittle",
  "",
  "GUISpell is the premier GUI front-end",
  "to ISpell 3.1ljr (or later) with ARexx",
  "Server Mode, for AmigaOS Release 2.04.",
  "",
  "If you use this software, please mail",
  "the author at l-rittle@uiuc.edu, so I",
  "can inform you of bugs and upgrades.",
  0,
};

struct List lvList;
struct Gadget *gList;
struct Window *window;
struct Screen *screen;
struct VisualInfo *vi;
void *memory;
struct IOClipReq *ClipReq;
struct MsgPort *ClipPort;
struct Hook ClipHook;
struct ClipHookMsg ClipHookMsg = {0, CMD_UPDATE, 0};
struct Task *Task;
ULONG ClipAlertSignalNum = -1;
BOOL done;
int auto_clip;
int lookup_type;

struct TextAttr topaz80 = {"topaz.font", 8, 0, 0};

char *cycleText[] = {"Spell", "Reg exp", "Exact", 0};

struct TagItem nullTags[] = {TAG_DONE, 0};
struct TagItem cycleTags[] = {GTCY_Labels, &cycleText[0], GTBB_Recessed, FALSE, TAG_DONE, 0};
struct TagItem listviewTags[] = {GTLV_Labels, &lvList, GTLV_ReadOnly, FALSE, GTLV_ScrollWidth, 16, TAG_DONE, 0};
struct TagItem stringTags[] = {GTTX_CopyText, TRUE, GTTX_Border, FALSE, TAG_DONE, 0};

struct
{
  struct TagItem *tags;
  ULONG kind;
  UWORD left, top, width, height;
  ULONG flags;
  char *text;
  struct Gadget *object;
} gads[] =
{
  {cycleTags, CYCLE_KIND, 121, 13, 90, 12, NG_HIGHLABEL, "Lookup Style:", 0},
  {nullTags, CHECKBOX_KIND, 310, 13, 80, 12, NG_HIGHLABEL, "Auto-Clip:", 0},
  {listviewTags, LISTVIEW_KIND, 20, 44, 360, 92, NG_HIGHLABEL | PLACETEXT_ABOVE, "", 0},
  {nullTags, BUTTON_KIND, 234, 27, 81, 12, NG_HIGHLABEL | PLACETEXT_IN,"From Clip", 0},
  {nullTags, BUTTON_KIND, 325, 27, 65, 12, NG_HIGHLABEL | PLACETEXT_IN,"To Clip", 0},
  {nullTags, BUTTON_KIND, 350, 13, 40, 12, NG_HIGHLABEL | PLACETEXT_IN,"Add", 0},
  {stringTags, STRING_KIND, 10, 27, 180, 12, NG_HIGHLABEL, "", 0},
  {nullTags, BUTTON_KIND, 200, 27, 24, 12, NG_HIGHLABEL | PLACETEXT_IN,"Do", 0},
  {0, 0, 0, 0, 0, 0, 0, 0, 0},
};

#define	LOOKUP_STYLE_ID	0
#define	AUTO_CLIP_ID	1
#define	WORD_LIST_ID	2
#define FROM_CLIP_ID	3
#define TO_CLIP_ID	4
#define ADD_WORD_ID	5
#define USER_WORD_ID	6
#define DO_ID		7

int main (int argc, char *argv[]);
void SubmitCheckRequest (int type, char *word);
void UpdateList (struct RexxMsg *msg);
void ProcessGadget (UWORD id, UWORD code);
void GadgetUp (struct IntuiMessage *m);
void *CreateList (struct List *list, char *array[]);
struct Gadget *CreateGadgets (void);
void OpenAll (void);
void CloseAll (void);
void rexxdisp (struct RexxMsg *msg, struct rexxCommandList *dat, char *p);

void rexxcheck (struct RexxMsg *msg, char *p);
//void rexxquickcheck (struct RexxMsg *msg, char *p);
//void rexxlookup (struct RexxMsg *msg, char *p);
//void rexxcheckmode (struct RexxMsg *msg, char *p);
//void rexxautomode (struct RexxMsg *msg, char *p);
//void rexxzoommode (struct RexxMsg *msg, char *p);
//void rexxzoomwindow (struct RexxMsg *msg, char *p);
void rexxcurrenttext (struct RexxMsg *msg, char *p);
void rexxcheckcallbackhook (struct RexxMsg *msg, char *p);
void invokerexxcheckcallbackhook (void);
void rexxversion (struct RexxMsg *msg, char *p);
void rexxexit (struct RexxMsg *msg, char *p);

struct rexxCommandList rcl[] =
{
  {"check", &rexxcheck},
  {"currenttext", &rexxcurrenttext},
  {"checkcallbackhook", &rexxcheckcallbackhook},
  {"version", &rexxversion},
  {"exit", &rexxexit},
  {NULL, NULL}
};

/* This is really kludged up to get it to work under SAS/C 5.10b
   with cres.o.  This code is unportable between compilers and
   in fact may fail with other versions of SAS/C.  */
ULONG __asm ClipChange (register __a0 struct Hook *hook,
			register __a4 void *junk)
{
  __builtin_putreg (REG_A4, (long) hook->h_SubEntry);
  Signal (Task, 1 << ClipAlertSignalNum);
  return 0;
}

int main (int argc, char *argv[])
{
  long rexxbit;

  OnError (&CloseAll);

  {
    int i;

    for (i = 0; initListText[i]; i++)
      listText[i] = initListText[i];
  }

  OpenAll ();

  if (!(rexxbit = upRexxPort ("GUISpell", rcl, 0, &rexxdisp)))
    Error ("ss", _ProgramName, ": MinRexx had a problem.\n");

  while (!done)
    {
      struct IntuiMessage *m;
      long returnbits = Wait ((1 << window->UserPort->mp_SigBit) | rexxbit |
			      (1 << ClipAlertSignalNum) | SIGBREAKF_CTRL_C);

      if (returnbits & SIGBREAKF_CTRL_C)
	done = 1;
      if (returnbits & rexxbit)
	dispRexxPort ();
      if ((returnbits & (1 << ClipAlertSignalNum)) && auto_clip)
	{
          char buffer[256];

          if (TextFromClip (buffer, 256) && !strchr (buffer, ' '))
	    {
	      static struct TagItem tags[] = {GTST_String, 0, TAG_DONE, 0};

	      tags[0].ti_Data = (ULONG) buffer;
	      GT_SetGadgetAttrsA(gads[USER_WORD_ID].object, window, NULL, tags);
	      SubmitCheckRequest (lookup_type, ((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
	    }
	}
      if (returnbits & (1 << window->UserPort->mp_SigBit))
        while (m = GT_GetIMsg (window->UserPort))
	  {
	    struct IntuiMessage msg = *m;
	    GT_ReplyIMsg (m);

	    switch (msg.Class)
	      {
	      case IDCMP_GADGETUP:
		if (msg.Code == 0x09)
		  {
		  }
		if (msg.Code == 0x5f)
		  {
		  }
		else
		  ProcessGadget (((struct Gadget *) msg.IAddress)->GadgetID, msg.Code);
	        break;

	      case IDCMP_REFRESHWINDOW:
	        GT_BeginRefresh (window);
	        GT_EndRefresh (window, TRUE);
	        break;

	      case IDCMP_CLOSEWINDOW:
	        done = 1;
	        break;

	      default:
	        break;
	      }
	  }
    }

  CloseAll ();
  return 0;
}

void SubmitCheckRequest (int lookup_type, char *word)
{
  char buffer[256];
  static char *rexxcommandname[] = {"check ", "lookup ", "quickcheck ", "add "};

  if (lookup_type > 3)
    {
      DisplayBeep (0);
      return;
    }
  strcpy (buffer, rexxcommandname[lookup_type]);
  strncat (buffer, word, 240);
  if (!sendSimpleCmd (buffer, "IRexxSpell", &UpdateList, NULL, NULL, NULL))
    DisplayBeep (0);
}

void UpdateList (struct RexxMsg *msg)
{
  static struct TagItem tags[] = {GTLV_Labels, ~0, TAG_DONE, 0};
  static struct TagItem tags2[] = {GTLV_Labels, &lvList, GTLV_Top, 0, TAG_DONE, 0};
  static char buffer[513];

  if (!msg->rm_Result1 && msg->rm_Result2)
    {
      GT_SetGadgetAttrsA (gads[WORD_LIST_ID].object, window, NULL, tags);

      if (((struct RexxArg *) (msg->rm_Result2-8))->ra_Length > 512)
	{
          memcpy (buffer, (char *) msg->rm_Result2, 512);
	  buffer[512] = '\0';
	}
      else
	{
          memcpy (buffer, (char *) msg->rm_Result2,
		  ((struct RexxArg *) (msg->rm_Result2-8))->ra_Length);
          buffer[((struct RexxArg *) (msg->rm_Result2-8))->ra_Length] = '\0';
	}

      {
	int a, b = 0;

	listText[b++] = &buffer[0];
	for (a = 0; buffer[a]; a++)
	  if (buffer[a] == ' ')
	    {
	      if (buffer[a+1])
		listText[b++] = &buffer[a+1];
	      buffer[a] = '\0';
	    }
	listText[b] = 0;
      }
      
      if (memory)
        {
          FreeVec (memory);
          memory = 0;
        }
      memory = CreateList (&lvList, &listText[0]);

      GT_SetGadgetAttrsA (gads[WORD_LIST_ID].object, window, NULL, tags2);

      DeleteArgstring ((char *) msg->rm_Result2);
    }
  else
    DisplayBeep (0);
}

void ProcessGadget (UWORD id, UWORD code)
{
  switch (id)
    {
    case LOOKUP_STYLE_ID:
      lookup_type = code;
      break;
    case AUTO_CLIP_ID:
      auto_clip = gads[id].object->Flags & SELECTED;
      break;
    case WORD_LIST_ID:
     {
      static struct TagItem tags[] = {GTST_String, 0, TAG_DONE, 0};
      tags[0].ti_Data = (ULONG) listText[code];
      GT_SetGadgetAttrsA(gads[USER_WORD_ID].object, window, NULL, tags);
      if (auto_clip)
	TextToClip (((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
      invokerexxcheckcallbackhook ();
      break;
     }
    case FROM_CLIP_ID:
     {
      char buffer[256];

      if (TextFromClip (buffer, 256))
	{
	  static struct TagItem tags[] = {GTST_String, 0, TAG_DONE, 0};

	  tags[0].ti_Data = (ULONG) buffer;
	  GT_SetGadgetAttrsA(gads[USER_WORD_ID].object, window, NULL, tags);
	  if (!strchr (buffer, ' '))
	    SubmitCheckRequest (lookup_type, ((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
	}
      break;
     }
    case TO_CLIP_ID:
      TextToClip (((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
      break;
    case ADD_WORD_ID:
      SubmitCheckRequest (3, ((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
      break;
    case DO_ID:
    case USER_WORD_ID:
      SubmitCheckRequest (lookup_type, ((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
      break;
    default:
      break;
    }
}

void *CreateList (struct List *list, char *array[])
{
  short i;
  struct Node *np, *start_np;

  NewList (list);
  for (i = 0; array[i]; i++)
    ;
  start_np = AllocVec (sizeof (struct Node) * i, 0);
  if (!start_np)
    Error ("ss", _ProgramName, ": No free memory!\n");

  for (i = 0, np = start_np; array[i]; i++, np++)
    {
      np->ln_Name = (APTR) array[i];
      np->ln_Pri = 0;
      AddTail (list, np);
    }
  return start_np;
}

struct Gadget *CreateGadgets (void)
{
  struct Gadget *gadget, *glist = 0;
  short i;
  struct NewGadget ng;

  gadget = CreateContext (&glist);
  if (!gadget)
    Error ("ss", _ProgramName, ": Can't CreateContext\n");
  for (i = 0; gads[i].tags; i++)
    {
      ng.ng_GadgetID = i;
      ng.ng_TextAttr = &topaz80;
      ng.ng_VisualInfo = vi;
      ng.ng_LeftEdge = gads[i].left;
      ng.ng_TopEdge = gads[i].top;
      ng.ng_Width = gads[i].width;
      ng.ng_Height = gads[i].height;
      ng.ng_GadgetText = gads[i].text;
      ng.ng_Flags = gads[i].flags;
      gads[i].object = gadget = CreateGadgetA (gads[i].kind, gadget, &ng, gads[i].tags);
      if (!gadget)
	Error ("ss", _ProgramName, ": Can't CreateGadget\n");
    }
  return glist;
}

void OpenAll (void)
{
  static struct TagItem tags[] = {GTLV_Labels, ~0, TAG_DONE, 0};
  static WORD zoom_data[4] = {0, 0, 400, 43};
  static struct TagItem wtags[] =
    {
      WA_Left, 0, WA_Top, 0, WA_Width, 400, WA_Height, 143,
      WA_MinWidth, 400, WA_MinHeight, 43, WA_MaxWidth, -1, WA_MaxHeight, -1,
      WA_DetailPen, 0, WA_BlockPen, 3, WA_IDCMP, 
      IDCMP_REFRESHWINDOW|IDCMP_CLOSEWINDOW|BUTTONIDCMP|CYCLEIDCMP|LISTVIEWIDCMP,
      WA_Gadgets, 0 /*set below*/, WA_PubScreen, 0, /*set below*/
      WA_Title, (ULONG)"GUISpell-1.0", WA_SuperBitMap, 0,
      WA_SizeGadget, FALSE, WA_DragBar, TRUE, WA_DepthGadget, TRUE,
      WA_CloseGadget, TRUE, WA_Backdrop, FALSE, WA_ReportMouse, FALSE,
      WA_Borderless, FALSE, WA_Activate, TRUE, WA_RMBTrap, FALSE,
      WA_SimpleRefresh, TRUE, WA_Zoom, (ULONG)zoom_data, WA_RMBTrap, TRUE, TAG_DONE, 0
    };

  OpenLibraries ();

  screen = LockPubScreen ("CygnusEdScreen1");
  if (!screen)
    screen = LockPubScreen ("Workbench");
  if (!screen)
    Error ("ss", _ProgramName, ": Can't Lock WB Screen!\n");

  vi = GetVisualInfoA (screen, TAG_DONE);
  if (!vi)
    Error ("ss", _ProgramName, ": Can't GetVisualInfoA\n");

  memory = CreateList (&lvList, &listText[0]);
  gList = CreateGadgets ();
  wtags[11].ti_Data = (ULONG) gList;
  wtags[12].ti_Data = (ULONG) screen;
  window = OpenWindowTagList (NULL, wtags);
  UnlockPubScreen (0, screen);
  if (!window)
    Error ("ss", _ProgramName, ": Can't open window\n");
  GT_RefreshWindow (window, NULL);
  GT_SetGadgetAttrsA (gads[WORD_LIST_ID].object, window, NULL, tags);

  Task = FindTask (0);
  if ((ClipAlertSignalNum = AllocSignal (-1)) == -1)
    Error ("ss", _ProgramName, ": Can't get a signal\n");
  if (!(ClipPort = CreatePort (0L, 0L)))
    Error ("ss", _ProgramName, ": Can't create port\n");
  if (!(ClipReq = (struct IOClipReq *) CreateExtIO (ClipPort, sizeof (*ClipReq))))
    Error ("ss", _ProgramName, ": Can't create ExtIO\n");
  if (OpenDevice ("clipboard.device", 0L, (struct IORequest *) ClipReq, 0L))
    Error ("ss", _ProgramName, ": Can't open clipboard device\n");
  ClipReq->io_Command = CBD_CHANGEHOOK;
  ClipReq->io_Length = 1;
  ClipReq->io_Data = (void *) &ClipHook;
  ClipHook.h_Entry = (void *) &ClipChange;
  ClipHook.h_SubEntry = (void *) __builtin_getreg (REG_A4);
  ClipHook.h_Data = &ClipHookMsg;
  if (DoIO ((struct IORequest *) ClipReq))
    Error ("ss", _ProgramName, ": Can't install clipboard hook\n");
}

void CloseAll (void)
{
  struct IntuiMessage *msg;

  if (ClipReq)
    {
      ClipReq->io_Command = CBD_CHANGEHOOK;
      ClipReq->io_Length = 0;
      ClipReq->io_Data = (void *) &ClipHook;
      if (DoIO ((struct IORequest *) ClipReq))
        Warning ("ss", _ProgramName, ": Can't deinstall clipboard hook (crash soon... :-)\n");
      if (ClipReq->io_Device)
        CloseDevice ((struct IORequest *)ClipReq);
      DeleteExtIO ((struct IORequest *)ClipReq);
    }
  if (ClipPort)
    DeletePort (ClipPort);
  if (ClipAlertSignalNum != -1)
    FreeSignal (ClipAlertSignalNum);

  dnRexxPort ();

  if (window)
    {
      while (msg = (struct IntuiMessage *) GT_GetIMsg (window->UserPort))
	GT_ReplyIMsg (msg);
      CloseWindow (window);
      window = 0;
    }

  if (vi)
    {
      FreeVisualInfo (vi);
      vi = 0;
    }

  if (gList)
    {
      FreeGadgets (gList);
      gList = 0;
    }

  if (memory)
    {
      FreeVec (memory);
      memory = 0;
    }

  CloseLibraries ();
}

void rexxdisp (struct RexxMsg *msg, struct rexxCommandList *dat, char *p)
{
  dat->userdata(msg, p);
}

void rexxcheck (struct RexxMsg *msg, char *p)
{
  static struct TagItem tags[] = {GTST_String, 0, TAG_DONE, 0};

  tags[0].ti_Data = (ULONG) p;
  GT_SetGadgetAttrsA(gads[USER_WORD_ID].object, window, NULL, tags);
  SubmitCheckRequest (0, ((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
  replyRexxCmd (msg, 0L, 0L, "ok");
}

void rexxcurrenttext (struct RexxMsg *msg, char *p)
{
  replyRexxCmd (msg, 0L, 0L, ((struct StringInfo *)gads[USER_WORD_ID].object->SpecialInfo)->Buffer);
}

char checkcallbackhookcode[512];

void rexxcheckcallbackhook (struct RexxMsg *msg, char *p)
{
  if (!checkcallbackhookcode[0] || (strlen (p) < sizeof (checkcallbackhookcode)))
    {
      strcpy (checkcallbackhookcode, p);
      replyRexxCmd (msg, 0L, 0L, "installed");
    }
  else
    replyRexxCmd (msg, 0L, 0L, "notinstalled");
}

void invokerexxcheckcallbackhook (void)
{
  if (checkcallbackhookcode[0] && !asyncRexxCmd (checkcallbackhookcode))
    DisplayBeep (0);
  checkcallbackhookcode[0] = '\0';
}

void rexxversion (struct RexxMsg *msg, char *p)
{
  char buf[512];
  int bufend = strlen(Version_ID+6);
  int namesize;
  int i;

  strcpy(buf, Version_ID+6);
  buf[bufend++] = '\n';
  for (i = 0; rcl[i].name != NULL; i++)
    {
      namesize = strlen(rcl[i].name);
      if ((namesize + bufend) > 511)
	break;
      strcpy (&(buf[bufend++]), " ");
      strcpy (&(buf[bufend]), rcl[i].name);
      bufend += namesize;
    }
  replyRexxCmd (msg, 0L, 0L, buf);
}

void rexxexit (struct RexxMsg *msg, char *p)
{
  done = 1;
  replyRexxCmd (msg, 0L, 0L, "bye");
}
