/*  031  08-Jun-87  ovprompt.c

        Copyright (c) 1987 by Blue Sky Software.  All rights reserved.

        Note, some of these routines use putchr() and putstr() instead of
        disp_*() because on an IBM PC type system, putchr() will update
        the hardware cursor position while the disp_*() routines MAY not.
        The put*() routines can be #defined to be disp_*() routines if the
        disp_*() routines update the hardware cursor position.
*/

#include <ctype.h>
#include <setjmp.h>
#include "ov.h"
#include "overr.h"
#include "dialog.h"
#include "direct.h"
#include "strmem.h"

extern int brkhit;
extern int errno;
extern int sys_nerr;
extern char *sys_errlist[];

static char reply[MAX_REPLY+1];        /* buffer for user replys */

#define READ_HISTORY (10+1)            /* keep last 10 read strings */
static int rdhidx = 0;                 /* next avail read history index */
static char *rdhstr[READ_HISTORY];     /* pointers to read history strings */

static char *esc2quit = "Press ESC to quit";
static char *esc2cont = "Press ESC to continue";

extern jmp_buf back_to_main;

#ifdef LINT_ARGS
char *catnstr(char *, char *, int);
void ALTCALL switch_to(char *, char *, int);
void ALTCALL setupbox(D_BOX *, D_FLD *, char *, char *, char *, int, int);
#else
char *catnstr();
void ALTCALL switch_to();
void ALTCALL setupbox();
#endif


/******************************************************************************
                             P R O M P T
 *****************************************************************************/


char * ALTCALL
prompt(t,s,ps,ip,rlen) /* prompt user, and read string reply */
char *t, *s;           /* prompt box title, prompt string */
char *ps;              /* initial prompt string */
int ip;                /* initial position in initial string */
int rlen;              /* length of reply allowed */
{
   D_BOX reply_box;
   D_FLD reply_fld;

   /* initialize some reply field values */

   reply_fld.len = rlen;
   reply_fld.pos = ip;
   reply_fld.value = reply;

   /* create the dialog box and finish the reply field setup */

   setupbox(&reply_box,&reply_fld,t,s,esc2quit,FIRST_NROW+4,0);

   setvattrib(DIS_HIBOX);              /* use highlighted box attribute */

   if (ps)                             /* copy initial value to result */
      strcpy(reply,ps);
   else                                /* or else clear reply area */
      *reply = '\0';

   dbx_rdfld(&reply_box,&reply_fld);   /* read the users reply */

   setvattrib(DIS_NORM);               /* restore default video attrib */

   dbx_close(&reply_box);              /* remove the dialog box */

   return(reply);                      /* return the string */
}


/*****************************************************************************
                               A S K / A S K R C
 *****************************************************************************/

ask(s)         /* prompt user, and read single char reply */
char *s;
{
   D_BOX ask_box;
   D_FLD ask_fld;

   ask_fld.len = 1;                    /* setupbox() needs this */

   /* make a dialog box the way we want it */

   setupbox(&ask_box,&ask_fld,NULL,s,NULL,FIRST_NROW+4,0);

   return(askcomm(&ask_box,&ask_fld));
}

askrc(s,r,c)   /* prompt user, and read single char reply from given location */
char *s;
int r,c;
{
   D_BOX ask_box;
   D_FLD ask_fld;

   ask_fld.len = 1;                    /* setupbox() needs this */

   /* make a dialog box the way we want it */

   setupbox(&ask_box,&ask_fld,NULL,s,NULL,r,c);

   return(askcomm(&ask_box,&ask_fld));
}


askcomm(dbp,dfp)       /* common ask(), askrc() code */
register D_BOX *dbp;
register D_FLD *dfp;
{
   int ch;

   setvattrib(DIS_HIBOX);              /* use proper video attrib */

   dbx_disp(dbp," ",dfp->row,dfp->col);        /* display spacer */
   dbx_goto(dbp,dfp->row,dfp->col);            /*   & stay there */

   showcursor();                       /* so user knows where he is */
   ch = getchr();                      /* get a single character */
   hidecursor();                       /* don't need this anymore */

   setvattrib(DIS_NORM);               /* restore default video attrib */

   dbx_close(dbp);                     /* remove the dialog box */

   return(ch);                         /* and tell caller what was read */
}


/*****************************************************************************
                              B R K O U T
 *****************************************************************************/

brkout() {     /* see if user wants to break out of some operation */

   register int ch;

   /* The user can break out of some operations by signaling an interrupt -
      we consider a ^C, ^U (for old WordStar users), or ESC to be an
      interrupt signal */

   if ((ch = peekchr()) == ('C' - 0x40) || ch == ('U' - 0x40) || ch == 27) {
         ch = getchr();                /* flush char from buffer */
         brkhit = 1;                   /* use code below to interrupt */
   }

   /* The brkhit flag is set above if certain keys hit, it may also be set
      by the int 23h trap (^C) which is another way to interrupt */

   if (brkhit) {
      brkhit = 0;
      ch = ask("Interrupt? (Y/n): ");
      if (yes(ch))
         return(1);            /* yes, break out (interrupt) */
   }

   return(0);                  /* doesn't want to interrupt */
}


/*****************************************************************************
                             R E A D _ S T R
 *****************************************************************************/

char * ALTCALL
read_str(rlen,initial,offset)  /* read a string of length rlen */
int rlen, offset;
char *initial;
{
   register int ch;
   register char *cp;
   char *defval, *endp;
   int insert = FALSE, rhi, lastrhi;

   defval = initial ? initial : "";    /* set the initial (default) value */
   strcpyfill(reply,defval,rlen,' ');  /*   into the reply buffer         */
   *(endp = reply + rlen)  = '\0';     /* terminate the reply             */

   /* make this default value the last entry in the read str history so we
      can get (back) to it with down arrow */

   if (rdhidx == READ_HISTORY) {                       /* history full?    */
      free(rdhstr[0]);                                 /* yes, free oldest */
      for (rhi = 0; rhi < READ_HISTORY - 1; rhi++)     /* shift rest down  */
         rdhstr[rhi] = rdhstr[rhi+1];
   } else
      rdhidx++;                                        /* no, use next one */

   rdhstr[rhi = lastrhi = rdhidx-1] = Strdup(defval);  /* add default val  */

   showcursor();                       /* let user see where the cursor is */

   putstr_nomove(reply);               /* display entire initial reply str */

   for (cp = reply; offset; --offset)  /* skip to callers offset position  */
      putchr(*cp++);

   while ((ch = getchr()) != '\r' && ch != ESC_KEY) {

      switch (ch) {

         case UP:                      /* up arrow-previous read str */
            if (rhi > 0) {
               switch_to(rdhstr[--rhi],cp,rlen);
               cp = reply;
            } else
               tone(100,5);
            break;

         case DOWN:                    /* down arrow-next read str */
            if (rhi < lastrhi) {
               switch_to(rdhstr[++rhi],cp,rlen);
               cp = reply;
            } else
               tone(100,5);
            break;

         case DEL:                     /* delete current char key? */
            if (cp < endp) {
               strncpy(cp,cp+1,rlen-(cp-reply)-1);
               *(endp-1) = ' ';
               putstr_nomove(cp);
            } else
               tone(100,5);
            break;

         case RUBOUT:                  /* rubout? */
            if (cp > reply) {
               strncpy(cp-1,cp,rlen-(cp-reply));
               *(endp-1) = ' ';
               putchr('\b');
               putstr_nomove(--cp);
            } else
               tone(100,5);
            break;

         case LEFT:                    /* left arrow? */
            if (cp > reply) {
               --cp;
               putchr('\b');
            } else
               tone(100,5);
            break;

         case RIGHT:                   /* right arrow? */
            if (cp < endp)
               putchr(*cp++);
            else
               tone(100,5);
            break;

         case INS:                     /* insert mode key? */
            insert ^= 1;
            break;

         default:

            if (ch < ' ' || ch > 0x7f) {       /* don't enter weird chars */
               tone(100,5);
               break;
            }

            if (cp < endp) {                   /* add char unless at end */

               if (insert) {
                  reply[rlen-1] = '\0';
                  memcpy(cp+1,cp,rlen-(cp-reply)-1);   /* watches for overlap */
               }

               *cp++ = ch;
               putchr(ch);

               if (insert)
                  putstr_nomove(cp);

            } else
               tone(100,5);
            break;
         }

   }

   hidecursor();                       /* done, get rid of the cursor */

   /* if the user didn't escape out, remove trailing blanks from string and
      pass it back to caller.  If he/she did escape, return a null string. */

   if (ch != ESC_KEY) {

      for (cp = reply + rlen - 1; cp >= reply && *cp == ' '; )
         *cp-- = '\0';

      free(rdhstr[lastrhi]);           /* maintain history of strings read */
      rdhstr[lastrhi] = Strdup(reply); /* free defval & assign final reply */

   } else {

      *reply = '\0';                   /* return a null string */
      rdhidx = lastrhi;                /* don't save empty strings in history */
      free(rdhstr[lastrhi]);           /* don't keep the defval string        */
   }


   return(reply);
}

static void ALTCALL
switch_to(str,cp,rlen) /* switch to str as the current string being read */
char *str;
register char *cp;
int rlen;
{
   strcpyfill(reply,str,rlen,' ');     /* move str into the reply buffer */
   while (cp-- > reply)                /* backup cursor to start of str  */
      putchr('\b');
   putstr_nomove(reply);               /* display new read_str value     */
}


/*****************************************************************************
                          S H O W _ E R R O R
 *****************************************************************************/

show_error(options,ljmp,count,m1)     /* show user an error msg */
int options, ljmp, count;
char *m1;
{
   int len, l2;
   D_BOX error_box;
   char **mp, fullmsg[SCREEN_COLS-4+1];

   /* create one string from callers strings */

   mp = &m1;                           /* variable # arguments, point to 1st */
   len = 0;                            /* clear full msg area */
   *fullmsg = '\0';

   /* concat each message to fullmsg */

   for ( ; count; count--, mp++)
      catnstr(fullmsg,*mp,sizeof(fullmsg)-1);

   /* if caller wants DOS msg, include that too */

   if ((options & SHOW_DOS) && errno <= sys_nerr)
      catnstr(fullmsg,sys_errlist[errno],sizeof(fullmsg)-1);

   /* display the error dialog box */

   setupbox(&error_box,NULL,NULL,fullmsg,esc2cont,FIRST_NROW+4,0);
   setvattrib(DIS_NORM);

   if (!(options & QUIET))             /* make sure user is awake */
      tone(200,6);

   while (getchr() != 27) ;            /* wait for an ESC */

   dbx_close(&error_box);              /* remove the dialog box */

   if (ljmp)                           /* longjmp back to main if a longjmp */
      longjmp(back_to_main,ljmp);      /*   code was given */
}


/*****************************************************************************
                             S E T U P B O X
 *****************************************************************************/

static void ALTCALL
setupbox(dbp,dfp,title,msg,lastmsg,row,col)       /* make a dialog box */
register D_BOX *dbp;
D_FLD *dfp;
int row, col;
char *msg, *title, *lastmsg;
{
   register int i;
   int lm, ll, rlen, rl = 0;

   rlen = dfp ? dfp->len : 0;          /* set reply len if reply fld needed */

   dbp->row = row;                     /* set starting row # */
   dbp->title = title;                 /* set title string   */

   /* determine the number of rows/cols and start column for the dialog box */

   dbp->nrows = 3;
   if ((i = rlen + (lm = strlen(msg))) > SCREEN_COLS - 4) {
      dbp->nrows += 2;
      i = rl = lm > rlen ? lm : rlen;  /* when rl is NZ, reply on sep line */
   }

   if (lastmsg && (ll = strlen(lastmsg))) {    /* is a last line msg given? */
      dbp->nrows += 2;
      i = ll > i ? ll : i;
   }

   dbp->ncols = i + 2;                         /* box must be this wide   */

   /* use starting column that caller gave or center it on screen */

   dbp->col = col ? col : (SCREEN_COLS - dbp->ncols) >> 1;

   setvattrib(DIS_BOX);                /* use the BOX video attribute */
   dbx_open(dbp,DBX_SAVE);             /* open the dialog box */

   dbx_disp(dbp,msg,1,1);              /* display prompt string */
   if (lastmsg)                        /* display last line msg */
      dbx_disp(dbp,lastmsg,dbp->nrows-1,((dbp->ncols - ll) >> 1));

   if (dfp) {                          /* setup reply field offsets if any */
      dfp->row = rl ? 3 : 1;           /* if rl != 0, reply on diff line   */
      dfp->col = rl ? 1 : lm + 1;      /*   and start column               */
   }
}
