/*
 *  $Id: key_bind.c,v 1.5 2002/03/30 01:45:41 hiroo Exp $
 */

/*
 * FreeWnn is a network-extensible Kana-to-Kanji conversion system.
 * This file is part of FreeWnn.
 * 
 * Copyright Kyoto University Research Institute for Mathematical Sciences
 *                 1987, 1988, 1989, 1990, 1991, 1992
 * Copyright OMRON Corporation. 1987, 1988, 1989, 1990, 1991, 1992, 1999
 * Copyright ASTEC, Inc. 1987, 1988, 1989, 1990, 1991, 1992
 * Copyright FreeWnn Project 1999, 2000, 2002
 *
 * Maintainer:  FreeWnn Project   <freewnn@tomo.gr.jp>
 *
 * 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 of the License, 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
 */

/*
    0: 変換後、変換結果を修正しているモード
    1: 変換前の文字列を入力しているモード
    2: 変換後、文節の区切りを伸ばしたり縮めたりしているモード
    3: バッファに文字が何も入っていないモード
    4: カーソルを動かしながら、何か候補を選択する時のモード
    5: 単語登録時に、漢字の範囲を設定している時のモード
    6: 単語登録時の読み入力などの文字列を入力している時
    7: 単語検索のモード
    8: 辞書検索のモード
    9:インスペクトのモード
*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <stdio.h>
#if STDC_HEADERS
#  include <stdlib.h>
#  include <string.h>
#else
#  if HAVE_STRINGS_H
#    include <strings.h>
#  endif
#endif /* STDC_HEADERS */

#include "commonhd.h"
#include "wnn_config.h"
#include "sdefine.h"
#include "sheader.h"
#define MADA NULL

extern int insert_it_as_yomi ();
extern int select_jikouho ();
extern int select_jikouho_dai ();
extern int jikouho_c ();
extern int zenkouho_c ();
extern int jikouho_dai_c ();
extern int zenkouho_dai_c ();
extern int enlarge_bunsetsu ();
extern int smallen_bunsetsu ();
extern int top_bunsetsu ();
extern int end_bunsetsu ();
extern int ren_henkan ();
extern int tan_henkan ();
extern int tan_henkan_dai ();
extern int nobi_henkan ();
extern int nobi_henkan_dai ();
extern int send_string ();
extern int t_yank ();
extern int yank_c ();
extern int kakutei ();
extern int remember_me ();
extern int reset_line ();
extern int touroku_c ();
extern int forward_char ();
extern int backward_char ();
extern int forward ();
extern int backward ();
extern int c_top ();
extern int c_end ();
extern int c_end_nobi ();
extern int t_quit ();
extern int t_delete_char ();
extern int delete_c ();
extern int t_rubout ();
extern int rubout_c ();
extern int t_markset ();
extern int kuten ();
extern int t_kill ();
extern int kill_c ();
extern int kaijo ();
extern int t_jmp_forward ();
extern int t_jmp_backward ();
extern int t_ret ();
extern int previous_history ();
extern int next_history ();
extern int forward_bunsetsu ();
extern int backward_bunsetsu ();
extern int jutil_c ();
extern int jis ();
extern int tijime ();
extern int jmptijime ();
extern int henkan_forward ();
extern int henkan_backward ();
extern int backward_c ();
extern int quote ();
extern int send_ascii ();
extern int not_send_ascii ();
extern int toggle_send_ascii ();
extern int send_ascii_e ();
extern int not_send_ascii_e ();
extern int toggle_send_ascii_e ();
extern int pop_send_ascii ();
extern int pop_send_ascii_e ();
extern int quote_send_ascii_e ();
extern int reconnect_jserver ();

extern int select_select ();
extern int quit_select ();
extern int lineend_select ();
extern int linestart_select ();
extern int redraw_select ();
extern int previous_select ();
extern int next_select ();
extern int backward_select ();
extern int forward_select ();

extern int select_select_line ();
extern int quit_select_line ();
extern int lineend_select_line ();
extern int linestart_select_line ();
extern int redraw_select_line ();
extern int previous_select_line ();
extern int next_select_line ();
extern int backward_select_line ();
extern int forward_select_line ();
extern int inspect_kouho ();
extern int sakujo_kouho ();

extern int henkan_off ();

extern int kdicdel ();
extern int kdicuse ();
extern int kdiccom ();
extern int kworddel ();
extern int kworduse ();
extern int kwordcom ();
extern int kwordima ();
extern int kwordhindo ();

extern int inspectdel ();
extern int inspectuse ();
extern int previous_inspect ();
extern int next_inspect ();

extern int kankana_ren_henkan ();

static struct kansuu kansuu_hyo[] = {
  {"send_string", "send what is in the buffer to pty.", 0, send_string, send_string, send_string, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"forward_char", "cursor wo hitotu maeni susumeru.", 1, forward_bunsetsu, forward_char, henkan_forward, NULL, NULL, forward_char, forward_char, NULL, NULL, NULL},
  {"backward_char", "cursor wo hitotu usironi susumeru.", 1, backward_bunsetsu, backward_c, henkan_backward, NULL, NULL, backward_char, backward_char, NULL, NULL, NULL},
  {"goto_top_of_line", "", 1, top_bunsetsu, c_top, jmptijime, NULL, NULL, c_top, c_top, NULL, NULL, NULL},
  {"goto_end_of_line", "", 1, end_bunsetsu, c_end, c_end_nobi, NULL, NULL, c_end, c_end, NULL, NULL, NULL},
  {"delete_char_at_cursor", "", 1, NULL, delete_c, NULL, NULL, NULL, t_delete_char, t_delete_char, NULL, NULL, NULL},
  {"kaijo", "", 0, kaijo, NULL, kaijo, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"henkan", "", 1, NULL, ren_henkan, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"tan_henkan", "", 1, NULL, tan_henkan, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"tan_henkan_dai", "", 1, NULL, tan_henkan_dai, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"jikouho", "", 0, jikouho_c, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"zenkouho", "", 0, zenkouho_c, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"jikouho_dai", "", 0, jikouho_dai_c, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"zenkouho_dai", "", 0, zenkouho_dai_c, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"select_jikouho", "", 0, select_jikouho, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"select_jikouho_dai", "", 0, select_jikouho_dai, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"kill", "", 1, NULL, kill_c, NULL, NULL, NULL, t_kill, t_kill, NULL, NULL, NULL},
  {"yank", "", 1, NULL, yank_c, NULL, NULL, NULL, t_yank, t_yank, NULL, NULL, NULL},
  {"yank_e", "", 1, NULL, yank_c, NULL, yank_c, NULL, t_yank, t_yank, NULL, NULL, NULL},
  {"bunsetu_nobasi", "", 0, enlarge_bunsetsu, NULL, forward, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"bunsetu_chijime", "", 0, smallen_bunsetsu, NULL, tijime, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"jisho_utility", "", 0, jutil_c, jutil_c, jutil_c, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"jisho_utility_e", "", 0, jutil_c, jutil_c, jutil_c, jutil_c, NULL, NULL, NULL, NULL, NULL, NULL},
  {"touroku", "", 1, touroku_c, touroku_c, touroku_c, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"touroku_e", "", 1, touroku_c, touroku_c, touroku_c, touroku_c, NULL, NULL, NULL, NULL, NULL, NULL},
  {"kakutei", "", 1, kakutei, kakutei, kakutei, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"sainyuuryoku", "", 1, NULL, remember_me, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"sainyuuryoku_e", "", 1, NULL, remember_me, NULL, remember_me, NULL, NULL, NULL, NULL, NULL, NULL},
  {"kuten", "", 0, NULL, kuten, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"kuten_e", "", 0, NULL, kuten, NULL, kuten, NULL, NULL, NULL, NULL, NULL, NULL},
  {"jis", "", 0, NULL, jis, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"jis_e", "", 0, NULL, jis, NULL, jis, NULL, NULL, NULL, NULL, NULL, NULL},
  {"redraw_line", "", 1, reset_line, reset_line, reset_line, NULL, redraw_select, reset_line, reset_line, redraw_select_line, redraw_select_line, redraw_select_line},
  {"redraw_line_e", "", 1, reset_line, reset_line, reset_line, reset_line, redraw_select, reset_line, reset_line, redraw_select_line, redraw_select_line, redraw_select_line},
  {"previous_history_e", "", 1, NULL, previous_history, NULL, previous_history, NULL, NULL, NULL, NULL, NULL, NULL},
  {"previous_history", "", 1, NULL, previous_history, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"next_history_e", "", 1, NULL, next_history, NULL, next_history, NULL, NULL, NULL, NULL, NULL, NULL},
  {"next_history", "", 1, NULL, next_history, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"touroku_mark_set", "", 0, NULL, NULL, NULL, NULL, NULL, t_markset, NULL, NULL, NULL, NULL},
  {"touroku_return", "", 0, NULL, NULL, NULL, NULL, NULL, NULL, t_ret, NULL, NULL, NULL},
  {"quit", "", 0, NULL, NULL, NULL, NULL, quit_select, t_quit, t_quit, quit_select_line, quit_select_line, quit_select_line},
  {"touroku_jump_forward", "", 0, NULL, NULL, NULL, NULL, NULL, t_jmp_forward, NULL, NULL, NULL, NULL},
  {"touroku_jump_backward", "", 0, NULL, NULL, NULL, NULL, NULL, t_jmp_backward, NULL, NULL, NULL, NULL},
  {"change_to_insert_mode", "", 0, insert_it_as_yomi, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"quote_e", "", 0, NULL, quote, NULL, quote, NULL, NULL, NULL, NULL, NULL, NULL},
  {"quote", "", 0, NULL, quote, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"send_ascii_char", "", 0, send_ascii, send_ascii, send_ascii, send_ascii_e, send_ascii, send_ascii, send_ascii, send_ascii, send_ascii, send_ascii},

  {"toggle_send_ascii_char", "", 0, toggle_send_ascii, toggle_send_ascii, toggle_send_ascii, toggle_send_ascii_e, toggle_send_ascii, toggle_send_ascii, toggle_send_ascii, toggle_send_ascii,
   toggle_send_ascii, toggle_send_ascii},
  {"not_send_ascii_char", "", 0, not_send_ascii, not_send_ascii, not_send_ascii, not_send_ascii_e, not_send_ascii, not_send_ascii, not_send_ascii, not_send_ascii, not_send_ascii, not_send_ascii},
  {"pop_send_ascii_char", "", 0, pop_send_ascii, pop_send_ascii, pop_send_ascii, pop_send_ascii_e, pop_send_ascii, pop_send_ascii, pop_send_ascii, pop_send_ascii, pop_send_ascii, pop_send_ascii},
  {"quote_send_ascii_char", "", 0, NULL, NULL, NULL, quote_send_ascii_e, NULL, NULL, NULL, NULL, NULL, NULL},
  {"select_select", "", 0, NULL, NULL, NULL, NULL, select_select, NULL, NULL, select_select_line, select_select_line, select_select_line},
  {"lineend_select", "", 0, NULL, NULL, NULL, NULL, lineend_select, NULL, NULL, lineend_select_line, lineend_select_line, lineend_select_line},
  {"linestart_select", "", 0, NULL, NULL, NULL, NULL, linestart_select, NULL, NULL, linestart_select_line, linestart_select_line, linestart_select_line},
  {"previous_select", "", 0, NULL, NULL, NULL, NULL, previous_select, NULL, NULL, previous_select_line, previous_select_line, previous_inspect},
  {"next_select", "", 0, NULL, NULL, NULL, NULL, next_select, NULL, NULL, next_select_line, next_select_line, next_inspect},
  {"backward_select", "", 0, NULL, NULL, NULL, NULL, backward_select, NULL, NULL, backward_select_line, backward_select_line, backward_select_line},
  {"forward_select", "", 0, NULL, NULL, NULL, NULL, forward_select, NULL, NULL, forward_select_line, forward_select_line, forward_select_line},

  {"reconnect_jserver", "", 0, reconnect_jserver, reconnect_jserver, reconnect_jserver, NULL, reconnect_jserver, reconnect_jserver, reconnect_jserver, reconnect_jserver, reconnect_jserver,
   reconnect_jserver},
  {"henkan_on", "", 0, henkan_off, henkan_off, henkan_off, henkan_off, henkan_off, henkan_off, henkan_off, henkan_off, henkan_off, henkan_off},
  {"inspect", "", 0, inspect_kouho, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"sakujo_kouho", "", 0, sakujo_kouho, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"nobi_henkan_dai", "", 0, NULL, NULL, nobi_henkan_dai, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"nobi_henkan", "", 0, NULL, NULL, nobi_henkan, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"del_entry", "", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, kworddel, kdicdel, inspectdel},
  {"use_entry", "", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, kworduse, kdicuse, inspectuse},
  {"com_entry", "", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, kwordcom, kdiccom, NULL},
  {"kana_henkan", "", 1, NULL, kankana_ren_henkan, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL},
  {"hindo_set", "", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, kwordhindo, NULL, NULL},
  {"ima_bit", "", 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, kwordima, NULL, NULL}
};

int kansuu_hyo_entry_suu = sizeof (kansuu_hyo) / sizeof (struct kansuu);

static char key_binded[sizeof (kansuu_hyo) / sizeof (struct kansuu) + 1];

int key_binding ();

int
init_key_table ()
{
  FILE *fp, *fopen ();
  char file_name[PATHNAMELEN];
  int j, k;

  for (k = 0; k < sizeof (kansuu_hyo) / sizeof (struct kansuu); k++)
    {
      key_binded[k] = 0;
    }

  /* uumrcファイルから。 */
  if (uumkey_name_in_uumrc[0] != '\0')
    {
      strcpy (file_name, uumkey_name_in_uumrc);
    }
  else if (strcpy (file_name, LIBDIR), strcat (file_name, "/"), strcat (file_name, lang_dir), strcat (file_name, CPFILE), (fp = fopen (file_name, "r")) != NULL)
    {
      /* default */
      fclose (fp);
    }
  else
    {
/*      fprintf(stderr, "Can't open default key bind file.%s\n" , CPFILE); */
/*   fprintf(stderr,"デフォルトのキーバインドが異常です。 %s\n",CPFILE); */
      fprintf (stderr, MSG_GET (29), CPFILE);
      return (-1);
    }

  for (j = 0; j < 3; j++)
    {
/*
    for(k = 0; k < 32;k++){
      main_table[j][k] = null_fun;
    }
*/
      for (k = 0; k < TBL_SIZE; k++)
        {
          main_table[j][k] = NULL;
        }
      main_table[j][NEWLINE] = (int (*)()) 0;
      main_table[j][CR] = (int (*)()) 0;
    }
  for (j = 3; j < TBL_CNT; j++)
    {
      for (k = 0; k < TBL_SIZE; k++)
        {
          main_table[j][k] = NULL;
        }
    }
  if (key_binding (file_name) == -1)
    return (-1);
  return (0);
}


int
key_binding (file_name)
     char file_name[64];
{
  register int k, kk;
  FILE *fp, *fopen ();
  char func_name[64];
  int com_cnt;
  int com_num;
  char buf[256];
  char com_name[16][EXPAND_PATH_LENGTH];
  int lc = 0;
  int tbl;

  if (verbose_option)
    fprintf (stderr, "keybind: using keybind-file %s\r\n", file_name);
  if ((fp = fopen (file_name, "r")) == NULL)
    {
      printf ("Key_binding file \"%s\" doesn't exist.\r\n", file_name);
      return (-1);
    }

  for (tbl = 0; tbl < TBL_CNT; tbl++)
    {
      romkan_clear_tbl[tbl][rubout_code] = 0;   /* Not clear romkan when rubout */
    }
  main_table[1][rubout_code] = rubout_c;
  main_table[5][rubout_code] = t_rubout;
  main_table[6][rubout_code] = t_rubout;

  lc = 0;
  while (fgets (buf, 256, fp))
    {
      lc += 1;
      if ((*buf == ';') || (*buf == ':') || (*buf == '\n'))
        continue;               /* for comment lines */
      if ((com_cnt = sscanf (buf, "%s %s %s %s %s %s %s %s %s %s %s", func_name, com_name[0],
                             com_name[1], com_name[2], com_name[3], com_name[4], com_name[5], com_name[6], com_name[7], com_name[8], com_name[9])) < 2)
        {                       /* able to get at most 9 arguments. */
          printf ("uumkey:file %s. Line %d is illegal.\r\n", file_name, lc);
          continue;             /* every command needs at least 1 argument. */
        }

/* henkan_on is treated as like others. so REMOVE below if possible. 
    if (strcmp(func_name, "henkan_on")== 0){
      if ((com_num = expand_argument(com_name[0])) < 0) {
        fprintf(stderr,"uumkey: %s に対する %s は不適切なコードです。\r\n",
        func_name, com_name[0]);
        fflush(stderr);
      } else kk_on = com_num;
    } else 
*/
      if (strcmp (func_name, "include") == 0)
        {
          if (0 != expand_expr (com_name[0]))
            {
              /*
                 fprintf(stderr,"uumkey: %s を展開できません。\r\n", com_name[0]);
               */
              fprintf (stderr, MSG_GET (29), com_name[0]);
            }
          key_binding (com_name[0]);
        }
      else if (strcmp (func_name, "unset") == 0)
        {
          if ((k = find_entry (com_name[0])) != -1)
            {
              key_binded[k] = 0;
              remove_key_bind (k);
            }
          else if (strcmp (com_name[0], "quote_keyin") == 0)
            {
              quote_code = -1;
            }
        }
      else if (strcmp (func_name, "quote_keyin") == 0)
        {
          if ((com_num = expand_argument (com_name[0])) < 0)
            {
              fprintf (stderr, "uumkey: %s is an inappropriate code for %s.\r\n", com_name[0], func_name);
              fflush (stderr);
            }
          else
            quote_code = com_num;
        }
      else
        {
          for (k = 0; k < kansuu_hyo_entry_suu; k++)
            {
              if (strcmp (func_name, kansuu_hyo[k].kansuumei) == 0)
                {               /* found ! */
                  if (key_binded[k])
                    {
                      remove_key_bind (k);
                    }
                  key_binded[k] = 1;
                  for (kk = 0; kk < com_cnt - 1; kk++)
                    {
                      com_num = expand_argument (com_name[kk]);
                      if (com_num < 0)
                        {
                          fprintf (stderr, "uumkey: %s is an inappropriate code for %s.\r\n", com_name[kk], func_name);
                          fflush (stderr);
                        }
                      else
                        {
                          for (tbl = 0; tbl < TBL_CNT; tbl++)
                            {
                              if ((main_table[tbl][com_num] != NULL) && (kansuu_hyo[k].func[tbl] != NULL))
                                {
                                  /* fprintf(stderr, "uumkey: Duplicate Definition at key: %s.\n",com_name[kk]); */
                                  /*
                                     fprintf(stderr, "uumkey: %s(0x%x)のキーが二重定義です。at line %d (%s)(table No. %d).\r\n",
                                   */
                                  fprintf (stderr, MSG_GET (30), com_name[kk], com_num, lc, kansuu_hyo[k].kansuumei, tbl);
/*                fprintf(stderr, "lc = %d,kansuu_hyo %d(%s) ,%d , \
                     %s(0x%x)のキーが二重定義です。\n",lc,k,kansuu_hyo[k].kansuumei,tbl ,com_name[kk],
                          com_num);
*/
                                  main_table[tbl][com_num] = kansuu_hyo[k].func[tbl];
                                  romkan_clear_tbl[tbl][com_num] = kansuu_hyo[k].romkan_flag;
                                }
                              else if (kansuu_hyo[k].func[tbl] != NULL)
                                {
                                  main_table[tbl][com_num] = kansuu_hyo[k].func[tbl];
                                  romkan_clear_tbl[tbl][com_num] = kansuu_hyo[k].romkan_flag;
                                }
                            }
                        }
                    }
                  break;
                }               /* end of routine when command is founded. */

              /* loop while command is founded */
            }
          if (k == kansuu_hyo_entry_suu)
            {
              fprintf (stderr, "uumkey: unknown entry name %s.\r\n", func_name);
            }
        }
    }
  fclose (fp);
  return (0);
}

/** argument expansion. */
int
expand_argument (st)
     char *st;
{
  int num;

  if (*st == '^')
    {
      num = (*(st + 1)) & 0x1f;
    }
  else if (*st == '0' || *st == '\\')
    {
      if (*(st + 1) == 'x' || *(st + 1) == 'X')
        {
          sscanf (st + 2, "%x", &num);
        }
      else
        {
          sscanf (st, "%o", &num);
        }
    }
  else if (!('0' <= *st && *st <= '9'))
    {
      return (-1);
    }
  else
    {
      num = atoi (st);
    }
  if (num >= TBL_SIZE)
    {
      printf ("uumkey: You can only bind a function to code less than %d.\r\n", TBL_SIZE);
      return (-1);
    }
  return (num);
}

void
remove_key_bind (key)
     int key;
{
  int tbl;
  int com;

  for (com = 0; com < TBL_SIZE; com++)
    {
      for (tbl = 0; tbl < TBL_CNT; tbl++)
        {
          if (kansuu_hyo[key].func[tbl] != NULL)
            {
              if (main_table[tbl][com] == kansuu_hyo[key].func[tbl])
                {
                  main_table[tbl][com] = NULL;
                }
            }
        }
    }
}


int
find_entry (com_name)
     char *com_name;
{
  int k;

  for (k = 0; k < kansuu_hyo_entry_suu; k++)
    {
      if (strcmp (com_name, kansuu_hyo[k].kansuumei) == 0)
        {
          if (key_binded[k])
            {
              return (k);
            }
        }
    }
  return (-1);
}
