/*---------------------------------------------------------*
 | Author:  Maurizio Loreti, aka MLO or I3NOO.             |
 | Address: University of Padova - Department of Physics   |
 |          Via F. Marzolo, 8 - 35131 PADOVA - Italy       |
 | Phone:   (39)(49) 844-313         FAX: (39)(49) 844-245 |
 | E-Mail:  LORETI at IPDINFN (BITNET); or VAXFPD::LORETI  |
 |         (DECnet) - VAXFPD is node 38.257 i.e. 39169; or |
 |          LORETI@PADOVA.INFN.IT (INTERNET).              |
 | Home: Via G. Donizetti 6 - 35010 CADONEGHE (PD) - Italy |
 *---------------------------------------------------------*/

#include <stdio.h>                      /* Standard library */
#include <string.h>
#include <exec/types.h>                 /* Amiga specific */
#include <intuition/intuition.h>
#include <graphics/gfxbase.h>
#include <libraries/dos.h>
#include <devices/printer.h>
#include <clib/exec_protos.h>
#include <clib/intuition_protos.h>
#include <clib/graphics_protos.h>
#if defined(REQ) | defined(BOTH)
#include <libraries/reqbase.h>
#endif
#include "mlo.h"                        /* Program specific */
#include "pf2.h"
#include "ext.h"
#include "aw.h"

static int aw_ytitle, aw_ycycle;

static int Ask(char *pTitle, int nText, char *Text[]);
static int AskInt(char *pTitle, short valMin, short valMax, short *pVal);
static void MakeWindow(struct Gadget *pG);

void SetupWB(void)
{

/**
 | Printer setup, driven with requester windows when called from
 | the Workbench.
**/
  char  cOri[] = "Orientation";
  char *tOri[] = {
    "Portrait",
    "Landscape",
    "\"Special\""
  };
  #define nOri (sizeof(tOri) / sizeof(char *))

  char  cFon[] = "Font";
  char *tFon[] = {
    "Courier",
    "Letter-Gothic",
    "Times"
  };
  #define nFon (sizeof(tFon) / sizeof(char *))

  char  cPit[]  = "Horizontal pitch";
  char *tPitC[] = {
    "10 characters per inch",
    "16.67 characters per inch",
    "20 characters per inch"
  };
  char *tPitG[] = {
    "12 characters per inch",
    "24 characters per inch"
  };
  #define nPitC (sizeof(tPitC) / sizeof(char *))
  #define nPitG (sizeof(tPitG) / sizeof(char *))

  char  cSty[] = "Style";
  char *tSty[] = {
    "Roman",
    "Italic"
  };
  #define nSty (sizeof(tSty) / sizeof(char *))

  char  cHei[] = "Character height";
  char *tHei[] = {
    "12 points",
    "6 points"
  };
  #define nHei (sizeof(tHei) / sizeof(char *))

  char  cQua[] = "Quality";
  char *tQua[] = {
    "Draft",
    "Letter"
  };
  #define nQua (sizeof(tQua) / sizeof(char *))

  char  cSpa[] = "Vertical spacing";
  char *tSpa[] = {
    "6 lines per inch",
    "8 lines per inch"
  };
  #define nSpa (sizeof(tSpa) / sizeof(char *))

/**
 | Open required libraries: try graphics library version 37 (Kickstart
 | 2.04) at first and, if successfull, use asl.library; if not, try
 | version 33 (Kickstart 1.2) and use req.library.
**/

#ifdef ASL
  GfxBase = LibOpen("graphics.library", REVISION_2, LO_ABORT | LO_MESSAGE);
  AslBase = LibOpen("asl.library",      REVISION_2, LO_ABORT | LO_MESSAGE);
#endif

#ifdef REQ
  GfxBase = LibOpen("graphics.library", REVISION, LO_ABORT | LO_MESSAGE);
  ReqBase = LibOpen("req.library",      0,        LO_ABORT | LO_MESSAGE);
#endif

#ifdef BOTH
  GfxBase = LibOpen("graphics.library", REVISION_2, 0);
  if (GfxBase == NULL) {
    GfxBase = LibOpen("graphics.library", REVISION,
                      LO_ABORT | LO_MESSAGE);
    ReqBase = LibOpen("req.library", 0, LO_ABORT | LO_MESSAGE);
  } else {
    AslBase = LibOpen("asl.library", REVISION_2, LO_ABORT | LO_MESSAGE);
  }
#endif

/**
 | Open topaz-8 font (in RAM)
**/

  if ((Topaz8 = (struct TextFont *) OpenFont(&ta)) == NULL) {
    printf("Can't open Topaz-8 font!\n");
    Cleanup(SYS_ABORT_CODE);
  }

r1:
  switch (Ask(cOri, nOri, tOri)) {
    case QUIT:
    case CANCEL:
      Cleanup(SYS_NORMAL_CODE);
    case 1:
      PageMode = SINGLE_PAGE;
      Orientation = PORTRAIT;
r2:   switch (Ask(cFon, nFon, tFon)) {
        case QUIT:
          Cleanup(SYS_NORMAL_CODE);
        case CANCEL:
          goto r1;
        case 1:
          Font = COURIER;
          switch (Ask(cPit, nPitC, tPitC)) {
            case QUIT:
              Cleanup(SYS_NORMAL_CODE);
            case CANCEL:
              goto r2;
            case 1:
              Pitch = P10CPI;
              break;
            case 2:
              Pitch = P16_67CPI;
              break;
            case 3:
              Pitch = P20CPI;
              break;
          }
          break;
        case 2:
          Font = GOTHIC;
          switch (Ask(cPit, nPitG, tPitG)) {
            case QUIT:
              Cleanup(SYS_NORMAL_CODE);
            case CANCEL:
              goto r2;
            case 1:
              Pitch = P12CPI;
              break;
            case 2:
              Pitch = P24CPI;
          }
          break;
        case 3:
          Font = TIMES;
          Pitch = PROPORTIONAL;
          break;
      }

      if (Font == COURIER  &&  Pitch == P16_67CPI) {
        Style = ROMAN;
      } else {
        switch (Ask(cSty, nSty, tSty)) {
          case QUIT:
            Cleanup(SYS_NORMAL_CODE);
          case CANCEL:
            goto r2;
          case 1:
            Style = ROMAN;
            break;
          case 2:
            Style = ITALIC;
            break;
        }
      }
      break;

    case 2:
r2l:  Orientation = LANDSCAPE;
      PageMode = SINGLE_PAGE;
      Font = COURIER;
      switch (Ask(cPit, nPitC, tPitC)) {
        case QUIT:
          Cleanup(SYS_NORMAL_CODE);
        case CANCEL:
          goto r1;
        case 1:
          Pitch = P10CPI;
          break;
        case 2:
          Pitch = P16_67CPI;
          break;
        case 3:
          Pitch = P20CPI;
          break;
      }
      break;
    case 3:
      PageMode = LEFT_PAGE;
      goto r5;
  }

r3:
  switch (Ask(cHei, nHei, tHei)) {
    case QUIT:
      Cleanup(SYS_NORMAL_CODE);
    case CANCEL:
      if (Orientation == PORTRAIT)  goto r2;
      else                          goto r2l;
    case 1:
      Height = 12;
      break;
    case 2:
      Height = 6;
      break;
  }

r4:
  switch (Ask(cSpa, nSpa, tSpa)) {
    case QUIT:
      Cleanup(SYS_NORMAL_CODE);
    case CANCEL:
      goto r3;
    case 1:
      Lpi = 6;
      break;
    case 2:
      Lpi = 8;
      break;
  }

r5:
  switch (Ask(cQua, nQua, tQua)) {
    case QUIT:
      Cleanup(SYS_NORMAL_CODE);
    case CANCEL:
      if (PageMode == LEFT_PAGE)   goto r1;
         else                      goto r4;
    case 1:
      Quality = DRAFT_Q;
      break;
    case 2:
      Quality = LETTER_Q;
      break;
  }

  if (PageMode == LEFT_PAGE)    goto r7;

r6:
  switch (AskInt("Leading blanks:", 0, 40, &nBlanks)) {
    case QUIT:
      Cleanup(SYS_NORMAL_CODE);
    case CANCEL:
      goto r5;
    case OK_FOLKS:
      if (nBlanks) {
        Buffer = inBuffer + nBlanks;
        memset(inBuffer, BLANK, nBlanks);
      }
  }

r7:
  switch (AskInt("TAB expansion:", 2, 20, &nTabs)) {
    case QUIT:
      Cleanup(SYS_NORMAL_CODE);
    case CANCEL:
      if (PageMode == LEFT_PAGE) {
        goto r5;
      }
      goto r6;
    case OK_FOLKS:
      windowOff();
      if (PageMode == LEFT_PAGE)    SetSpecialMode();
  }
}

void windowOff(void)
{

/**
 | Closes the requester window (if opened), saving the current
 | values for left top edge coordinates.
**/

  struct Message *pIM;

  if (Wind != NULL) {
    while ((pIM = GetMsg(Wind->UserPort)) != NULL) ReplyMsg(pIM);
    NWind.LeftEdge = Wind->LeftEdge;
    NWind.TopEdge  = Wind->TopEdge;
    CloseWindow(Wind);
    Wind = NULL;
  }
}

void *LibOpen(
  char  *Name,                    /* Library name */
  long   Rev,                     /* Revision level (or 0) */
  USHORT flag                     /* Abort flag */
){

/**
 | Open required library
**/

  void *p;

  p = OpenLibrary(Name, Rev);
  if (p == NULL) {
    if (flag & LO_MESSAGE) {
      fprintf(stderr, "This program requires %s", Name);
      if (Rev) {
        fprintf(stderr, " Rev. %d.\n", Rev);
      } else {
        fputs("", stderr);
      }
    }
    if (flag & LO_ABORT)    Cleanup(SYS_ABORT_CODE);
  }
  return p;
}

static int Ask(
  char *pTitle,                   /* General title */
  int nText,                      /* Number of choices */
  char *cText[]                    /* Description */
){

/**
 | This routine displays a "cycle gadget" (an array of nText choices
 | described by cText[]): i.e. a window where the general title and the
 | current choice are displayed, together with a forward- and a backward-
 | cycling gadget, and the 3 fixed gadgets "OK", "QUIT" and "CANCEL".
 | The return value is either CANCEL (0), or QUIT (-1), or, if OK was
 | selected, a number in the range 1..nText identifying the currently
 | selected cycle-gadget choice. The window size should be enough to
 | display the pTitle and cText strings.
**/

  int x;                          /* X coordinate, current string */
  int n;                          /* Number of characters, cur. string */
  int newx, newn, newcurrent;
  int current = 0;                /* Current cycle gadget choice */
  struct IntuiMessage *pIM;       /* Intuition message */
  ULONG MClass;                   /* Message type */
  struct RastPort *pRP;           /* Requester window raster port */
  struct Gadget *pG;              /* Selected gadget */

/**
 | Make sure that the requester window is opened
**/

  MakeWindow(&forwGadget);

/**
 | Clear the window, and display the general title and the first
 | cycle-gadget choice.
**/

  SetAPen((pRP = Wind->RPort), BLACK_PEN);
  RectFill(pRP, Wind->BorderLeft, Wind->BorderTop,
           realWidth, realHeight);
  RefreshGadgets(Wind->FirstGadget, Wind, NULL);

  SetAPen(pRP, RED_PEN);
  SetBPen(pRP, BLACK_PEN);
  x = (realWidth - TextLength(pRP, pTitle, (n = strlen(pTitle)))) / 2;
  Move(pRP, x, aw_ytitle);
  (void) Text(pRP, pTitle, n);

  x = (realWidth - TextLength(pRP, cText[0], (n = strlen(cText[0])))) / 2;
  SetAPen(pRP, WHITE_PEN);
  Move(pRP, x, aw_ycycle);
  (void) Text(pRP, cText[0], n);

/**
 | Now, wait for gadget hits and perform the appropriate action.
 | Exit when either OK or CANCEL or QUIT has been hit.
**/

  FOREVER {
    (void) Wait(1L << Wind->UserPort->mp_SigBit);
    while ((pIM = (struct IntuiMessage *)
            GetMsg(Wind->UserPort)) != NULL) {
      MClass = pIM->Class;
      pG = (struct Gadget *) pIM->IAddress;
      ReplyMsg((struct Message *) pIM);

      switch (MClass) {
        case GADGETUP:

          switch (pG->GadgetID) {
            case AW_CANCEL:
              return CANCEL;
            case AW_QUIT:
              return QUIT;
            case AW_OK:
              return ++current;
            case AW_FORWARD:
              newcurrent = current + 1;
              if (newcurrent >= nText) {
                newcurrent = 0;
              }
              newx = (realWidth - TextLength(pRP, cText[newcurrent],
                  (newn = strlen(cText[newcurrent])))) / 2;
              break;
            case AW_BACKWARD:
              newcurrent = current - 1;
              if (newcurrent < 0) newcurrent = nText - 1;
              newx = (realWidth - TextLength(pRP, cText[newcurrent],
                  (newn = strlen(cText[newcurrent])))) / 2;
              break;
          }

          SetAPen(pRP, BLACK_PEN);
          Move(pRP, x, aw_ycycle);
          (void) Text(pRP, cText[current], n);
          SetAPen(pRP, WHITE_PEN);

          current = newcurrent;
          x = newx;
          n = newn;
          Move(pRP, x, aw_ycycle);
          (void) Text(pRP, cText[current], n);
      }
    }
  }
}

static int AskInt(
  char *pTitle,                   /* General title */
  short int valMin,               /* Minimum allowed value */
  short int valMax,               /* Maximum allowed value */
  short int *pVal                 /* Output */
){

/**
 | This procedure displays a requester for an integer value,
 | to be checked against the [valMin,valMax] range.
**/

  int x;                          /* X coordinate, title */
  int n;                          /* Number of characters, title */
  struct IntuiMessage *pIM;       /* Intuition message */
  ULONG MClass;                   /* Message type */
  struct RastPort *pRP;           /* Requester window raster port */
  struct Gadget *pG;              /* Selected gadget */

/**
 | Make sure that the requester window is opened; then clear
 | the window, and display the general title.
**/

  MakeWindow(&intGadget);
  sprintf(intBuffer, "%d", (intInfo.LongInt = *pVal));

  SetAPen((pRP = Wind->RPort), BLACK_PEN);
  RectFill(pRP, Wind->BorderLeft, Wind->BorderTop,
           realWidth, realHeight);
  (void) ActivateGadget(&intGadget, Wind, NULL);
  RefreshGadgets(Wind->FirstGadget, Wind, NULL);

  SetAPen(pRP, RED_PEN);
  SetBPen(pRP, BLACK_PEN);
  x = (realWidth - TextLength(pRP, pTitle, (n = strlen(pTitle)))) / 2;
  Move(pRP, x, aw_ytitle);
  (void) Text(pRP, pTitle, n);
  sprintf(Limits, "Min = %d, Max = %d.", valMin, valMax);

/**
 | Now, wait for gadget hits and perform the appropriate action.
 | Exit when either OK or CANCEL or QUIT has been hit, or the integer
 | string gadget deselected (i.e. the user has hit <CR>).
**/

  FOREVER {
    (void) Wait(1L << Wind->UserPort->mp_SigBit);
    while ((pIM = (struct IntuiMessage *)
            GetMsg(Wind->UserPort)) != NULL) {
      MClass = pIM->Class;
      pG = (struct Gadget *) pIM->IAddress;
      ReplyMsg((struct Message *) pIM);

      switch (MClass) {
        case GADGETUP:

          switch (pG->GadgetID) {
            case AW_CANCEL:
              return CANCEL;
            case AW_QUIT:
              return QUIT;
            case AW_OK:
            case AW_INT:
              if (intInfo.LongInt < valMin  ||  intInfo.LongInt > valMax) {
                (void) AutoRequest(Wind, art0, NULL, &arOkText,
                                   0, 0, AR_WIDTH, AR_HEIGHT);
              } else {
                *pVal = intInfo.LongInt;
                return OK_FOLKS;
              }
          }
      }
    }
  }
}

static void MakeWindow(
  struct Gadget *pG               /* First gadget */
){

/**
 | Both Ask() and AskInt() use the same window for their requesters.
 | This routine first checks if the window exists, and if not opens it;
 | if the window exists, checks if the appropriate gadget chain for
 | Ask() or for AskInt() is displayed: if so no action is required, if
 | not MakeWindow() removes the existing gadgets then inserts the new
 | ones. RefreshGadgets() is called from Ask()/AskInt() after clearing
 | the requester window calling RectFill().
**/

  if (!title[0]) {
    sprintf(title, "\"PF2\" v%s - MLO %s ", VERSION, LAST_CHANGE);
  }

  if (Wind == NULL) {
    aw_ytitle          = ScreenFontHeight + AW_YTITLE;
    aw_ycycle          = ScreenFontHeight + AW_YCYCLE;
    NWind.Height       = ScreenFontHeight + AW_HEIGHT;
    askGad[0].TopEdge  = ScreenFontHeight + AW_YNGAD;
    askGad[1].TopEdge  = ScreenFontHeight + AW_YNGAD;
    askGad[2].TopEdge  = ScreenFontHeight + AW_YNGAD;
    intGadget.TopEdge  = ScreenFontHeight + AW_YCGAD;
    backGadget.TopEdge = ScreenFontHeight + AW_YCGAD;
    forwGadget.TopEdge = ScreenFontHeight + AW_YCGAD;
    NWind.FirstGadget  = pG;

    if ((Wind = OpenWindow(&NWind)) == NULL)    Cleanup(SYS_ABORT_CODE);
    realWidth  = Wind->Width  - Wind->BorderRight;
    realHeight = Wind->Height - Wind->BorderBottom;
    SetFont(Wind->RPort, Topaz8);
  } else {
    if (Wind->FirstGadget != pG) {
      (void) RemoveGList(Wind, Wind->FirstGadget, -1);
      (void) AddGList(Wind, pG, 0, -1, NULL);
    }
  }
}
