/*
 *	Key-map handler
 *	Copyright
 *		(C) 1992 Joseph H. Allen
 *
 *	This file is part of JOE (Joe's Own Editor)
 */
#include "config.h"
#include "types.h"

#include <string.h>

#include "macro.h"
#include "termcap.h"
#include "utils.h"
#include "vs.h"

/* Create a KBD */

KBD *mkkbd(KMAP *kmap)
{
	KBD *kbd = (KBD *) joe_malloc(sizeof(KBD));

	kbd->topmap = kmap;
	kbd->curmap = kmap;
	kbd->x = 0;
	return kbd;
}

/* Eliminate a KBD */

void rmkbd(KBD *k)
{
	joe_free(k);
}

/* Process next key for KBD */

void *dokey(KBD *kbd, int n)
{
	void *bind = NULL;

	/* If we were passed a negative character */
	if (n < 0)
		n += 256;

	/* If we're starting from scratch, clear the keymap sequence buffer */
	if (kbd->curmap == kbd->topmap)
		kbd->x = 0;

	if (kbd->curmap->keys[n].k == 1) {	/* A prefix key was found */
		kbd->seq[kbd->x++] = n;
		kbd->curmap = kbd->curmap->keys[n].value.submap;
	} else {		/* A complete key sequence was entered or an unbound key was found */
		bind = kbd->curmap->keys[n].value.bind;
/*  kbd->seq[kbd->x++]=n; */
		kbd->x = 0;
		kbd->curmap = kbd->topmap;
	}
	return bind;
}

/* Return key code for key name or -1 for syntax error */

static int keyval(char *s)
{
	if (s[0] == '^' && s[1] && !s[2])
		if (s[1] == '?')
			return 127;
		else
			return s[1] & 0x1F;
	else if ((s[0] == 'S' || s[0] == 's')
		 && (s[1] == 'P' || s[1] == 'p') && !s[2])
		return ' ';
	else if (s[1] || !s[0])
		return -1;
	else
		return (unsigned char) s[0];
}

/* Create an empty keymap */

KMAP *mkkmap(void)
{
	KMAP *kmap = (KMAP *) joe_calloc(sizeof(KMAP), 1);

	return kmap;
}

/* Eliminate a keymap */

void rmkmap(KMAP *kmap)
{
	int x;

	if (!kmap)
		return;
	for (x = 0; x != KEYS; ++x)
		if (kmap->keys[x].k == 1)
			rmkmap(kmap->keys[x].value.submap);
	joe_free(kmap);
}

/* Parse a range */

static char *range(char *seq, int *vv, int *ww)
{
	char c;
	int x, v, w;

	for (x = 0; seq[x] && seq[x] != ' '; ++x) ;	/* Skip to a space */
	c = seq[x];
	seq[x] = 0;		/* Zero terminate the string */
	v = keyval(seq);	/* Get key */
	w = v;
	if (w < 0)
		return NULL;
	seq[x] = c;		/* Restore the space or 0 */
	for (seq += x; *seq == ' '; ++seq) ;	/* Skip over spaces */

	/* Check for 'TO ' */
	if ((seq[0] == 'T' || seq[0] == 't') && (seq[1] == 'O' || seq[1] == 'o') && seq[2] == ' ') {
		for (seq += 2; *seq == ' '; ++seq) ;	/* Skip over spaces */
		for (x = 0; seq[x] && seq[x] != ' '; ++x) ;	/* Skip to space */
		c = seq[x];
		seq[x] = 0;	/* Zero terminate the string */
		w = keyval(seq);	/* Get key */
		if (w < 0)
			return NULL;
		seq[x] = c;	/* Restore the space or 0 */
		for (seq += x; *seq == ' '; ++seq) ;	/* Skip over spaces */
	}

	if (v > w)
		return NULL;

	*vv = v;
	*ww = w;
	return seq;
}

/* Add a binding to a keymap */

static KMAP *kbuild(CAP *cap, KMAP *kmap, char *seq, void *bind, int *err, char *capseq, int seql)
{
	int v, w;

	if (!seql && seq[0] == '.' && seq[1]) {
		int x, c;
		unsigned char *s;

		for (x = 0; seq[x] && seq[x] != ' '; ++x) ;
		c = seq[x];
		seq[x] = 0;
#ifdef __MSDOS__
		if (!strcmp(seq + 1, "ku")) {
			capseq = "\0H";
			seql = 2;
		} else if (!strcmp(seq + 1, "kd")) {
			capseq = "\0P";
			seql = 2;
		} else if (!strcmp(seq + 1, "kl")) {
			capseq = "\0K";
			seql = 2;
		} else if (!strcmp(seq + 1, "kr")) {
			capseq = "\0M";
			seql = 2;
		} else if (!strcmp(seq + 1, "kI")) {
			capseq = "\0R";
			seql = 2;
		} else if (!strcmp(seq + 1, "kD")) {
			capseq = "\0S";
			seql = 2;
		} else if (!strcmp(seq + 1, "kh")) {
			capseq = "\0G";
			seql = 2;
		} else if (!strcmp(seq + 1, "kH")) {
			capseq = "\0O";
			seql = 2;
		} else if (!strcmp(seq + 1, "kP")) {
			capseq = "\0I";
			seql = 2;
		} else if (!strcmp(seq + 1, "kN")) {
			capseq = "\0Q";
			seql = 2;
		} else if (!strcmp(seq + 1, "k1")) {
			capseq = "\0;";
			seql = 2;
		} else if (!strcmp(seq + 1, "k2")) {
			capseq = "\0<";
			seql = 2;
		} else if (!strcmp(seq + 1, "k3")) {
			capseq = "\0=";
			seql = 2;
		} else if (!strcmp(seq + 1, "k4")) {
			capseq = "\0>";
			seql = 2;
		} else if (!strcmp(seq + 1, "k5")) {
			capseq = "\0?";
			seql = 2;
		} else if (!strcmp(seq + 1, "k6")) {
			capseq = "\0@";
			seql = 2;
		} else if (!strcmp(seq + 1, "k7")) {
			capseq = "\0A";
			seql = 2;
		} else if (!strcmp(seq + 1, "k8")) {
			capseq = "\0B";
			seql = 2;
		} else if (!strcmp(seq + 1, "k9")) {
			capseq = "\0C";
			seql = 2;
		} else if (!strcmp(seq + 1, "k0")) {
			capseq = "\0D";
			seql = 2;
		}
		seq[x] = c;
		if (seql) {
			for (seq += x; *seq == ' '; ++seq) ;
		}
#else
		s = jgetstr(cap, seq + 1);
		seq[x] = c;
		if (s && (s = tcompile(cap, s, 0, 0, 0, 0))
		    && (sLEN(s) > 1 || (signed char)s[0] < 0)) {
			capseq = s;
			seql = sLEN(s);
			for (seq += x; *seq == ' '; ++seq) ;
		}
#endif
		else {
			*err = -2;
			return kmap;
		}
	}

	if (seql) {
		v = w = (unsigned char) *capseq++;
		--seql;
	} else {
		seq = range(seq, &v, &w);
		if (!seq) {
			*err = -1;
			return kmap;
		}
	}

	if (!kmap)
		kmap = mkkmap();	/* Create new keymap if 'kmap' was NULL */

	/* Make bindings between v and w */
	while (v <= w) {
		if (*seq || seql) {
			if (kmap->keys[v].k == 0)
				kmap->keys[v].value.submap = NULL;
			kmap->keys[v].k = 1;
			kmap->keys[v].value.submap = kbuild(cap, kmap->keys[v].value.bind, seq, bind, err, capseq, seql);
		} else {
			if (kmap->keys[v].k == 1)
				rmkmap(kmap->keys[v].value.submap);
			kmap->keys[v].k = 0;
			kmap->keys[v].value.bind =
			    /* This bit of code sticks the key value in the macro */
			    (v == w ? macstk(bind, v) : dupmacro(macstk(bind, v)));
		}
		++v;
	}
	return kmap;
}

int kadd(CAP *cap, KMAP *kmap, unsigned char *seq, void *bind)
{
	int err = 0;

	kbuild(cap, kmap, seq, bind, &err, NULL, 0);
	return err;
}

void kcpy(KMAP *dest, KMAP *src)
{
	int x;

	for (x = 0; x != KEYS; ++x)
		if (src->keys[x].k == 1) {
			if (dest->keys[x].k != 1) {
				dest->keys[x].k = 1;
				dest->keys[x].value.submap = mkkmap();
			}
			kcpy(dest->keys[x].value.submap, src->keys[x].value.submap);
		} else if (src->keys[x].k == 0 && src->keys[x].value.bind) {
			if (dest->keys[x].k == 1)
				rmkmap(dest->keys[x].value.submap);
			dest->keys[x].value.bind = src->keys[x].value.bind;
			dest->keys[x].k = 0;
		}
}

/* Remove a binding from a keymap */

int kdel(KMAP *kmap, unsigned char *seq)
{
	int err = 1;
	int v, w;

	seq = range(seq, &v, &w);
	if (!seq)
		return -1;

	/* Clear bindings between v and w */
	while (v <= w) {
		if (*seq) {
			if (kmap->keys[v].k == 1) {
				int r = kdel(kmap->keys[v].value.submap, seq);

				if (err != -1)
					err = r;
			}
		} else {
			if (kmap->keys[v].k == 1)
				rmkmap(kmap->keys[v].value.submap);
			kmap->keys[v].k = 0;
			kmap->keys[v].value.bind = NULL;
			if (err != -1)
				err = 0;
		}
		++v;
	}

	return err;
}
