#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include "global.h"
#include "constants.h"
#include "command.h"
#include "binding.h"
#include "window.h"

extern server_ptr gsrv;

/* Non meta keys are in META[MAX_META][...] */
struct binds META[MAX_META+1][128];

extern void handle_key();
extern char *inp_backspace(), *inp_backward_character(),
            *inp_backward_history(), *inp_beginning_of_line(),
            *inp_forward_character(), *inp_forward_history();
void bind_typetext(), bind_execute_line();

struct command_node bindlist[] =
{
  {{"META1"},              (char *)(-9)},
  {{"META2"},              (char *)(-8)},
  {{"META3"},              (char *)(-7)},
  {{"META4"},              (char *)(-6)},
  {{"META5"},              (char *)(-5)},
  {{"META6"},              (char *)(-4)},
  {{"META7"},              (char *)(-3)},
  {{"META8"},              (char *)(-2)},
  {{"META9"},              (char *)(-1)},
  {{"BACKSPACE"},          (char *)inp_backspace},
  {{"BACKWARD_CHARACTER"}, (char *)inp_backward_character},
  {{"BACKWARD_HISTORY"},   (char *)inp_backward_history},
  {{"BEGINNING_OF_LINE"},  (char *)inp_beginning_of_line},
  {{"FORWARD_CHARACTER"},  (char *)inp_forward_character},
  {{"FORWARD_HISTORY"},    (char *)inp_forward_history},
  {{"PARSE_COMMAND"},      (char *)bind_execute_line},
  {{"SELF_INSERT"},        (char *)(-10)},
#ifdef ENABLE_SPAWN
  {{"STOP_IRC"},           (char *)user_spawn},
#endif
  {{"TYPE_TEXT"},          (char *)bind_typetext},
  {{NULL},                 NULL}
};

/* ------------------------------------------------------------------------ */
void bind_execute_line(char *line)
{
  parse_line(line, line, FALSE, FALSE);
}

/* ------------------------------------------------------------------------ */
extern unsigned char (*input_routine)();
unsigned char (*old)();

int bind_curr;
char *bind_line;
unsigned char bind_keyget(void)
{
  int key;
  if (bind_line[bind_curr] == '^')
    key = bind_line[++bind_curr] - 64;
  else
    key = bind_line[bind_curr];
  if (bind_line[bind_curr]) bind_curr++;
  if (!bind_line[bind_curr]) {
    input_routine = old;
    free(bind_line);
  }
  return key;
}

/* ------------------------------------------------------------------------ */
void bind_typetext(char *line)
{
  if (input_routine==bind_keyget) {
    yell("*** Recursive TYPE. Eeeek!");
    return;
  }
  bind_curr = 0;
  bind_line = ms_strcpy(line);
  old = input_routine;
  input_routine = bind_keyget;
}

/* ------------------------------------------------------------------------ */
void show_bind(int meta, int key)
{
  int loop, good = -1;
  char comm[MAXLEN], string[MAXLEN];
  if (META[meta][key].func == 0) return;
  if ((int)META[meta][key].func < (int)0) {
    (void)strcpy(comm, bindlist[(int)META[meta][key].func + MAX_META].command);
  } else {
    for (loop=0; bindlist[loop].command; loop++)
       if ((int)META[meta][key].func == (int)bindlist[loop].constant)
         good = loop;
    (void)strcpy(comm, (char *)bindlist[good].command);
  }
  if (meta<MAX_META) sprintf(string, "META%d-", meta+1);
  else string[0] = 0;
  if (key<32)
    say("*** %s^%c is bound to %s %s", string, key+64, comm,
        META[meta][key].param);
  else
     say("*** %s%c is bound to %s %s", string, key, comm,
         META[meta][key].param);
}

/* ------------------------------------------------------------------------ */
/*
 *   -(MAX_META+1) implies no character
 */

int trans_key(key)
  char **key;
{
  char *duh = *key;
  int ret = 0;

  if (*duh == 0)
    ret = -(MAX_META+1);
  else if (*duh=='^') {
    duh++;
    if ((*duh >= 65) && (*duh <= 95)) ret = *duh-64;
    else if ((*duh >= 97) && (*duh <= 122)) ret = *duh-96;
    else if (*duh!=0) ret = 0;
    if (*duh) duh++;
  } else {
    ret = *duh;
    if (strncasecmp(duh, "meta", 4) == 0) {
      duh += 4;
      ret = *duh - '1' - MAX_META;
      duh += 2;
    } else duh++;
  }
  *key = duh;
  return ret;
}

void dobind(str)
   char *str;
{
  int num, meta, mlame = -1, loop, loop2, i, indx;
  char first[MAXLEN], *ff;

  (void)grab_word(&str, ' ', first);
  if (!*first) {
    for (loop=0; loop<=MAX_META; loop++)
      for (loop2=0;loop2<128;loop2++)
        show_bind(loop, loop2);
    return;
  }

  ff = first;
  meta = trans_key(&ff);
  num = trans_key(&ff);
  if (num==-(MAX_META+1)) {  /* Only a signle key */
    num = meta;
    meta = 0;
  }

/* Was there something left in the input string */
  if (*ff) {
    say("*** Key sequences may not contain more than two keys");
    return;
  }

/* Get the right META key 0 -> MAX_META-1 */
  if (!meta) mlame = MAX_META;
  else if (meta<0)
    mlame = meta + MAX_META;
  else if ((int)META[MAX_META][meta].func < (int)0)
    mlame = (int)META[MAX_META][meta].func + MAX_META;
  if (mlame == -1) {
    say("*** Illegal key sequence: %c is not a meta-key", meta);
    return;
  }

  (void)grab_word(&str, ' ', first);
  loop = lookup_command(bindlist, first, &indx);
  if (loop<0)
    say("*** Unknown or ambiguous function: %s", first);
  else {
    (void)strcpy(META[mlame][num].param, str);
    if ((int)indx==(-MAX_META-1))
      META[mlame][num].func = 0;
    else
      (void)memcpy(&META[mlame][num].func, &indx, sizeof(indx));
    show_bind(mlame, num);
  }
}

void init_binds(void)
{
  int loop,loop2;
  for (loop=0; loop<=MAX_META; loop++)
    for (loop2=0; loop2<128; loop2++) {
      META[loop][loop2].func = NULL;
      META[loop][loop2].param[0] = 0;
    }
}
