/*
 * ui_binding.c -- management of keybindings
 *
 * Copyright (C) 2001,2001 Øyvind Kolås <pippin@users.sourceforge.net>
 *
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2, or (at your option) any later
 * version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 59
 * Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

#if HAVE_CONFIG_H
#include <config.h>
#endif

#include "tree.h"
#include "prefs.h"
#include <ctype.h>

#include "cli.h"				/* should be moved to keybinding file */
#include <string.h>				/* should be moved to keybinding file */
#include <stdlib.h>
#include <curses.h>

#include "ui.h"

static int scope_count = ui_scope_tempterm;

char *ui_scope_names[MAX_SCOPES] =
	{ "main", "confirm", "nodeedit", "lineedit" };

static char *ui_action_names[ui_action_terminator - 512] = {
	"backspace",
	"bkill_word",
	"bol",
	"bottom",
	"bskipword",
	"cancel",
	"command",
	"complete",
	"confirm",
	"delete",
	"down",
	"eol",
	"ignore",
	"join",
	"kill_line",
	"kill_to_bol",
	"kill_to_eol",
	"kill_word",
	"left",
	"pagedown",
	"pageup",
	"quit",
	"right",
	"skipword",
	"split",
	"top",
	"unbound",
	"up",
};


typedef struct {
	int code;
	char *name;
} keydbitm;

static keydbitm keydb[] = {
	{KEY_BREAK, "break"},
	{KEY_DOWN, "down"},
	{KEY_UP, "up"},
	{KEY_LEFT, "left"},
	{KEY_RIGHT, "right"},
	{KEY_HOME, "home"},
	{KEY_BACKSPACE, "backspace"},
	{KEY_F (1), "f1"},
	{KEY_F (2), "f2"},
	{KEY_F (3), "f3"},
	{KEY_F (4), "f4"},
	{KEY_F (5), "f5"},
	{KEY_F (6), "f6"},
	{KEY_F (7), "f7"},
	{KEY_F (8), "f8"},
	{KEY_F (9), "f9"},
	{KEY_F (10), "f10"},
	{KEY_F (11), "f11"},
	{KEY_F (12), "f12"},
	{KEY_DC, "del"},
	{KEY_IC, "ins"},
	{KEY_EIC, "eic"},
	{KEY_NPAGE, "npage"},
	{KEY_PPAGE, "ppage"},
	{KEY_ENTER, "enter"},
	{KEY_LL, "ll"},
	{KEY_A1, "a1"},
	{KEY_A3, "a3"},
	{KEY_B2, "b2"},
	{KEY_C1, "c1"},
	{KEY_C3, "c3"},
	{KEY_BEG, "beg"},
	{KEY_CANCEL, "cancel"},
	{KEY_CLOSE, "close"},
	{KEY_COMMAND, "command"},
	{KEY_COPY, "copy"},
	{KEY_CREATE, "create"},
	{KEY_END, "end"},
	{KEY_EXIT, "exit"},
	{KEY_FIND, "find"},
	{KEY_HELP, "help"},
	{KEY_MARK, "mark"},
	{KEY_MESSAGE, "message"},
	{KEY_MOVE, "move"},
	{KEY_NEXT, "next"},
	{KEY_OPEN, "open"},
	{KEY_OPTIONS, "options"},
	{KEY_PREVIOUS, "previous"},
	{KEY_REDO, "redo"},
	{KEY_REFERENCE, "reference"},
	{KEY_REFRESH, "refresh"},
	{KEY_REPLACE, "replace"},
	{KEY_RESTART, "restart"},
	{KEY_RESUME, "resume"},
	{KEY_SAVE, "save"},
	{KEY_SBEG, "sbeg"},
	{KEY_SCANCEL, "scancel"},
	{KEY_SCOMMAND, "scommand"},
	{KEY_SCOPY, "scopy"},
	{KEY_SCREATE, "screate"},
	{KEY_SDC, "sdc"},
	{KEY_SDL, "sdl"},
	{KEY_SELECT, "select"},
	{KEY_SEND, "send"},
	{KEY_SEOL, "seol"},
	{KEY_SEXIT, "sexit"},
	{KEY_SFIND, "sfind"},
	{KEY_SHELP, "shelp"},
	{KEY_SHOME, "shome"},
	{KEY_SIC, "sic"},
	{KEY_SLEFT, "sleft"},
	{KEY_SMESSAGE, "smessage"},
	{KEY_SMOVE, "smove"},
	{KEY_SNEXT, "snext"},
	{KEY_SOPTIONS, "soptions"},
	{KEY_SPREVIOUS, "sprevious"},
	{KEY_SPRINT, "sprint"},
	{KEY_SREDO, "sredo"},
	{KEY_SREPLACE, "sreplace"},
	{KEY_SRIGHT, "sright"},
	{KEY_SRSUME, "srsume"},
	{KEY_SSAVE, "ssave"},
	{KEY_SSUSPEND, "ssupsend"},
	{KEY_SUNDO, "sundo"},
	{KEY_SUSPEND, "suspend"},
	{KEY_UNDO, "undo"},
	{' ', "space"},
	{0, "^space"},
	{1, "^A"},
	{2, "^B"},
	{3, "^C"},
	{4, "^D"},
	{5, "^E"},
	{6, "^F"},
	{7, "^G"},
	{8, "backspace2"},
	{9, "tab"},
	{10, "^J"},
	{11, "^K"},
	{12, "^L"},
	{13, "return"},
	{14, "^N"},
	{15, "^O"},
	{16, "^P"},
	{17, "^Q"},
	{18, "^R"},
	{19, "^S"},
	{20, "^T"},
	{21, "^U"},
	{22, "^V"},
	{23, "^W"},
	{24, "^X"},
	{25, "^Y"},
	{26, "^Z"},
	{27, "esc"},
	{999, ".."},
	{1000, "any"},				/* special key used to trap all keys (i.e. avoid typing without editing) */
};


#define MAX_BINDINGS 255		/* maximum number of bound functions in one context */

Tbinding ui_binding[MAX_SCOPES][MAX_BINDINGS];
static int ui_binding_count[MAX_SCOPES] = { 0 };


static int string2scope (char *str)
{
	int j = 0;

	while (j < scope_count) {
		if (!strcmp (str, ui_scope_names[j]))
			return j;
		j++;
	}
	if (j < MAX_SCOPES - 2) {
		ui_scope_names[j] = strdup (str);
		scope_count++;
		return j;
	}
	printf ("scope limit exceeded\n");
	exit (0);
}


static int string2action (char *str)
{
	int j = 0;

	while (j < ui_action_terminator - 512) {
		if (!strcmp (ui_action_names[j], str))
			return j + 512;
		j++;
	}
	return -1;
}


static int string2keycode (char *str)
{
	int i;

	if (strlen (str) == 1)		/* a string consisting og just one char,.. is a single key */
		return str[0];

	for (i = 0; i < sizeof (keydb) / sizeof (keydbitm); i++)
		if (!strcmp (keydb[i].name, str))
			return (keydb[i].code);
	return -1;
}


int ui_current_scope = 0;

static int ui_context_cmd (int argc, char **argv, void *data)
{
	if(argc<2){
		cli_outfunf("usage: %s <contextname>",argv[0]);
		return(int)data;
	}
	ui_current_scope = string2scope (argv[1]);
	return (int) data;
}

static void makebinding (int scope_no, int key, int action, char *action_name,
						 char *action_params)
{
	ui_binding[scope_no][ui_binding_count[scope_no]].key = key;
	ui_binding[scope_no][ui_binding_count[scope_no]].action = action;
	ui_binding[scope_no][ui_binding_count[scope_no]].action_name =
		strdup (action_name);
	ui_binding[scope_no][ui_binding_count[scope_no]].action_param =
		strdup (action_params);
	ui_binding_count[scope_no]++;
}

static int ui_bind_cmd (int argc, char **argv, void *data)
{
/*	char context[40];*/
	char *key;
	char *action;

	if(argc<3){
		cli_outfunf("error in bindings %s %s %s %s\n",argv[0],argv[1],argv[2],argv[3]);
		return (int)data;
	}
	key=argv[1];
	action=argv[2];

	if (string2action (action) != -1) {
		makebinding (ui_current_scope, string2keycode (key),
					 string2action (action), action, "");
	} else {
		makebinding (ui_current_scope, string2keycode (key),
					 ui_action_command, "command", action);
	}

	return (int) data;
}



static Tbinding keyproxy = { 0, 0, "key", "key" };
Tbinding *lastbinding;

Tbinding *parsekey (int key, int scope)
{
	int j = 0;

	while (j < ui_binding_count[scope]) {
		if (key == ui_binding[scope][j].key) {
			lastbinding = &ui_binding[scope][j];
			return lastbinding;
		} else if (ui_binding[scope][j].key == 1000 /* anykey */ ) {
			lastbinding = &ui_binding[scope][j];
			*((int *) &lastbinding->action_param[0]) = key;
			return lastbinding;
		}

		j++;
	}
	keyproxy.action = key;
	keyproxy.key = key;
	lastbinding = &keyproxy;
	return lastbinding;
}

static char *unboundstr = "><";

char *resolve_binding (int scope, int action)
{
	int no = 0;

	while (no < ui_binding_count[scope]) {
		if (action == ui_binding[scope][no].action)
			return tidy_keyname (keyname (ui_binding[scope][no].key));
		no++;
	}
	return unboundstr;
}


/* 
 *	Function to make a keynames returned from curses a little nicer
 *
 */
char *tidy_keyname (const char *keyname)
{
	static char buf[100];

	strcpy (buf, keyname);
	if (!strncmp (buf, "KEY_", 4)) {
		memmove (buf, buf + 4, sizeof (buf) - 4);
	}

	if (buf[1] == '(') {
		memmove (buf + 1, buf + 2, sizeof (buf) - 3);	/* Function keys */
		if (buf[2] == ')')
			memmove (buf + 2, buf + 3, sizeof (buf) - 4);
		if (buf[3] == ')')
			memmove (buf + 3, buf + 4, sizeof (buf) - 5);
	}

	if (!strcmp (buf, "IC"))
		return "ins";
	if (!strcmp (buf, "DC"))
		return "del";
	if (!strcmp (buf, "UP"))
		return "up";
	if (!strcmp (buf, "^M"))
		return "return";
	if (!strcmp (buf, "^I"))
		return "tab";
	if (!strcmp (buf, "END"))
		return "end";
	if (!strcmp (buf, "^["))
		return "esc";
	if (!strcmp (buf, "^@"))
		return "^space";
	if (!strcmp (buf, " "))
		return "space";
	if (!strcmp (buf, "BACKSPACE"))
		return "backspace";
	if (strlen (buf) > 3) {
		char *c = buf;

		while (*c) {
			*c = tolower (*c);
			c++;
		}
	}
	return buf;
}


/*
!init_ui_binding();
*/

static int dummy_cmd (int argc, char **argv, void *data)
{
	return (int) data;
}

void init_ui_binding ()
{
	cli_add_command ("bind", ui_bind_cmd, "<key> <action> [parameter]");
	cli_add_help ("bind",
				  "Binds the action, to the specified key, subsequent actions my be bound by using the virtual key '..' thus chaining them together into a small script.");
	cli_add_command ("context", ui_context_cmd, "<context>");
	cli_add_help ("context",
				  "Changes context, context may be one of 'main', 'lineedit', 'nodeedit', 'confirm' or any other userdefined context.");

/* dummy commands to show the 'builtins' */
/*	cli_add_command ("backspace", dummy_cmd, "");
	cli_add_help ("backspace", "erase the preceding character");
	cli_add_command ("delete", dummy_cmd, "");
	cli_add_help ("delete", "erase the following character");

	cli_add_command ("down", dummy_cmd, "");
	cli_add_command ("left", dummy_cmd, "");
	cli_add_command ("right", dummy_cmd, "");
	cli_add_command ("up", dummy_cmd, "");
	cli_add_help ("down", "moves the cursor or selection down");
	cli_add_help ("left", "moves the cursor or selection left");
	cli_add_help ("right", "moves the cursor or selection right");
	cli_add_help ("up", "moves the cursor or selection up");

	cli_add_command ("bkill_word", dummy_cmd, "");
	cli_add_command ("kill_word", dummy_cmd, "");
	cli_add_help ("bkill_word", "erases the preceding word");
	cli_add_help ("bkill_word", "erases the following word");

	cli_add_command ("bol", dummy_cmd, "");
	cli_add_help ("bol", "move cursor to beginning of line");

	cli_add_command ("top", dummy_cmd, "");
	cli_add_command ("bottom", dummy_cmd, "");
	cli_add_help ("top", "move to the topmost sibling of the current entry");
	cli_add_help ("bottom",
				  "move to the bottommost sibling of the current entry");

	cli_add_command ("bskipword", dummy_cmd, "");
	cli_add_command ("cancel", dummy_cmd, "");

	cli_add_command ("complete", dummy_cmd, "");
	cli_add_help ("complete",
				  "output possible completions of entered command or variable");

	cli_add_command ("confirm", dummy_cmd, "");
	cli_add_help ("confirm",
				  "confirm the question posed, (used in dialogs asking for the users' consent)");


	cli_add_command ("eol", dummy_cmd, "");
	cli_add_help ("eol", "move to the end of the line");

	cli_add_command ("ignore", dummy_cmd, "");

	cli_add_command ("join", dummy_cmd, "");
	cli_add_command ("split", dummy_cmd, "");

	cli_add_help ("join",
				  "join the currently edited node with the following sibling");
	cli_add_help ("split",
				  "split the currently edited node at the cursor location");

	cli_add_command ("kill_line", dummy_cmd, "");
	cli_add_help ("kill_line", "erase the currently edited line");

	cli_add_command ("kill_to_bol", dummy_cmd, "");
	cli_add_command ("kill_to_eol", dummy_cmd, "");

	cli_add_command ("pagedown", dummy_cmd, "");
	cli_add_command ("pageup", dummy_cmd, "");

	cli_add_command ("skipword", dummy_cmd, "");

	cli_add_command ("unbound", dummy_cmd, "");
	cli_add_help ("unbound",
				  "give a message indicating that the pressed key is not bound in the current context");
*/
}
