/* # skkinput (Simple Kana-Kanji Input)
 * skkmbuf.c --- mini-buffer
 * 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/Xlib.h>
#include <X11/Xatom.h>
#include <X11/IntrinsicP.h>
#include <X11/StringDefs.h>
#include <X11/Shell.h>
#include <X11/ShellP.h>

#include "commondef.h"
#include "buffers.h"
#include "skkbuf.h"
#include "skkel.h"
#include "SeparateWin.h"
#include "OverWin.h"

/* skkmarker.c */
extern void markers_init( struct SKKInputNode *node ) ;
extern int make_marker
( struct SKKInputNode *node, int *marker, int flag ) ;
/* skkel.c */
extern void j_completion_close
( struct SKKInputNode *node ) ;
/* skksvect.c */
extern void free_VectorIndex( VectorIndex **top ) ;
extern void free_SkkinpSearchVector( SkkinpSearchVector **top ) ;

/*
 * skkinput の仮名漢字変換に関する部分についての初期化を行う関数。
 *-----
 * カーソル位置の初期化とかその他。
 */
static void skkinput_SkkInputNodeInit( struct SKKInputNode *node )
{
  /* マーカー(？)を初期化する。マーカーは emacs-lisp のマーカーの実装 *
   * である。非常に怪しい実装であるが…。*/
  markers_init( node ) ;

  /* 仮名漢字変換回りでのグローバル変数の初期化。*/
  node->cur_pos            = 0 ;
  node->cur_pos2           = 0 ;
  node->cur_pos3           = 0 ;
  node->cur_pos4           = 0 ;
  node->cur_pos_backup     = 0 ;
  node->cur_pos_top        = 0 ;
  make_marker( node, &( node->cur_pos ), 1 ) ;
  make_marker( node, &( node->cur_pos2 ), 1 ) ;
  make_marker( node, &( node->cur_pos3 ), 1 ) ;
  make_marker( node, &( node->cur_pos4 ), 1 ) ;
  make_marker( node, &( node->cur_pos_backup ), 1 ) ;
  node->j_henkan_mode      = False ;
  node->j_henkan_on        = False ;
  node->j_okurigana_mode   = False ;
  node->j_kana_mode        = False ;
  node->j_katakana_mode    = False ;
  node->j_mode             = True ;
  node->j_abbrev           = False ;
  node->j_zenkaku          = False ;
  node->j_henkan_show_candidate_mode = False ;
  node->j_input_by_code_or_menu_mode = False ;
  node->j_minibuff_usage   = J_MINIBUFF_NOT_USING ;

  MYCHAR_SET_END_OF_STRING( node->textbuffer[ 0 ] ) ;
  MYCHAR_SET_END_OF_STRING( node->mtextbuffer[ 0 ] ) ;
  MYCHAR_SET_END_OF_STRING( node->j_num_list[ 0 ] ) ;
  MYCHAR_SET_END_OF_STRING( node->j_prefix[ 0 ] ) ;

  node->j_okurigana_start_point = -1 ;
  make_marker( node, &( node->j_okurigana_start_point ), 0 ) ;
  node->j_kana_start_point      = -1 ;
  make_marker( node, &( node->j_kana_start_point ), 0 ) ;
  node->j_henkan_start_point    = -1 ;
  make_marker( node, &( node->j_henkan_start_point ), 0 ) ;
  node->j_henkan_end_point      = -1 ;
  make_marker( node, &( node->j_henkan_end_point ), 0 ) ;
  node->j_input_by_code_or_menu_point = -1 ;
  make_marker( node, &( node->j_input_by_code_or_menu_point ), 0 ) ;

  /* ユーザが自由に設定できるマーカの初期化。*/
  node->mark_pos           = -1 ;
  make_marker( node, &( node->mark_pos ), 0 ) ;

  node->j_henkan_vector_top               = NULL ;
  node->j_henkan_vector_index_top         = NULL ;
  node->j_current_henkan_vector_index     = NULL ;
  node->j_henkan_vector_index_length      = 0 ;

  node->j_completion_vector_top           = NULL ;
  node->j_completion_vector_index_top     = NULL ;
  node->j_current_completion_vector_index = NULL ;
  node->j_completion_mode                 = False ;

  node->prev_cx = node->prev_cy = -1 ;
  node->prev_start_pos     = 0 ;

  return ;
}

/*
 * minibuffer 用のノードを新しく確保する関数。
 */
struct SKKInputNode *skkinput_AllocateMinibuffer( void )
{
  struct SKKInputNode *node ;

  node = ( struct SKKInputNode *)malloc( sizeof( struct SKKInputNode ) ) ;
  if( node == NULL ){
    fprintf( stderr, "Fatal error .... Memory exhausted.\n" ) ;
    exit( 1 ) ;
  }
  node->parentbuffer = NULL ;
  node->minibuffer   = NULL ;
  skkinput_SkkInputNodeInit( node ) ;
  return node ;
}

/*
 * mini-buffer を解放する関数。
 */
void free_Minibuffer
( Widget gw, struct skkinputBuffer *buffer, struct SKKInputNode *node )
{
  if( node->parentbuffer != NULL ){
    /* 親の子供は自分の子供。*/
    ( node->parentbuffer )->minibuffer = node->minibuffer ;
  } else {
    /* 一番最初を常に指すようにしなければならないので、先頭が解放さ *
     * れたら修正。*/
    buffer->topbuffer = node->minibuffer ;
  }
  /* ありえないと思うけど、自分に子供があったら、その子供の親は自分 *
   * の親。*/
  if( node->minibuffer != NULL ){
    ( node->minibuffer )->parentbuffer = node->parentbuffer ;
    /* このノードが一番子供じゃないのだから、w->skkinput.lastbuffer を修 *
     * 正する必要はない。*/
  } else {
    /* 一番最後を常に指すようにしておく。*/
    buffer->lastbuffer = node->parentbuffer ;
  }
  /* 変換終了処理を呼出す。*/
  free_VectorIndex( &( node->j_henkan_vector_index_top ) ) ;
  free_SkkinpSearchVector( &( node->j_henkan_vector_top ) ) ;
  node->j_current_henkan_vector_index = NULL ;
  /* コンプリーションの途中であったなら、そのメモリを解放する。*/
  j_completion_close( node ) ;
  /* 自分を解放する。*/
  free( node ) ;
}

/*
 * ミニバッファ無いでの仮名漢字変換を行うための初期化を行う関数。
 */
int skkinput_j_henkan_in_minibuff_init
( Widget gw, struct skkinputBuffer *sbuffer, struct SKKInputNode *node )
{
  struct SKKInputNode *mNode ;

#ifdef DEBUG
  /* 渡されたバッファ内にカーソルが無い、もしくは、そこに既に変換用の *
   * ミニバッファがある…のは変である。あってはならない事態であろう。 */
  if( !node->cur_exist || node->minibuffer != NULL ){
    fprintf( stderr, "*** Warning *** : I miss your cursor.\n" ) ;
    return ERR ;
  }
#endif
  /* 新しくバッファを確保する。*/
  mNode = skkinput_AllocateMinibuffer() ;
  mNode->parentbuffer = node ;
  node->minibuffer = sbuffer->lastbuffer = mNode ;

  /* バッファがあふれないようにキーをコピーする。*/
  if( !IS_END_OF_STRING( node->j_num_list[ 0 ] ) ){
    myCharStrncpy
      ( mNode->textbuffer, node->j_search_key, TEXTMAXLEN - 1 ) ;
  } else {
    myCharStrncpy
      ( mNode->textbuffer, node->j_henkan_key2, TEXTMAXLEN - 1 ) ;
  }
  MYCHAR_SET_END_OF_STRING( mNode->textbuffer[ TEXTMAXLEN - 1 ] ) ;
  myCharCharStrcat( mNode->textbuffer, " " ) ;
  MYCHAR_SET_END_OF_STRING( mNode->textbuffer[ TEXTMAXLEN ] ) ;

  /* 初期カーソル位置及びカーソルの最小位置の設定。*/
  mNode->cur_pos_top = mNode->cur_pos = myCharStrlen( mNode->textbuffer ) ;

  /* バッファ間でのカーソル移動。*/
  node->cur_exist  = False ;
  mNode->cur_exist = True ;
  /* 下の j-read-string と区別するのに必要。親に対する action が違う。*/
  mNode->j_minibuff_usage = J_HENKAN_IN_MINIBUFF_MODE ;

  /* ミニバッファ内に以前表示されていたものを消去する。*/
  XtVaSetValues( gw, XtNclearMinibuffer, True, NULL ) ;
  return 0 ;
}

/*
 * minibuffer から文字列を入力させる関数。
 *---------
 * 文字列の入力が終了すると、この関数へ longjmp で戻ってくるのでその時
 * に True が返るから呼出し側が文字列の入力が完了したのだと分かる。
 * 入力された文字列は temporary_buffer に一時的に保存されるので、戻っ
 * てきたら可及的速やかに使ってしまうか別のところに保存しましょう。
 */
int j_read_string
( Widget gw, struct skkinputBuffer *sbuffer,
  struct SKKInputNode *node, unsigned char *string, int minibuff_usage )
{
  struct SKKInputNode *mNode ;
  int length = strlen( string ) ;

#ifdef DEBUG
  /* 渡されたバッファ内にカーソルが無い、もしくは、そこに既に変換用の *
   * ミニバッファがある…のは変である。あってはならない事態であろう。 */
  if( !node->cur_exist || node->minibuffer != NULL ){
    fprintf( stderr, "*** Warning *** : I miss your cursor.\n" ) ;
    return ERR ;
  }
#endif
  /* 新しくバッファを確保する。*/
  mNode = skkinput_AllocateMinibuffer() ;
  mNode->parentbuffer = node ;
  node->minibuffer = sbuffer->lastbuffer = mNode ;

  /* バッファがあふれないようにキーをコピーする。*/
  if( length > TEXTMAXLEN )
    length = TEXTMAXLEN ;
  myCharCharStrncpy( mNode->textbuffer, string, length ) ;
  MYCHAR_SET_END_OF_STRING( mNode->textbuffer[ length ] ) ;

  /* 初期カーソル位置及びカーソルの最小位置の設定。*/
  mNode->cur_pos_top = mNode->cur_pos = myCharStrlen( mNode->textbuffer ) ;

  /* バッファ間でのカーソル移動。*/
  node->cur_exist  = False ;
  mNode->cur_exist = True ;
  mNode->j_minibuff_usage = minibuff_usage ;

  /* このモードでは、仮名入力モードはデフォルトでオフ。*/
  mNode->j_mode = False ;

  /* ミニバッファ内に以前表示されていたものを消去する。*/
  XtVaSetValues( gw, XtNclearMinibuffer, True, NULL ) ;
  return 0 ;
}					
