/*
**
** EMACS's kernel processes two distinct forms of characters.
** One of these is a standard unsigned character which is
** used in the edited text.  The other form, called an EMACS
** Extended Character is a 2 byte value which contains both
** an ascii value, and flags for certain prefixes/events.
**
** Bit   Usage
** ---   -----
** 0 -> 7   Standard 8 bit ascii character
** 8  Control key flag
** 9  META prefix flag
** 10 ^X prefix flag
** 11 Function key flag
** 12 Mouse prefix
** 13 Shifted flag (not needed on alpha shifted characters)
** 14 Alterate prefix (ALT key on PCs)
**
** The machine dependent driver is responsible for returning
** a byte stream from the various input devices with various
** prefixes/events embedded as escape codes.  Zero is used as the
** value indicating an escape sequence is next.  The format of
** an escape sequence is as follows:
**
** 0     Escape indicator
** <prefix byte>  upper byte of extended character
** {<col><row>}   col, row position if the prefix byte
**       indicated a mouse event
** <event code>   value of event
**
** Two successive zeroes are used to indicate an actual
** NULL being input.  These values are then interpreted by
** getkey() to construct the proper extended character
** sequences to pass to the kernel.
*/

#include "Include.h"

int cpending;        /* input character pending?   */
int charpending;     /* character pushed back   */

// internally used

STD ectoc(int);

//

/// mkprompt()
static void mkprompt(char *buf, char *prompt, char *yes, char *no, char *retry)
{
   strcpy(buf, prompt);
   strcat(buf, " (");
   strcat(buf, yes);
   strcat(buf, "/");
   strcat(buf, no);
   if (retry) {
       strcat(buf, "/");
       strcat(buf, retry);
   }
   strcat(buf, ")? ");
}
//-

/// mlyesno
/*
** Ask a yes or no question in the message line. Return either TRUE, FALSE, or
** ABORT. The ABORT status is returned if the user bumps out of the question
** with a ^G. Used any time a confirmation is required.
 ***********************************************************************/
STD mlyesno(char *prompt)
{
      // simplified
   return xenoyesno(prompt, 0, 'q', "quit");
}
//-

/// xenoyesno
REG xenoyesno(char *prompt, int dflt, int quit, char *abortprompt)
{
   int  c;        /* input character */
   char buf[NPAT];      /* prompt to user */
   char junk;

   for (;;) {
      /* build and prompt the user */
      if (dflt == *xl_yes) {
         mkprompt(buf, prompt, xl_YES, xl_no, abortprompt);
      } else {
         mkprompt(buf, prompt, xl_yes, xl_NO, abortprompt);
      }
      mlwrite(buf);

      /* get the response */
           c = getkey();

      /* XENO_ EMACS allows default */
      if (dflt && c == (CTRL | 'M')) {
         c = dflt;
      }

      if (c == abortc) {   /* Bail out! */
         if (dflt)
            continue;   /* nope, not with XENO_ EMACS */
         else
            return ABORT;
      }

      // if quit flag is non-zero, it is the character
      // to function the same as abortc.  typically
      // 'q' for quit or 'r' for retry
      if (quit && c == quit) {
          return ABORT;
      }
      
      junk = c;
      lowercase(&junk);

           if  (junk == *xl_no)
                  return(FALSE);  /* ONLY 'y' or 'Y' allowed!!! */

      if (junk == *xl_yes)
         return(TRUE);

   }
}
//-

/// mlreply()
/*
** Write a prompt into the message line, then read back a response. Keep
** track of the physical position of the cursor. If we are in a keyboard
** macro throw the prompt away, and return the remembered response. This
** lets macros run at full speed. The reply is always terminated by a carriage
** return. Handle erase, kill, and abort keys.
 ***********************************************************************/
STD mlreply(char *prompt, char *buf, int nbuf)
{
   return(nextarg(prompt, buf, nbuf, ctoec((int) '\r')));
}
//-

/// mltreply
STD mltreply(char *prompt, char *buf, int nbuf, int eolchar)
{
   return(nextarg(prompt, buf, nbuf, eolchar));
}
//-

/// ctoec()
/* ctoec:   character to extended character
**     pull out the CTRL and SPEC prefixes (if possible)
***********************************************************/
STD ctoec(int c)
{
        if ((c>=0x00 && c<=0x1F) || c == 0x7F)
                c = CTRL | (c ^ 0x40);
        return(c);
}
//-

/// getcbuf()
/* getcbuf: get a completion from the user for a buffer name.
** I was goaded into this by lots of other people's completion code.
**
** char *prompt;     prompt to user on command line
** char *defval;     default value to display to user
** int createflag;   should this create a new buffer?
 *********************************************************************/
BUFFER *STD getcbuf(char *prompt, char *defval, int createflag)
{
   char *sp;   /* ptr to the returned string */

   sp = complete(prompt, defval, CMP_BUFFER, NBUFN, 0);
   if (sp == NULL)
      return(NULL);

   return(bfind(sp, createflag, 0));
}
//-

/// tgetc()
/* tgetc:   Get a key from the terminal driver, resolve any keyboard
**    macro action
************************************************************************/
int STD tgetc()
{
   int c;   /* fetched character */

   /* if we are playing a keyboard macro back, */
   if (kbdmode == PLAY) {

      /* if there is some left... */
      if (kbdptr < kbdend)
         return((int)*kbdptr++);

      /* at the end of last repitition? */
      if (--kbdrep < 1) {
         kbdmode = STOP;
         /* force a screen update after all is done */
         update(FALSE);
      } else {

         /* reset the macro to the begining for the next rep */
         kbdptr = &kbdm[0];
         return((int)*kbdptr++);
      }
   }

   /* if no pending character */
   if (cpending == FALSE) {

      /* fetch a character from the terminal driver */
      c = TTgetc();

   } else {
      
      c = charpending;
      cpending = FALSE;
   }

   /* record it for $lastkey */
   lastkey = c;

   /* save it if we need to */
   if (kbdmode == RECORD) {
      *kbdptr++ = c;
      kbdend = kbdptr;

      /* don't overrun the buffer */
      if (kbdptr == &kbdm[NKBDM - 1]) {
         kbdmode = STOP;
         TTbeep();
      }
   }

   /* and finally give the char back */
   return(c);
}
//-

/// getkey()
/* getkey:  Get one keystroke. The legal prefixs here
**          are the SPEC, MOUS and CTRL prefixes.
**
 ***********************************************************************/
STD getkey(void)
{
   int c;      /* next input character */
   int upper;  /* upper byte of the extended sequence */

   /* get a keystroke */
        c = tgetc();

   /* if it exists, process an escape sequence */
   if (c == 0) {

      /* get the event type */
      upper = tgetc();

      /* mouse events need us to read in the row/col */
      if (upper & (MOUS >> 8)) {
         /* grab the x/y position of the mouse */
         xpos = tgetc();
         ypos = tgetc();
      }

      /* get the event code */
      c = tgetc();

      /* if it is a function key... map it */
      c = (upper << 8) | c;
   }

   /* yank out the control prefix */
        if (((c & 255) >=0x00 && (c & 255) <= 0x1F) || (c & 255) == 0x7F)
                c = CTRL | (c ^ 0x40);

   /* return the character */
        return(c);
}
//-

/// getcmd()
/*
** GETCMD:  Get a command from the keyboard. Process all applicable
** prefix keys
 ***********************************************************************/
STD getcmd(void)
{
   int c;      /* fetched keystroke */
   KEYTAB *key;   /* ptr to a key entry */

   /* get initial character */
   c = getkey();
   key = getbind(c);

   /* resolve META and CTLX prefixes */
   if (key) {
      if (key->k_ptr.fp == meta) {
         c = getkey();
         c = upperc(c) | (c & ~255);   /* Force to upper */
         c |= META;
      } else if (key->k_ptr.fp == cex) {
         c = getkey();
         c = upperc(c) | (c & ~255);   /* Force to upper */
         c |= CTLX;
      }
   }
   /* return it */
   return(c);
}
//-

/// getstring()
/* A more generalized prompt/reply function allowing the caller
** to specify the proper terminator. If the terminator is not
** a return('\r'), return will echo as "<NL>"
**
** char *prompt;
** char *buf;
** int nbuf;
** int eolchar;
 ***********************************************************************/
STD getstring(char *prompt, char *buf, int nbuf, int eolchar)
{
   register int cpos;   /* current character position in string */
   register int c;      /* current input character */
   register int quotef; /* are we quoting the next char? */

   cpos = 0;
   quotef = FALSE;

   /* prompt the user for the input string */
   if (discmd)
      mlwrite(prompt);
   else
      movecursor(term.t_nrow, 0);

   for (;;) {
      /* get a character from the user */
      c = getkey();

      /* if they hit the line terminate, wrap it up */
      if (c == eolchar && quotef == FALSE) {
         buf[cpos++] = 0;

         /* clear the message line */
         mlerase();
         TTflush();

         /* if we default the buffer, return FALSE */
         if (buf[0] == 0)
            return(FALSE);

         return(TRUE);
      }

      /* change from command form back to character form */
      c = ectoc(c);

      if (c == ectoc(abortc) && quotef == FALSE) {
         /* Abort the input? */
         ctrlg(FALSE, 0);
         TTflush();
         return(ABORT);
      } else if ((c==0x7F || c==0x08) && quotef==FALSE) {
         /* rubout/erase */
         if (cpos != 0) {
            outstring("\b \b");
            --ttcol;

            if (buf[--cpos] < 0x20) {
               outstring("\b \b");
               --ttcol;
            }

            if (buf[cpos] == '\r') {
               outstring("\b\b  \b\b");
               ttcol -= 2;
            }
            TTflush();
         }

      } else if (c == 0x0b && quotef == FALSE) {

         /* C-K, kill default buffer and return null */
 
         /* clear the buffer */
         buf[0] = 0;
 
         /* clear the message line and return */
         mlwrite("");
         TTflush();
         return(TRUE);

      } else if (c == 0x15 && quotef == FALSE) {

         /* C-U, kill */
         while (cpos != 0) {
            outstring("\b \b");
            --ttcol;

            if (buf[--cpos] < 0x20) {
               outstring("\b \b");
               --ttcol;
            }
         }
         TTflush();

      } else if (c == ectoc(quotec) && quotef == FALSE) {
         quotef = TRUE;
      } else {
         quotef = FALSE;
         if (cpos < nbuf-1) {
            buf[cpos++] = c;

            if ((c < ' ') && (c != '\r')) {
               outstring("^");
               ++ttcol;
               c ^= 0x40;
            }

            if (c != '\r') {
               if (disinp)
                  mlout(c);
            } else { /* put out <NL> for <ret> */
               outstring("<NL>");
               ttcol += 3;
            }
            ++ttcol;
            TTflush();
         }
      }
   }
}
//-

/// outstring()
/*
** output a string of input characters
**
** char *s;    string to output
 ***********************************************************************/
STD outstring(char *s)
{
   if (disinp)
      while (*s)  mlout(*s++);

   return(1);
}
//-

/// complete
char *STD complete(char *prompt, char *defval, int type, int maxlen, int wrt)
/*
** char *prompt;     prompt to user on command line
** char *defval;     default value to display to user
** int type;         type of what we are completing
** int maxlen;       maximum length of input field
** int wrt;          XENO: file requester write flag
********************/
{
   static char buf[NSTRING];/* buffer to hold tentative name */
   char buf2[NSTRING];
   /* if we are executing a command line get the next arg and match it */
   if (clexec) {
      if (macarg(buf) != TRUE)
         return(NULL);
      return(buf);
   }

// show requester if on local port and requester is allowed (sysop)
   if (type == CMP_FILENAME && wrt >= 0) {
      int st = xenorequest(buf, prompt, wrt);
      if (st == TRUE) return buf;
      if (st == ABORT) return NULL;
   }

   // filename input is completely replaced by dlg stuff
   strcpy(buf2, prompt);
   strcat(buf2, ": ");
   if (getdlgstring(buf2, buf, "") == TRUE)
       return buf;
   else
       return NULL;
}
//-

// internally used functions

/// ectoc()
/* ectoc:   expanded character to character
&&          collapse the CTRL and SPEC flags back into an ascii code
 ***********************************************************************/
STD ectoc(int c)
{
   if (c & CTRL)
      c = c ^ (CTRL | 0x40);
   if (c & SPEC)
      c= c & 255;
   return(c);
}
//-


