/* LISTING 3  Prompt Function     */

#include <stdio.h>
#include <conio.h>
#include <ctype.h>
#include <dos.h>
#include <string.h>
#include "./keys.h"

#ifndef YES
#define YES   1
#endif

#ifndef NO
#define NO    0
#endif

#define M_INSERT        1
#define M_TYPEOVER      2

#define MOVE_CUR(row,col) printf("\x1B[%d;%df",row,col);
#define NORMAL  "\x1B[0m"  /* ANSI Normal  Video */
#define REVERSE "\x1B[7m"  /* ANSI Reverse Video */

/* Prototypes */
extern int  match  (int, char);
extern void change_cur (int, int);
extern void main (void);

int    key;

int prompt( data, match_ch, min_len, max_len, row, col, 
	    message, fyi_row, fyi_col)

char   data [];       /* Return Message              */
char   match_ch;      /* Code For Which Characters 
			 Are Allowed                 */
int    min_len;       /* Min Length Allowed          */
int    max_len;       /* Max Length Allowed          */
int    row;           /* Window Row                  */
int    col;           /* Window Column               */
char   *message;      /* Prompt Message              */
int    fyi_row;       /* FYI Window Row              */
int    fyi_col;       /* FYI Window Column           */
{
   char  buf [80];
   int   terminate;   /* Key That Caused Termination */
   int   index,i;     /* General Index Variables     */
   int   mode;        /* Mode: INSERT or TYPEOVER    */
   int   changed;     /* Changed Flag:  1=YES, 0=NO  */
   int   edit;        /* Edit Flag:     1=YES, 0=NO  */
   int   frst_key;    /* First Key Flag: 1=YES, 0=NO */
   char  more;        /* YES/NO Flag . . . 
			 Used To Terminate Input     */

   /* Initializations */
   edit     = NO;
   frst_key = NO;
   changed  = NO;

   mode = M_TYPEOVER;
   change_cur(0, 13); /* Set Cursor To Block Mode    */

   /* Display The FYI Message At The FYI Row, Colunn */
   if (strlen(message)) { 
      MOVE_CUR (fyi_row, fyi_col);
      printf(message);
   }

   printf(REVERSE);   /* Change To Reverse Video    */

   strcpy(buf, data); /* Make A Copy Of The Default 
			 Return Value               */

   for (index = strlen(buf); index < max_len; ++index) {
      buf [index] = ' ';     /* Fill Remainder Of Default 
				Value With Blanks  */
   }
   buf [index] = '\0';

   /* Display Default Value For Field */
   MOVE_CUR (row, col);  
   printf(buf);

   /* Position Cursor At Beginning Of Field */
   MOVE_CUR (row, col);  
   
   /* Get Input From User */
   for (index = 0,more = YES; more; ) {
       key = getch();
       if (key == 0)
	  key = getch()+256;

       switch(key) {
       case C_F1       : /* F1  Key        */
       case C_F2       : /* F2  Key        */
       case C_F3       : /* F3  Key        */
       case C_F4       : /* F4  Key        */
       case C_F5       : /* F4  Key        */
       case C_F6       : /* F6  Key        */
       case C_F7       : /* F7  Key        */
       case C_F8       : /* F8  Key        */
       case C_F9       : /* F9  Key        */
       case C_F10      : /* F0  Key        */
       case C_F11      : /* F11 Key        */
       case C_F12      : /* F12 Key        */
       case C_ESC      : /* ESCAPE Key     */
	    frst_key = NO;
	    terminate = key;
	    buf[index] = '\0';
	    more = NO;
	    break;

       case C_INS  : /* INSERT Key       */
	    frst_key = NO;
	    if (mode == M_INSERT) {
	       /* Set The Mode To TYPEOVER and
		  Change Cursor To Block Mode */
	       mode = M_TYPEOVER;
	       change_cur(0, 13); 
	    }
	    else {
	       /* Set The Mode To INSERT And
		  Change Cursor To Underline  */
	       mode = M_INSERT;
	       change_cur(12, 13);  
	    }
	    putchar(buf[index]);
	    MOVE_CUR(row, (col + index));
	    break;

       case C_BACK   : /* BACKSPACE Key  */
       case C_DEL    : /* DELETE Key     */
	    frst_key = NO;
	    changed  = YES;
	    if (index >= max_len)
	       index = max_len - 1;
	    for (i = index; i < max_len; i++) {
		if (buf[i+1] == '\0')
		   buf[i] = ' ';
		else
		   buf[i] = buf[i+1];
	    }

	    /* Redisplay The Field */
	    MOVE_CUR(row, col);
	    printf(buf);

	    /* Reposition The Cursor. */
	    MOVE_CUR(row, (col + index));
	    putchar(buf[index]);
	    MOVE_CUR(row, (col + index));

	    if (key == C_DEL)
	       break;

       case C_LEFT : /* LEFT ARROW      */
	    frst_key = NO;
	    if (index <= 0)
	       break;
	    if (index >= max_len) 
	       index = max_len -1;
	    index--;
	    MOVE_CUR(row, (col + index));
	    break;

       case C_RIGHT: /* RIGHT ARROW     */
	    frst_key = NO;
	    edit = YES;
	    if (index >= (max_len - 1))
	       break;
	    index++;
	    MOVE_CUR(row, (col + index));
	    break;

       case C_HOME : /* HOME KEY      */
       case C_END  : /* END  KEY      */
	    frst_key = NO;
	    if (edit == YES) {
	       if (key == C_END) 
		  index = end_of_fld(buf,(strlen(buf)));
	       else 
		  index = 0;
	       MOVE_CUR(row, (col + index));
	       break;
	    }

       case C_DOWN : /* DOWN ARROW      */
       case C_UP   : /* UP ARROW        */
       case C_PGDN : /* PAGE DOWN       */
       case C_PGUP : /* PAGE UP         */
       case C_CR   : /* CARRIAGE RETURN */
       case C_TAB  : /* TAB    Key      */

	    frst_key = NO;

	    /* Reset Mode and Edit Flag */
	    edit = NO;
	    mode = M_INSERT;

	    if (strlen(data) >= (unsigned)min_len && 
		index == 0 && changed == NO) {
	       /* If The User Has Not Changed The Value Of
		  The Data Field, Return The Appropriate
		  NO CHANGE Key For The terminating Key
		  That Was Pressed. */

	       if (key == C_CR) {
		  /* Return No Change For Carriage Return */
		  terminate = CR_NO_CHG;
	       }
	       if (key == C_UP) {
		  /* Return No Change For Up Arrow   */
		  terminate = UP_NO_CHG;
	       }
	       if (key == C_DOWN) {
		  /* Return No Change For Down Arrow */
		  terminate = DN_NO_CHG;
	       }
	       if (key == C_TAB) {
		  /* Return No Change For Tab Key    */
		  terminate = TB_NO_CHG;
	       }
	       if (key != C_CR   &&
		   key != C_UP   &&
		   key != C_DOWN &&
		   key != C_TAB)
		  terminate = key;

	       strcpy(buf, data);

	       more = NO;
	       break;
	    }

	    /* If Minimum Length Requirement Has been Met,
	       Quit */
	    if (index >= min_len) {
	       terminate = key;
	       more = NO;
	       break;
	    }

	    /* Else Ignore */
	    break;

       default     :
	    if (index == max_len)
	       /* At End Of Data Field - Do Nothing */
	       break;

	    /* We Have Dealt With All Of The Keys That We
	       Needed Above, So Now We Will Filter Out All
	       Keys And Characters Above 122 (z) Here. */

	    if (key > 'z')              /* Ignore Key */
	       break;

	    if (match(index, match_ch) == YES) {
	       /* Only Accept The User Entered Character
		  If It Successfully Passed The match
		  function. */
	       edit    = YES;
	       changed = YES;

	       if (frst_key == YES) {
		  /* If This Is The First Valid Key That
		     The User Has Entered, Then Wipe Out
		     The Field. */
		  for (i = 0; i < max_len; i++) 
		      buf[i] = ' ';
		  frst_key = NO;
	       }

	       if (mode == M_INSERT) {
		  /* Insert The User Entered Character
		     Into The Field */
		  for (i = (max_len - 1); i > index; i--) 
		      buf[i] = buf[i-1];
	       }

	       /* Overwrite The Buffer With The User Entered
		  Character */
	       buf[index] = (char)key;

	       /* Redisplay The Field */
	       MOVE_CUR(row, col);
	       printf(buf);

	       /* Reposition The Cursor. */
	       index++;
	       if (index >= max_len) 
		  index --;
	       MOVE_CUR(row, (col + index));
	       putchar(buf[index]);
	       MOVE_CUR(row, (col + index));
	    }
	    break;
       }
   }
   /* Redraw The Field In The Window's Normal
      Color Attribute */
   printf(NORMAL);
   MOVE_CUR (row, col);
   printf(buf);

   strcpy(data, buf);
   data[(end_of_fld(data,(strlen(data))) + 1)] = '\0';
   
   /* Change Cursor Back To Underline */
   change_cur(12, 13);
   return (terminate);
}

static int match(index,match_ch)
int  index;
char match_ch;
{
   int matches = YES;

   switch (match_ch) {
   case '$' : /* 0-9 or ,$. */
	if (!isdigit(key) && !strchr(".,$",key))
	   matches = NO;
	break;

   case '#' : /* 0 - 9 */
	if (!isdigit(key))
	   matches = NO;
	break;

   case 'A' : /* A - Z */
	if (!isalpha(key) && !strchr(" ",key))
	   matches = NO;
	break;

   case 'D' : /* Date: 0-9 or - or / or .*/
	if (!isdigit(key) && !strchr(".-*/",key))
	   matches = NO;
	break;

   case 'l' : /* a-z, A-Z, 0-9, or ;:.,/?*-$#()'! or
		 leading space */
	if (!isalnum(key) &&
	    !strchr(" !@#$%^&*()-_=+[{]}';:.,/?",key))
	   matches = NO;
	break;

   case 'Q' : /* YyNn as Reply To Yes/No Question */
	if (!strchr("YyNn",key))
	   matches = NO;
	key = toupper(key);
	break;

   case 'S' : /* 0-9 or + or - */
	if (!isdigit(key) && !strchr("+-",key))
	   matches = NO;
	break;

   case 'T' : /* Time: 0-9 or . or : */
	if (!isdigit(key) && !strchr(".:",key))
	   matches = NO;
	break;

   case 'X' : /* MmFf As Reply To Male/Female */
	if (!strchr("MmFf",key))
	   matches = NO;
	key = toupper(key);
	break;

   default :
	matches = NO;
	break;
   }
   return (matches);
}

/* This Function Will Return A 0 Based Index Of The
   First Non-Blank Character At The End Of A String. */
int end_of_fld(string, length)
char *string;
int  length;
{
   int  i;

   for (i = length - 1; i >= 0; i--) {
       if (string[i] != ' ' && string[i] != '\0')
	  return(i);
   }
   return(0);
}

/* This Function Will Change The Appearance Of The 
   Cursor To A Block Or An Underscore */
void change_cur(start, end)
int start;
int end;
{
   union REGS r;

   r.h.ah = 1;
   r.h.ch = start;
   r.h.cl = end;
   int86(0x10, &r, &r);
}

