/* key.c, Atto Emacs, Public Domain, Hugh Barney, 2016, Derived from: Anthony's Editor January 93 */

#include "header.h"

struct mapkey {
	const char *key_desc;   	/* name of bound function */
	const char *key_bytes;  	/* the string of bytes when this key is pressed */
	void (*func)(void);
};

struct cursorkey {
        const char *key_desc;
        int key_val;
        void (*func)(void);
};

static const struct cursorkey curskeymap[] = {
	{"backspace delete-left    ", KEY_BACKSPACE, backsp },
	{"down next-line           ", KEY_DOWN, down },
	{"up previous-line         ", KEY_UP, up },
	{"left backward-character  ", KEY_LEFT, left },
	{"right forward-character  ", KEY_RIGHT, right },
	{"home beginning-of-line   ", KEY_HOME, lnbegin },
	{"end end-of-line          ", KEY_END, lnend },
	{"pgup backward-page       ", KEY_PPAGE, pgup },
	{"pgdn forward-page        ", KEY_NPAGE, pgdown },
	{"resize resize-terminal   ", KEY_RESIZE, resize_terminal },
	{"del forward-delete-char  ", KEY_DC, delete },
	{"ins toggle-overwrite-mode", KEY_IC, toggle_overwrite_mode },
	{"K_ERROR                  ", -1, NULL }
};

/* desc, keys, func */
static const struct mapkey keymap[] = {
	{"C-a beginning-of-line    ", "\x01", lnbegin },
	{"C-b backward-char        ", "\x02", left },
	{"C-d delete               ", "\x04", delete },
	{"C-e end-of-line          ", "\x05", lnend },
	{"C-f foward-char          ", "\x06", right },
	{"C-h backspace            ", "\x7F", backsp },
	{"C-k kill-to-eol          ", "\x0B", killtoeol },
	{"C-l refresh              ", "\x0C", redraw },
	{"C-n next-line            ", "\x0E", down },
	{"C-p previous-line        ", "\x10", up },
	{"C-r search               ", "\x12", search },
	{"C-s search               ", "\x13", search },
	{"C-v forward-page         ", "\x16", pgdown },
	{"C-w kill-region          ", "\x17", cut},
	{"C-y yank                 ", "\x19", paste},
	{"C-space set-mark         ", "\x00", iblock },  /* ctrl-space */
	{"C-x 1 delete-other-window", "\x18\x31", delete_other_windows },
	{"C-x 2 split-window       ", "\x18\x32", split_window },
	{"C-x o other-window       ", "\x18\x6F", next_window },
	{"C-x = cursor-position    ", "\x18\x3D", showpos },
	{"C-x i insert-file        ", "\x18\x69", insertfile },
	{"C-x k kill-buffer        ", "\x18\x6B", killbuffer },
	{"C-x C-n next-buffer      ", "\x18\x0E", next_buffer },
	{"C-x n next-buffer        ", "\x18\x6E", next_buffer },
	{"C-x C-f find-file        ", "\x18\x06", readfile },
	{"C-x C-s save-buffer      ", "\x18\x13", savebuffer },  
	{"C-x C-w write-file       ", "\x18\x17", writefile },  /* write and prompt for name */
	{"C-x C-c exit             ", "\x18\x03", quit_ask },
	{"esc b back-word          ", "\x1B\x62", wleft },
	{"esc f forward-word       ", "\x1B\x66", wright },
	{"esc g gotoline           ", "\x1B\x67", gotoline },
	{"esc k kill-region        ", "\x1B\x6B", cut },
	{"esc r query-replace      ", "\x1B\x72", query_replace },
	{"esc v backward-page      ", "\x1B\x76", pgup },
	{"esc w copy-region        ", "\x1B\x77", copy},
	{"esc @ set-mark           ", "\x1B\x40", iblock },  /* esc-@ */
	{"esc < beg-of-buf         ", "\x1B\x3C", top },
	{"esc > end-of-buf         ", "\x1B\x3E", bottom },
#ifndef SMALL
	{"esc esc show-menu        ", "\x1B\x1B", menu_escape },
#endif
	{"K_ERROR                  ", NULL, NULL }
};

char_t *get_key(void (**command_out)(void))
{
	const struct cursorkey *ck;
	const struct mapkey *k;
	int submatch;
	int res;
	static char_t buffer[K_BUFFER_LENGTH];
	static char_t *record = buffer;

	*command_out = NULL;

	/* if recorded bytes remain, return next recorded byte. */
	if (*record != '\0') {
		return record++;
	}
	/* reset record buffer. */
	record = buffer;

	do {
		assert(K_BUFFER_LENGTH > record - buffer);
		/* read and record one byte. */
		res = getch();
		if (res == ERR) {
			*record = '\0';
			return record; /* empty string */
		}

		for (ck = curskeymap; ck->func != NULL; ++ck) {
			if (ck->key_val == res) {
				*record = '\0';
				*command_out = ck->func;
				return record; /* empty string */
			}
		}

		*record++ = (char_t)res;
		*record = '\0';

		/* if recorded bytes match any multi-byte sequence... */
		for (k = keymap, submatch = 0; k->key_bytes != NULL; ++k) {
			const char_t *p, *q;

			for (p = buffer, q = (const char_t *)k->key_bytes; *p == *q; ++p, ++q) {
			        /* an exact match */
				if (*q == '\0' && *p == '\0') {
	    				record = buffer;
					*record = '\0';
					*command_out = k->func;
					return record; /* empty string */
				}
			}
			/* record bytes match part of a command sequence */
			if (*p == '\0' && *q != '\0') {
				submatch = 1;
			}
		}
	} while (submatch);
	/* nothing matched, return recorded bytes. */
	record = buffer;
	return (record++);
}

int getinput(const char *prompt, char *buf, int nbuf, int flag)
{
	int cpos = 0;
	int c;
	int start_col = strlen(prompt);

	mvaddstr(MSGLINE, 0, prompt);
	clrtoeol();

	if (flag == F_CLEAR) buf[0] = '\0';

	/* if we have a default value print it and go to end of it */
	if (buf[0] != '\0') {
		addstr(buf);
		cpos = strlen(buf);
	}

	for (;;) {
		refresh();
		c = getch();
		/* ignore control keys other than backspace, cr, lf */
		if (c < 32 && c != 0x07 && c != 0x08 && c != 0x0a && c != 0x0d)
			continue;

		switch(c) {
		case 0x0a: /* cr, lf */
		case 0x0d:
			buf[cpos] = '\0';
			return (cpos > 0 ? TRUE : FALSE);

		case 0x07: /* ctrl-g */
			return FALSE;

		case 0x7f: /* del, erase */
		case 0x08: /* backspace */
		case KEY_BACKSPACE:
			if (cpos == 0)
				continue;

			move(MSGLINE, start_col + cpos - 1);
			addch(' ');
			move(MSGLINE, start_col + cpos - 1);
			buf[--cpos] = '\0';
			break;

		default:
			if (c > 255)	/* function key, not a character */
				break;
			if (cpos < nbuf -1) {
				addch(c);
				buf[cpos++] = c;
				buf[cpos] ='\0';
			}
			break;
		}
	}
}
