/*
 * Terminal independent keyboard handling. 
 */
#include	"no_macro.h"
#include	"no_metakey.h"
#include	"no_dprompt.h"
#include	"bsmap.h"
#ifndef		NO_METAKEY
#include	"metabit.h"
#endif

#include	"def.h"
#include	"kbd.h"
#include	"line.h"
#include	"buffer.h"
#include	"window.h"
#ifndef NO_MACRO
#include "macro.h"
#endif

#define EXTERN
#include	"key.h"


#ifndef	NO_METAKEY
int             use_metakey = TRUE;
/*
 * Toggle the value of use_metakey 
 */
do_meta(f, n)
{
	if (f & FFARG)
		use_metakey = n > 0;
	else
		use_metakey = !use_metakey;
	ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis");
	return TRUE;
}
#endif

#ifdef	BSMAP
static int      bs_map = BSMAP;
/*
 * Toggle backspace mapping 
 */
bsmap(f, n)
{
	if (f & FFARG)
		bs_map = n > 0;
	else
		bs_map = !bs_map;
	ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis");
	return TRUE;
}
#endif

#ifndef NO_DPROMPT
#define PROMPTL 80
char            prompt[PROMPTL], *promptp;
#endif

static int      pushed = FALSE;
static int      pushedc;

VOID
ungetkey(c)
	int             c;
{
#ifndef	NO_METAKEY
	if (use_metakey && pushed && c == CCHR('['))
		pushedc |= METABIT;
	else
#endif
		pushedc = c;
	pushed = TRUE;
}

int
getkey(flags)
	int             flags;
{
	int             c;
	char           *keyname();
#ifndef	NO_MACRO
	extern struct macro kbdmacro;
#endif

#ifndef NO_DPROMPT
	if ((flags & PROMPT) && !pushed && !inmacro) {
		if (prompt[0] != '\0' && ttwait()) {
			ewprintf("%s", prompt);	/* avoid problems with % */
			update();	/* put the cursor back	 */
			epresf = ERASE;
		}
		if (promptp > prompt)
			*(promptp - 1) = ' ';
	}
#endif
	if (pushed) {
		c = pushedc;
		pushed = FALSE;
#ifndef	NO_MACRO
	} else if (inmacro) {
		if (inmacro->m_cur - inmacro->m_text >= inmacro->m_count)
			return CCHR('G');
		else {
			c = *(inmacro->m_cur++);
			if (!(flags & CRFLAGS))
				c = (c == SOFTCR ? CCHR('M') : c);
			else if (flags & DISSCR) {
				if (c == SOFTCR)
					return getkey(flags);
				else if (*inmacro->m_cur == SOFTCR)
					inmacro->m_cur++;
			}	/* else flags & SEESCR, so we pass it as is */
		}
#endif
	} else
		c = getkbd();
#ifndef	NO_METAKEY
	if (use_metakey && (c & METABIT)) {
		pushedc = c & ~METABIT;
		pushed = TRUE;
		c = CCHR('[');
	}
#endif
#ifndef	NO_MACRO
	if (macrodef && !inmacro)
		add_key_to_macro((KCHAR) c, &kbdmacro);
#endif
#ifdef 	BSMAP
	if (bs_map)
		if (c == CCHR('H'))
			c = CCHR('?');
		else if (c == CCHR('?'))
			c = CCHR('H');
#endif
#ifndef NO_DPROMPT
	if ((flags & PROMPT) && promptp < &prompt[PROMPTL - 5]) {
		promptp = keyname(promptp, c);
		*promptp++ = '-';
		*promptp = '\0';
	}
#endif
	return c;
}

/*
 * doscan scans a keymap for a keyboard character and returns a pointer to
 * the function associated with that character.  Sets ele to the keymap
 * element the keyboard was found in as a side effect. 
 */

struct map_element *ele;

struct function
               *
doscan(map, c)
	register struct keymap *map;
	register int    c;
{
	register struct map_element *elec = &map->map_element[0];	/* local register copy
									 * for faster access */
	register struct map_element *last = &map->map_element[map->map_num];

	while (elec < last && c > elec->k_num)
		elec++;
	ele = elec;		/* used by prefix and binding code	 */
	if (elec >= last || c < elec->k_base)
		return map->map_default;
	return &elec->k_entry[c - elec->k_base];
}

doin()
{
	struct keymap  *curmap;
	struct function *funct;

#ifndef NO_DPROMPT
	*(promptp = prompt) = '\0';
#endif
	curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
	key.k_count = 0;

	while ((funct =
		doscan(curmap, (key.k_chars[key.k_count++] = getkey(PROMPT))))->f_type
	       == F_PREFIX)
		curmap = (struct keymap *) funct->f_pref;
#ifndef	NO_MACRO
	if (funct->f_type == F_MACRO)
		return runmacro((struct macro *) funct->f_macro, 0, 1, FALSE);
	else
#endif
		return (*(funct->f_funcp)) (0, 1);
}

rescan(f, n)
	int             f, n;
{
	int             c;
	register struct keymap *curmap;
	int             i;
	struct function *fp;
	int             mode = curbp->b_nmodes;

	for (;;) {
		if (ISUPPER(key.k_chars[key.k_count - 1])) {
			c = TOLOWER(key.k_chars[key.k_count - 1]);
			curmap = curbp->b_modes[mode]->p_map;
			for (i = 0; i < key.k_count - 1; i++) {
				if ((fp = doscan(curmap, (key.k_chars[i])))->f_type != F_PREFIX)
					break;
				curmap = (struct keymap *) fp->f_pref;
			}
			if (fp->f_type == F_PREFIX) {
				if ((fp = doscan(curmap, c))->f_type == F_PREFIX)
					while ((fp = doscan(curmap, key.k_chars[key.k_count++] =
					getkey(PROMPT)))->f_type == F_PREFIX)
						curmap = (struct keymap *) fp->f_pref;
#ifndef	NO_MACRO
				if (fp->f_type == F_MACRO)
					return runmacro((struct macro *) fp->f_macro, f, n, FALSE);
				else
#endif
				if (fp->f_funcp != rescan)
					return (*(fp->f_funcp)) (f, n);
			}
		}
		/* try previous mode */
		if (--mode < 0)
			return ABORT;
		curmap = curbp->b_modes[mode]->p_map;
		for (i = 0; i < key.k_count; i++) {
			if ((fp = doscan(curmap, (key.k_chars[i])))->f_type != F_PREFIX)
				break;
			curmap = (struct keymap *) fp->f_pref;
		}
		if (fp->f_type == F_PREFIX) {
			while ((fp = doscan(curmap, key.k_chars[i++] = getkey(PROMPT)))->f_type
			       == F_PREFIX)
				curmap = (struct keymap *) fp->f_pref;
			key.k_count = i;
		}
		if (i >= key.k_count - 1)
#ifndef	NO_MACRO
			if (fp->f_type == F_MACRO)
				return runmacro((struct macro *) fp->f_macro, f, n, FALSE);
			else
#endif
			if (fp->f_funcp != rescan)
				return (*(fp->f_funcp)) (f, n);
	}
}

universal_argument(f, n)
	int             f, n;
{
	int             c, nn = 4;
	struct keymap  *curmap;
	struct function *funct;

	if (f & FFUNIV)
		nn *= n;
	for (;;) {
		key.k_chars[0] = c = getkey(PROMPT);
		key.k_count = 1;
		if (c == '-')
			return negative_argument(f, 4);
		if (c >= '0' && c <= '9')
			return digit_argument(f, nn);
		curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
		while ((funct = doscan(curmap, c))->f_type == F_PREFIX) {
			curmap = (struct keymap *) funct->f_pref;
			key.k_chars[key.k_count++] = c = getkey(PROMPT);
		}
#ifndef	NO_MACRO
		if (funct->f_type == F_MACRO)
			return runmacro((struct macro *) funct->f_macro, FFUNIV, nn, FALSE);
		else
#endif
		if (funct->f_funcp != universal_argument)
			return (*(funct->f_funcp)) (FFUNIV, nn);
		nn *= 4;
	}
}

/* ARGSUSED */
digit_argument(f, n)
	int             f, n;
{
	int             nn, c;
	struct keymap  *curmap;
	struct function *funct;

	nn = key.k_chars[key.k_count - 1] - '0';
	for (;;) {
		c = getkey(PROMPT);
		if (c < '0' || c > '9')
			break;
		nn *= 10;
		nn += c - '0';
	}
	key.k_chars[0] = c;
	key.k_count = 1;
	curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
	while ((funct = doscan(curmap, c))->f_type == F_PREFIX) {
		curmap = (struct keymap *) funct->f_pref;
		key.k_chars[key.k_count++] = c = getkey(PROMPT);
	}
#ifndef	NO_MACRO
	if (funct->f_type == F_MACRO)
		return runmacro((struct macro *) funct->f_macro, FFOTHARG, nn, FALSE);
	else
#endif
		return (*(funct->f_funcp)) (FFOTHARG, nn);
}

negative_argument(f, n)
	int             f, n;
{
	int             nn = 0, c;
	struct keymap  *curmap;
	struct function *funct;

	for (;;) {
		c = getkey(PROMPT);
		if (c < '0' || c > '9')
			break;
		nn *= 10;
		nn += c - '0';
	}
	if (nn)
		nn = -nn;
	else
		nn = -n;
	key.k_chars[0] = c;
	key.k_count = 1;
	curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
	while ((funct = doscan(curmap, c))->f_type == F_PREFIX) {
		curmap = (struct keymap *) funct->f_pref;
		key.k_chars[key.k_count++] = c = getkey(PROMPT);
	}
#ifndef	NO_MACRO
	if (funct->f_type == F_MACRO)
		return runmacro((struct macro *) funct->f_macro, FFOTHARG, nn, FALSE);
	else
#endif
		return (*(funct->f_funcp)) (FFNEGARG, nn);
}

/*
 * Insert a character. 
 */

selfinsert(f, n)
	int             f, n;
{
	register int    c;
	int             count;
	VOID            lchange();

	if (n < 0)
		return FALSE;
	if (n == 0)
		return TRUE;
	c = key.k_chars[key.k_count - 1];
	if (c == '\n') {
		do {
			count = lnewline();
		} while (--n && count == TRUE);
		return count;
	}
	if (curbp->b_flag & BFOVERWRITE) {	/* Overwrite mode	 */
		lchange(WFEDIT);
		while (curwp->w_doto < llength(curwp->w_dotp) && n--)
			lputc(curwp->w_dotp, curwp->w_doto++, c);
		if (n <= 0)
			return TRUE;
	}
	return linsert(n, c);
}

/*
 * this could be implemented as a keymap with everthing defined as
 * self-insert. (well, not quite - it needs to deal with ^Q###). 
 */
quote(f, n)
{
	register int    c;

#ifndef	NO_METAKEY
	int             save_metakey = use_metakey;

	use_metakey = FALSE;
#endif
	key.k_count = 1;
	if ((key.k_chars[0] = getkey(PROMPT | DISSCR)) >= '0' && key.k_chars[0] <= '7') {
		key.k_chars[0] -= '0';
		if ((c = getkey(PROMPT | DISSCR)) >= '0' && c <= '7') {
			key.k_chars[0] *= 8;
			key.k_chars[0] += c - '0';
			if ((c = getkey(PROMPT | DISSCR)) >= '0' && c <= '7') {
				key.k_chars[0] *= 8;
				key.k_chars[0] += c - '0';
			} else
				ungetkey(c);
		} else
			ungetkey(c);
	}
#ifndef	NO_METAKEY
	use_metakey = save_metakey;
#endif
	return selfinsert(f, n);
}
