/* # skkinput (Simple Kana-Kanji Input)
 * keymap.c --- Look up at keymap.
 * This file is part of skkinput.
 * Copyright (C) 1997
 * Takashi SAKAMOTO (sakamoto@yajima.kuis.kyoto-u.ac.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, 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 skkinput; see the file COPYING.  If not, write to
 * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <X11/X.h>

#include "commondef.h"
#include "buffers.h"
#include "config.h"
#include "skkkey.h"
#include "defaultKeyMap.h"

/*
 * プロトタイプ宣言。
 */
static struct skkinputKeyTable *skkinput_AllocKeyTable
( int func_no ) ;
static int skkinput_CompileOneKeyBindWithInternalChar
( struct skkinputKeyTable *top,
  struct myChar *string, int length, int function_no ) ;
static int skkinput_UndefineKeySub
( struct skkinputKeyTable *top, struct myChar *string, int length ) ;

int skkinput_CompileDefaultKeyBind
( void ) ;
struct skkinputKeyTable *skkinput_CompileKeyBind
( struct skkinputKeyBind *tabletop, int func_no ) ;
int skkinput_CompileOneKeyBind
( struct skkinputKeyTable *top, unsigned char *string,
  int length, int function_no ) ;
void skkinput_FreeKeyTable
( struct skkinputKeyTable *node ) ;
int lookup_at_keymap
( struct keyBuffer *keybuf, struct SKKInputNode *node ) ;
int skkinput_DefineKey
( struct myChar *string, int length, int func_no, int map ) ;
int skkinput_UndefineKey
( struct myChar *string, int length, int map ) ;

/*
 * 通常の仮名漢字入力時のキーマップ。
 *----
 * コントロールの部分は、全角半角入力の時と共通とする。
 */
#ifdef THIS_IS_NECESSARY
static struct skkinputKeyTable *skkinput_skkmap ;
#endif
static struct skkinputKeyTable *skkinput_skkjmap ;
static struct skkinputKeyTable *skkinput_skkabbrevmap ;

/*
 * デフォルトのキーマップを登録するのに利用する関数。
 *-----
 * 起動時に一回すれば十分である。
 */
int skkinput_CompileDefaultKeyBind
( void )
{
  /* 順番にマップ毎のキーバインドをコンパイルしていく…実は凄い手抜き
   * だけど…。本当にメモリを節約する気があるのなら、かなり重なってい
   * る部分があるので、大元のバインドを設定してから継承(？)していくよ
   * うにするべきである。*/
#ifdef THIS_IS_NECESSARY
  skkinput_skkmap       = skkinput_CompileKeyBind
    ( defaultSkkMapBind,    FUNCNO_SELF_INSERT_CHARACTER ) ;
#endif
  skkinput_skkjmap      = skkinput_CompileKeyBind
    ( defaultSkkJMapBind,   FUNCNO_J_SELF_INSERT ) ;
  skkinput_skkabbrevmap = skkinput_CompileKeyBind
    ( defaultAbbrevMapBind, FUNCNO_SELF_INSERT_CHARACTER ) ;
  return True ;
}

struct skkinputKeyTable *skkinput_CompileKeyBind
( struct skkinputKeyBind *tabletop, int func_no )
{
  struct skkinputKeyBind *ptr ;
  struct skkinputKeyTable *top ;

  /* 一番上のキーマップだけはマップ毎に指定しておく。*/
  if( ( top = skkinput_AllocKeyTable( func_no ) ) == NULL )
    return NULL ;
  for( ptr = tabletop ; ptr->length > 0 ; ptr ++ ){
    if( !skkinput_CompileOneKeyBind
	( top, ptr->string, ptr->length, ptr->function_no ) ){
      /* 登録に失敗したら、キーテーブルを解放して返す。*/
      skkinput_FreeKeyTable( top ) ;
      return NULL ;
    }
  }
  return top ;
}

int skkinput_CompileOneKeyBind
( struct skkinputKeyTable *top,
  unsigned char *string, int length, int function_no )
{
  struct skkinputKeyTable *node, *newNode ;
  int i ;
  if( top == NULL )
    return False ;
  length -- ;
  i    = 0 ;
  node = top ;
  /* 文字列の長さに応じて順番にもぐって行く。*/
  while( i < length ){
    if( node->next[ ( int )string[ i ] ] == NULL ){
      if( ( newNode = skkinput_AllocKeyTable
	    ( FUNCNO_INVALID_CHAR ) ) == NULL )
	return False ;
      node->next[ ( int )string[ i ] ] = newNode ;
      node    = newNode ;
    } else {
      node = node->next[ ( int )string[ i ] ] ;
    }
    i ++ ;
  }
  /* 機能を割り当てる。*/
  node->function[ ( int )string[ i ] ] = function_no ;
  return True ;
}

static struct skkinputKeyTable *skkinput_AllocKeyTable
( int func_no )
{
  struct skkinputKeyTable *node ;
  int i ;
  if( ( node = malloc( sizeof( struct skkinputKeyTable ) ) ) == NULL )
    return NULL ;
  for( i = 0 ; i < NUMBER_OF_KEYS ; i ++ )
    node->next[ i ] = NULL ;
  i = 0 ;
  /* デフォルトの機能の初期化。通常コントロールキーに対しては何の機能
   * も割り当てられるべきではないと考えて、INVALID_CHAR を選択。それ以
   * 外の文字は、その文字を挿入する…を選択する。*/
  while( i < 0x20 )
    node->function[ i ++ ] = FUNCNO_INVALID_CHAR ;
  while( i < 0x7F )
    node->function[ i ++ ] = func_no ;
  while( i < NUMBER_OF_KEYS )
    node->function[ i ++ ] = FUNCNO_INVALID_CHAR ;
  return node ;
}

void skkinput_FreeKeyTable( struct skkinputKeyTable *node )
{
  int i ;
  if( node == NULL )
    return ;
  /* まず子供を解放することから考える。*/
  for( i = 0 ; i < NUMBER_OF_KEYS ; i ++ ){
    if( node->next[ i ] != NULL )
      skkinput_FreeKeyTable( node->next[ i ] ) ;
  }
  /* 全ての子供が解放されたことが分かれば、次に自分自身を解放して終了
   * する。*/
  free( node ) ;
  return ;
}

/*
 * キーテーブルを参照する関数。
 */
int lookup_at_keymap
( struct keyBuffer *keybuf, struct SKKInputNode *skkNode )
{
  struct skkinputKeyTable *table, *node ;
  int prev_function, function, i ;

  prev_function = FUNCNO_NOFUNCTION ;
  if( !skkNode->j_mode ){
#ifdef THIS_IS_NECESSARY
    /* 「SKK モード」と「全英モード」のキーマップ。*/
    table = skkinput_skkmap ;
#else
    if( keybuf->buffer[ keybuf->usage - 1 ] >= 0x20 &&
	keybuf->buffer[ keybuf->usage - 1 ] <  0x7F ){
      if( skkNode->j_zenkaku ){
	return FUNCNO_J_SELF_ZENKAKU_INSERT ;
      } else {
	return FUNCNO_SELF_INSERT_CHARACTER ;
      }
    }
    /* 「かなモード」と「カナモード」のキーマップを使う。*/
    table = skkinput_skkjmap ;
#endif
  } else if( skkNode->j_abbrev && !skkNode->j_henkan_on ){
    /* 「ABBREVモード」のキーマップ。*/
    table = skkinput_skkabbrevmap ;
  } else {
    /* 「かなモード」と「カナモード」のキーマップ。*/
    table = skkinput_skkjmap ;
  }
  node     = table ;
  function = FUNCNO_NOFUNCTION ;
  for( i = 0 ; i < ( keybuf->usage - 1 ) ; i ++ ){
    if( keybuf->buffer[ i ] >= NUMBER_OF_KEYS )
      return FUNCNO_NOFUNCTION ;
    if( node->next[ ( int )keybuf->buffer[ i ] ] == NULL ){
      if( prev_function == FUNCNO_J_SENDBACK_NEXT_KEY ){
	return FUNCNO_J_SENDBACK_KEY ;
      }
      return FUNCNO_NOFUNCTION ;
    }
    /* 次の機能を読みに行く。*/
    prev_function = node->function[ ( int )keybuf->buffer[ i ] ] ;
    node = node->next[ ( int )keybuf->buffer[ i ] ] ;
  }
  if( keybuf->buffer[ i ] < NUMBER_OF_KEYS ){
    function = node->function[ ( int )keybuf->buffer[ i ] ] ;
  }
#ifdef THIS_IS_NECESSARY
  if( !skkNode->j_mode &&
      function == FUNCNO_SELF_INSERT_CHARACTER &&
      skkNode->j_zenkaku ){
    function = FUNCNO_J_SELF_ZENKAKU_INSERT ;
  }
#endif
  return function ;
}

/*
 * とにもかくにもキー定義を行ってくれる関数。
 */
int skkinput_DefineKey
( struct myChar *string, int length, int func_no, int map )
{
  int ret ;
  switch( map ){
#ifdef THIS_IS_NECESSARY
  case 0 :
    ret = skkinput_CompileOneKeyBindWithInternalChar
      ( skkinput_skkmap, string, length, func_no ) ;
    break ;
#endif
  case 1 :
    ret = skkinput_CompileOneKeyBindWithInternalChar
      ( skkinput_skkjmap, string, length, func_no ) ;
    break ;
  case 2 :
    ret = skkinput_CompileOneKeyBindWithInternalChar
      ( skkinput_skkabbrevmap, string, length, func_no ) ;
    break ;
  default :
    ret = False ;
    break ;
  }
  return ret ;
}

/*
 * 指定された文字列に対する機能を無効にする関数。
 */
int skkinput_UndefineKey
( struct myChar *string, int length, int map )
{
  int ret ;
  switch( map ){
#ifdef THIS_IS_NECESSARY
  case 0 :
    ret = skkinput_UndefineKeySub
      ( skkinput_skkmap, string, length ) ;
    break ;
#endif
  case 1 :
    ret = skkinput_UndefineKeySub
      ( skkinput_skkjmap, string, length ) ;
    break ;
  case 2 :
    ret = skkinput_UndefineKeySub
      ( skkinput_skkabbrevmap, string, length ) ;
    break ;
  default :
    ret = False ;
    break ;
  }
  return ret ;
}

static int skkinput_CompileOneKeyBindWithInternalChar
( struct skkinputKeyTable *top,
  struct myChar *string, int length, int function_no )
{
  struct skkinputKeyTable *node, *newNode ;
  struct myChar *ptr ;
  int i ;
  if( ( node = top ) == NULL )
    return False ;
  length -- ;
  i   = 0 ;
  ptr = string ;
  /* 文字列の長さに応じて順番にもぐって行く。*/
  while( i < length ){
    if( ptr->charset != CHARSET_ASCII ||
	ptr->chara >= NUMBER_OF_KEYS )
      return False ;
    if( node->next[ ( int )ptr->chara ] == NULL ){
      if( ( newNode = skkinput_AllocKeyTable
	    ( FUNCNO_INVALID_CHAR ) ) == NULL )
	return False ;
      node->next[ ( int )ptr->chara ] = newNode ;
      node    = newNode ;
    } else {
      node = node->next[ ( int )ptr->chara ] ;
    }
    i   ++ ;
    ptr ++ ;
  }
  if( ptr->charset != CHARSET_ASCII ||
      ptr->chara >= NUMBER_OF_KEYS )
    return False ;
  /* 機能を割り当てる。*/
  node->function[ ( int )ptr->chara ] = function_no ;
  return True ;
}

static int skkinput_UndefineKeySub
( struct skkinputKeyTable *top, struct myChar *string, int length )
{
  struct myChar *ptr ;
  struct skkinputKeyTable *node ;
  int i ;
  if( top == NULL )
    return False ;
  length -- ;
  i    = 0 ;
  ptr  = string ;
  node = top ;
  /* 文字列の長さに応じて順番にもぐって行く。*/
  while( i < length ){
    if( ptr->charset != CHARSET_ASCII ||
	ptr->chara >= NUMBER_OF_KEYS )
      return False ;
    if( node->next[ ( int )ptr->chara ] == NULL ){
      /* もともとそんなキーバインドは無いので、undefine も何もない。*/
      return False ;
    } else {
      node = node->next[ ( int )ptr->chara ] ;
    }
    i   ++ ;
    ptr ++ ;
  }
  if( ptr->charset != CHARSET_ASCII ||
      ptr->chara >= NUMBER_OF_KEYS )
    return False ;
  /* 機能を割り当てる。*/
  node->function[ ( int )ptr->chara ] = FUNCNO_NOFUNCTION ;
  skkinput_FreeKeyTable( node->next[ ( int )ptr->chara ] ) ;
  return True ;
}

void skkinput_initKeyBuffer( struct keyBuffer *keybuf )
{
  keybuf->usage  = 0 ;
  keybuf->size   = KEYBUFSIZE ;
  keybuf->buffer = keybuf->internal ;
  return ;
}

void skkinput_closeKeyBuffer( struct keyBuffer *keybuf )
{
  if( keybuf->buffer != keybuf->internal )
    free( keybuf->buffer ) ;
  keybuf->buffer = keybuf->internal ;
  keybuf->usage  = 0 ;
  keybuf->size   = KEYBUFSIZE ;
  return ;
}

void skkinput_addKeyBuffer( struct keyBuffer *keybuf, int chara )
{
  char *newbuf ;
  int newsize ;
  if( keybuf->usage >= ( keybuf->size - 1 ) ){
    newsize = keybuf->usage + KEYBUFSIZE ;
    /* malloc に失敗したら新しく入力された文字なんて無視！ */
    if( ( newbuf = malloc( newsize ) ) == NULL )
      return ;
    keybuf->size = newsize ;
    memcpy( newbuf, keybuf->buffer, keybuf->usage ) ;
    if( keybuf->buffer != keybuf->internal ){
      free( keybuf->buffer ) ;
    }
    newbuf[ keybuf->usage ++ ] = chara ;
    keybuf->buffer = newbuf ;
  } else {
    keybuf->buffer[ keybuf->usage ++ ] = chara ;
  }
  return ;
}
