/*********************************************************************\
* This file has Intuition-related stuff: menus, fonts, etc (intuit.c) *
\*********************************************************************/

#include <exec/types.h>
#include <exec/exec.h>
#include <stdio.h>
#include <libraries/diskfont.h>
#include <intuition/intuitionbase.h>
#include <intuition/intuition.h>

/* Library pointers imported from main.c */
extern struct IntuitionBase *IntuitionBase;
extern struct GfxBase       *GfxBase;
extern struct DiskfontBase  *DiskfontBase;

/*
 * Initialise an IntuiText structure
 */

/* constants for pen colors */
#define FORE0  (0 << 2)
#define FORE1  (1 << 2)
#define FORE2  (2 << 2)
#define FORE3  (3 << 2)
#define BACK0  0
#define BACK1  1
#define BACK2  2
#define BACK3  3

static InitText(intui, text, pens, left, top) struct IntuiText *intui; char *text; SHORT pens, left, top; {
   intui->FrontPen = (pens >> 2) & 3;
   intui->BackPen = pens & 3;
   intui->DrawMode = JAM2;
   intui->LeftEdge = left;
   intui->TopEdge = top;
   intui->ITextFont = NULL;
   intui->NextText = NULL;
   intui->IText = (UBYTE *)text;
   }

/*
 * Menu Creation
 */

/* macro to compute size in bytes for menu with n items */
#define MenuBytes(n)  (sizeof(struct Menu) + (n)*(sizeof(struct MenuItem) + sizeof(struct IntuiText)))

static struct Menu *menuList = NULL;

static AddMenu(title, size, names, init, rightify) int size, init, rightify; char *title, *names[]; {
   struct Menu **menu;
   struct MenuItem *items = NULL;
   struct IntuiText *text = NULL;
   int n, width = 0, itemLeft = 1, menuLeft = 5;
   USHORT flags = ITEMTEXT | ITEMENABLED | HIGHBOX;
   if (init >= 0) {flags |= CHECKIT; itemLeft += CHECKWIDTH;}
   for (menu = &menuList; *menu; menu = &((*menu)->NextMenu)) menuLeft += 90;
   /* menu now points to where the next menu pointer should go */
   *menu = (struct Menu *)AllocMem(MenuBytes(size), MEMF_CHIP);
   if (*menu == NULL) return(TRUE); /* out of memory */
   items = (struct MenuItem *)(sizeof(**menu) + (UBYTE *)*menu);
   text = (struct IntuiText *)(size*sizeof(*items) + (UBYTE *)items);
   InitText(text, (*menu)->MenuName = title, 0, 0, 0); /* just to measure it */
   (*menu)->Width = 8 + IntuiTextLength(text);
   for (n=0; n<size; n++) {
      int thisWidth;
      InitText(text, *names++, FORE0|BACK1, itemLeft, 1);
      thisWidth = IntuiTextLength(text++);
      width = max(width, thisWidth); /* max is macro; mustn't do ++ twice! */
      }
   text -= size;
   width += 2;
   if (init >= 0) width += CHECKWIDTH;
   (*menu)->LeftEdge = rightify ? 630-width : menuLeft;
   (*menu)->FirstItem = items;
   (*menu)->NextMenu = NULL;
   (*menu)->TopEdge = 0;
   (*menu)->Height = 10;
   (*menu)->Flags = MENUENABLED;
   for (n=0; n<size; n++) {
      items->NextItem = items+1;
      items->LeftEdge = 0;
      items->TopEdge = 11 * n;
      items->Width = width;
      items->Height = 10;
      items->Flags = flags;
      items->MutualExclude = (~(1 << n));
      items->ItemFill = (APTR)text++;
      items->SelectFill = NULL;
      items->Command = 0;
      items->SubItem = NULL;
      items->NextSelect = 0;
      items++;
      }
   (--items)->NextItem = NULL;
   items -= (size-1);
   if (init >= 0 && init < size) items[init].Flags |= CHECKED;
   return(FALSE); /* no problems */
   } /* end of AddMenu */

/* Specific menus */

static char *fileStrings[] = {"Start Capture", "Start Send", "End Capture", "Cancel Send"};
static char *baudStrings[] = {" 300", "1200", "2400", "4800", "9600"};
static char *dpyStrings[] = {"Vanilla", "SAIL"};
static char *beepStrings[] = {"Brash", "Clear", "Calm", "Eerie", "Subdued", "Silent"};
static char *wnStrings[] = {"Move to Back", "Move to Front", "Close Window"};

static InitMenus() { /* returns TRUE if unable to create them all */
   return(AddMenu("File", 2, fileStrings, -1, FALSE)
      || AddMenu("Baud", 5, baudStrings, 1, FALSE)
      || AddMenu("Display", 2, dpyStrings, 1, FALSE)
      || AddMenu("Beep", 6, beepStrings, 1, FALSE)
      || AddMenu("Window", 3, wnStrings, -1, TRUE));
   }

/*
 * SAIL Font (weird chars in positions 0x80-0x9F)
 */

static struct TextFont *tf;

static InitSAILFont(window) struct Window *window; {
   struct TextAttr ta;
   ta.ta_Name = "SAIL.font";
   ta.ta_YSize = 8;
   ta.ta_Style = 0;
   ta.ta_Flags = FPF_ROMFONT | FPF_DISKFONT |
      FPF_PROPORTIONAL | FPF_DESIGNED;
   tf = (struct TextFont *)OpenDiskFont(&ta);
   if (tf) SetFont(window->RPort, tf);
   }

InitWindowStuff(window) struct Window *window; {
   if (InitMenus() || MakeRequester()) {FreeMenus(); return(TRUE);}
   InitSAILFont(window);
   SetMenuStrip(window, menuList);
   return(FALSE); /* no problem */
   }

CleanUpWindow(window) struct Window *window; {
   if (tf) CloseFont(tf);
   if (window) ClearMenuStrip(window);
   FreeMenus();
   }

static FreeMenus() {
   struct MenuItem *item;
   while (menuList) {
      int size = 0;
      struct Menu *next = menuList->NextMenu;
      for (item = menuList->FirstItem; item; item = item->NextItem) size++;
      FreeMem(menuList, MenuBytes(size));
      menuList = next;
      }
   }

/*
 * Function to handle file menu commands, usually via a requester
 */

FileMenu(w, item, r, s, flags) struct Window *w; int item, *flags; FILE **r, **s; {
   /* called with item<0 to fix up menu item text to match current state */
   switch (item) {
      case 0: /* capture */
         if (OpenOrCloseFile(w, r, "Capture to", "w", "Include control chars?")) *flags |= 1;
        else *flags &= ~1;
         break;
      case 1: /* send */
         if (OpenOrCloseFile(w, s, "Send", "r", "Convert LFs to CRs?")) *flags |= 2;
         else *flags &= ~2;
         break;
      }
   ((struct IntuiText *)menuList->FirstItem->ItemFill)->IText =
      (UBYTE *)fileStrings[*r == NULL ? 0 : 2];
   ((struct IntuiText *)menuList->FirstItem->NextItem->ItemFill)->IText =
      (UBYTE *)fileStrings[*s == NULL ? 1 : 3];
   }

/* The Requester */

static struct Requester myreq;

#define FNAMEBUFSIZE 60
static UBYTE fnamebuf[FNAMEBUFSIZE];

static struct StringInfo fname = {
   fnamebuf, NULL, 0, FNAMEBUFSIZE, 0, 0, 0, 0, 0, 0, 0, 0, NULL};

/* border for requester */
#define FILREQWIDTH  400
#define FILREQHEIGHT 130
#define TEXTGAP      14
#define TEXTTOP      10+TEXTGAP*2

static SHORT reqPairs[] = {
   4, 2,                             FILREQWIDTH-5, 2,
   FILREQWIDTH-5, FILREQHEIGHT-3,    4, FILREQHEIGHT-3,
   4, 2,                             5, 2,
   5, FILREQHEIGHT-3,                FILREQWIDTH-6, FILREQHEIGHT-3,
   FILREQWIDTH-6, 2 };

static SHORT textPairs[] = {0, TEXTTOP-TEXTGAP-1, 0, TEXTTOP-TEXTGAP-1};
static SHORT errorPairs[] = {0, TEXTTOP-2*TEXTGAP-1, 0, TEXTTOP-2*TEXTGAP-1};

/* border for activation gadgets */
#define OCTLAP      2
#define OCTWIDTH  100 /* tentative, adjusted based on font width */
#define OCTHEIGHT  27 /* likewise adjusted based on font height */
static SHORT gadPairs[] = {
   OCTLAP*4, -OCTLAP,
   OCTWIDTH-OCTLAP*4-1, -OCTLAP,
   OCTWIDTH+OCTLAP*2-1, OCTLAP*2,
   OCTWIDTH+OCTLAP*2-1, OCTHEIGHT-OCTLAP*2-1,
   OCTWIDTH+OCTLAP*2-2, OCTLAP*2,
   OCTWIDTH+OCTLAP*2-2, OCTHEIGHT-OCTLAP*2-1,
   OCTWIDTH+OCTLAP*2-1, OCTHEIGHT-OCTLAP*2-1, 
   OCTWIDTH-OCTLAP*4-1, OCTHEIGHT+OCTLAP-1,
   OCTLAP*4, OCTHEIGHT+OCTLAP-1,
   -OCTLAP*2, OCTHEIGHT-OCTLAP*2-1,
   -OCTLAP*2, OCTLAP*2,
   -OCTLAP*2+1, OCTHEIGHT-OCTLAP*2-1,
   -OCTLAP*2+1, OCTLAP*2,
   -OCTLAP*2, OCTLAP*2,
   OCTLAP*4, -OCTLAP };

/* border and background for yes/no gadget */
static SHORT ynBkgPairs[48];
static SHORT ynBrdPairs[] = {-2,-1, -2,3, 1,3, 1,-1, -1,-1, -1,3, 0,3, 0,-1};

static struct Border reqBorder = {0, 0, 3, 0, JAM1, 9, reqPairs, NULL};
static struct Border textBorder = {0, 0, 3, 2, JAM1, 2, textPairs, &reqBorder};
static struct Border errBorder = {0, 0, 1, 2, JAM1, 2, errorPairs, &textBorder};

static struct Border gadBorder = {0, 0, 3, 2, JAM1, 15, gadPairs, NULL};

static struct Border ynBorder = {0, 0, 1, 0, JAM1, 8, ynBrdPairs, NULL};
static struct Border ynBackground = {0, 0, 0, 2, JAM1, 24, ynBkgPairs, &ynBorder};

static struct IntuiText gadText[4], ynText[2];
static struct IntuiText reqText, errorText, optText;
static char reqTextChars[30];

static struct Gadget gadgets[5] = {
  {&gadgets[1], 20,-20-OCTHEIGHT,OCTWIDTH,OCTHEIGHT,
   GADGHCOMP | GRELBOTTOM, RELVERIFY | ENDGADGET, REQGADGET | BOOLGADGET,
   (APTR)&gadBorder, NULL, &gadText[0], NULL, NULL, TRUE, NULL},
  {&gadgets[2], -20-OCTWIDTH,-20-OCTHEIGHT,OCTWIDTH,OCTHEIGHT,
   GADGHCOMP | GRELRIGHT | GRELBOTTOM, RELVERIFY | ENDGADGET, REQGADGET | BOOLGADGET,
   (APTR)&gadBorder, NULL, &gadText[1], NULL, NULL, FALSE, NULL},
  {&gadgets[3], 0,TEXTTOP+TEXTGAP*3/2,0,0,
   GADGHCOMP, RELVERIFY, REQGADGET | BOOLGADGET,
   (APTR)&ynBackground, NULL, &ynText[0], NULL, NULL, TRUE, (APTR)&gadgets[3]},
  {&gadgets[4], 0,TEXTTOP+TEXTGAP*3/2,0,0,
   GADGHCOMP, RELVERIFY, REQGADGET | BOOLGADGET,
   (APTR)&ynBackground, NULL, &ynText[1], NULL, NULL, FALSE, (APTR)&gadgets[2]},
  {NULL, FILREQWIDTH/5,TEXTTOP,FILREQWIDTH*3/5,12,
   GADGHCOMP, STRINGCENTER, REQGADGET | STRGADGET,
   NULL, NULL, NULL, NULL, (APTR)&fname, NULL, NULL} };

static MakeRequester() {
   int n, width;
   BYTE height;
   SHORT *coord;
   InitRequester(&myreq);
   myreq.LeftEdge = (640-FILREQWIDTH)/2;
   myreq.TopEdge = 8;
   myreq.Width = FILREQWIDTH;
   myreq.Height = FILREQHEIGHT;
   myreq.ReqGadget = gadgets;
   myreq.ReqBorder = &textBorder;
   myreq.ReqText = &reqText;
   myreq.BackFill = 2;
   GetPrefs(&height, 1); /* get height of default font */
   InitText(&ynText[0], "Yes", FORE1|BACK0, 4, 2);
   InitText(&ynText[1], "No", FORE1|BACK0, 4, 2);
   width = IntuiTextLength(&ynText[0]);
   ynText[1].LeftEdge += (width-IntuiTextLength(&ynText[1])) / 2;
   gadgets[2].Width = gadgets[3].Width = width += 8;
   gadgets[2].Height = gadgets[3].Height = height+3;
   coord = ynBkgPairs;
   for (n=0; n<height+3; n++) {
      *coord++ = n&1 ? width-1 : 0;
      *coord++ = n;
      *coord++ = n&1 ? 0 : (width-1);
      *coord++ = n;
      }
   ynBackground.Count = (height+3)*2; /* number of pairs to draw background */
   coord = ynBrdPairs-1;
   for (n=0; n<ynBorder.Count; n++) {
      if (*++coord >= 0) *coord += width;
      if (*++coord >= 0) *coord += height;
      }
   InitText(&gadText[0], "  CONFIRM  ", FORE0|BACK1, 2, height+1);
   InitText(&gadText[1], " FORGET IT ", FORE0|BACK1, 2, height+1);
   InitText(&gadText[2], "           ", FORE0|BACK1, 2, 1);
   InitText(&gadText[3], "           ", FORE0|BACK1, 2, height*2+1);
   gadText[0].NextText = gadText[1].NextText = &gadText[2];
   gadText[2].NextText = &gadText[3];
   gadgets[1].Width = gadgets[0].Width = width = IntuiTextLength(gadText) + 4;
   gadgets[1].Height = gadgets[0].Height = height = height*3 + 2;
   gadgets[1].LeftEdge = -20-width;
   coord = gadPairs-1;
   for (n=0; n<gadBorder.Count; n++) {
      if (*++coord > OCTWIDTH/2) *coord += width-OCTWIDTH;
      if (*++coord > OCTHEIGHT/2) *coord += height-OCTHEIGHT;
      }
   InitText(&reqText, reqTextChars, FORE2|BACK3, 0, TEXTTOP - TEXTGAP);
   reqText.NextText = &optText;
   InitText(&optText, NULL, FORE1|BACK2, 0, gadgets[2].TopEdge + ynText[0].TopEdge);
   InitText(&errorText, " Can't open file; try again ", FORE0|BACK1, 0, TEXTTOP - 2*TEXTGAP);
   errorText.NextText = &reqText;
   width = IntuiTextLength(&errorText);
   errorPairs[0] = errorText.LeftEdge = (FILREQWIDTH-width) / 2;
   errorPairs[2] = errorPairs[0] + width - 1;
   return(FALSE); /* no problems */
   }

static OpenOrCloseFile(w, file, label, mode, option) struct Window *w; FILE **file; char *label, *mode, *option; {
   struct IntuiMessage *message;
   ULONG class;
   struct Gadget *gadg;
   int proceed, k, opt;
   if (*file != NULL) {fclose(*file); *file = NULL; return(NULL);}
   sprintf(reqTextChars, " %s file: ", label);
   k = IntuiTextLength(&reqText);
   textPairs[0] = reqText.LeftEdge = (FILREQWIDTH-k) / 2;
   textPairs[2] = textPairs[0] + k - 1;
   optText.IText = (UBYTE *)option;
   k = IntuiTextLength(&optText);
   opt = gadgets[2].Width + 2; /* distance between Yes gadget and No gadget */
   optText.LeftEdge = (FILREQWIDTH - (k + 3*opt)) / 2;
   gadgets[2].LeftEdge = optText.LeftEdge + k + opt;
   gadgets[3].LeftEdge = gadgets[2].LeftEdge + opt;
   fnamebuf[0] = k = 0;
   myreq.ReqBorder = &textBorder;
   myreq.ReqText = &reqText;
   gadgets[2].Flags &= ~SELECTED;
   gadgets[3].Flags |= SELECTED;
   gadgets[2].Activation |= TOGGLESELECT;
   gadgets[3].Activation &= ~TOGGLESELECT;
   for (;;) {
      if (! Request(&myreq, w)) return(NULL); /* couldn't open requester */
      for (proceed=TRUE; proceed; ) {
         while (message = (struct IntuiMessage *)GetMsg(w->UserPort)) {
            class = message->Class;
            gadg = (struct Gadget *)message->IAddress;
            ReplyMsg(message);
            switch (class) {
               case GADGETUP:
                  if (gadg->Activation & ENDGADGET) {
                     k = gadg->GadgetID;
                     opt = gadgets[2].Flags & SELECTED;
                     }
                  else if (gadg->Activation & TOGGLESELECT) {
                     gadg->Activation &= ~TOGGLESELECT;
                     gadg = (struct Gadget *)gadg->UserData;
                     gadg->Flags &= ~SELECTED;
                     gadg->Activation |= TOGGLESELECT;
                     RefreshGadgets(&gadgets[2], w, &myreq);
                     }
                  break;
               case REQCLEAR: proceed = FALSE; break;
               }
            }
         }
      /* requester came down, now check what we're supposed to do */
      if (!k) return(NULL); /* user said to forget it */
      if (*file = fopen(fnamebuf, mode)) return(opt); /* no problem */
      /* bring requester back, this time with error message added */
      myreq.ReqBorder = &errBorder;
      myreq.ReqText = &errorText;
      }
   }
