/*
 * Anthyのキーの受け付けやプリエディットの制御を行うレイヤー
 *
 * 今からアプリケーションを書く場合にはuimの利用をお薦めします。
 *
 * Funded by IPA未踏ソフトウェア創造事業 2002 1/23
 * Copyright (C) 2001-2002 UGAWA Tomoharu
 *
 * $Id: input.c,v 1.25 2002/11/16 03:35:21 yusuke Exp $
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>

#include <anthy/anthy.h>
#include <anthy/input.h>

#include "rkconv.h"
#include "rkhelper.h"

struct anthy_input_context {
  /* ANTHY_INPUT_ST_* */
  int state;

  /* always allocated */
  struct rk_conv_context* rkctx;
  int map_no;  /* RKMAP_* */
  /* 変換する文字列のバッファ*/
  char* hbuf;
  int n_hbuf;
  int s_hbuf;
  char* hbuf_follow;
  int n_hbuf_follow;
  int s_hbuf_follow;

  /* allocated only in conv state */
  anthy_context_t actx;
  struct a_segment* segment;
  struct a_segment* cur_segment;
  int enum_cand_count;
  int enum_cand_limit;
  int enum_reverse;
  int last_gotten_cand;

  /* always allocated by the library */
  /* コミットバッファ */
  char* commit;
  int n_commit;
  int s_commit;

  /* always allocated by the library */
  /* カットバッファ */
  char* cut;
  int n_cut;
  int s_cut;

  struct anthy_input_config* cfg;
  struct anthy_input_context* next_cfg_owner;
};

int anthy_input_errno;

#define DEFAULT_ENUM_CAND_LIMIT 3

#define MAX(a,b) ((a) > (b) ? (a) : (b))

#define is_eucchar(s)  (((s)[0] & 0x80) && ((s)[1] & 0x80))


struct anthy_input_config {
  struct rk_option* rk_option;
  /* 6はsrc-util/rkhelper.h の NR_RKMAPに相当 */
  struct rk_map*   rk_map[6];
  struct anthy_input_context* owners;
  /**/
  int break_into_roman;
  int preedit_mode;
};

struct a_segment {
  int index;
  int pos;
  struct anthy_segment_stat ass;
  int cand;
  struct a_segment* next, * prev;
};

static int
ensure_buffer(char** buf, int* size, int to_size)
{
  if (*size < to_size) {
    *buf = (char*) realloc(*buf, to_size);
    if (*buf == NULL) {
      anthy_input_errno = AIE_NOMEM;
      return -1;
    }
    *size = to_size;
  }
  return 0;
}

static void
leave_edit_state(struct anthy_input_context* ictx)
{
  /* do noting */
  (void) ictx;
}


static void
enter_none_state(struct anthy_input_context* ictx)
{
  ictx->state = ANTHY_INPUT_ST_NONE;
}

static void
enter_edit_state(struct anthy_input_context* ictx)
{
  ictx->state = ANTHY_INPUT_ST_EDIT;
  rk_flush(ictx->rkctx);
  rk_select_registered_map(ictx->rkctx, ictx->map_no);
  ictx->n_hbuf = 0;
  ictx->n_hbuf_follow = 0;
}

static void
enter_edit_state_noinit(struct anthy_input_context* ictx)
{
  ictx->state = ANTHY_INPUT_ST_EDIT;
}

static void
leave_conv_state(struct anthy_input_context* ictx)
{
  struct a_segment* as, * next;
  anthy_release_context(ictx->actx);
  for (as = ictx->segment; as; as = next) {
    next = as->next;
    free(as);
  }
  anthy_reset_context(ictx->actx);
}

static void
reset_anthy_input_context(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_NONE:
    break;
  case ANTHY_INPUT_ST_EDIT:
    leave_edit_state(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    leave_conv_state(ictx);
    break;
  }
  enter_none_state(ictx);
}

static void
read_rk_result(struct anthy_input_context* ictx)
{
  int ret;

  ret = rk_result(ictx->rkctx, ictx->hbuf + ictx->n_hbuf,
		  ictx->s_hbuf - ictx->n_hbuf);
  if (ret > 0) {
    if (ictx->s_hbuf - ictx->n_hbuf > 0)
      ictx->n_hbuf = ictx->s_hbuf - 1;
    
    ensure_buffer(&ictx->hbuf, &ictx->s_hbuf, ictx->n_hbuf + ret + 1);
    
    rk_result(ictx->rkctx, ictx->hbuf + ictx->n_hbuf, 
	      ictx->s_hbuf - ictx->n_hbuf);
  }
  if (ictx->hbuf)
    ictx->n_hbuf += strlen(ictx->hbuf + ictx->n_hbuf);
}

static void
terminate_rk(struct anthy_input_context* ictx)
{
  rk_terminate(ictx->rkctx);
  read_rk_result(ictx);
  rk_flush(ictx->rkctx);
}

static void
join_noconv_string(struct anthy_input_context* ictx)
{
  if (ictx->n_hbuf_follow > 0) {
    ensure_buffer(&ictx->hbuf, &ictx->s_hbuf, 
		  ictx->n_hbuf + ictx->n_hbuf_follow);
    memcpy(ictx->hbuf + ictx->n_hbuf, ictx->hbuf_follow, ictx->n_hbuf_follow);
    ictx->n_hbuf += ictx->n_hbuf_follow;
    ictx->n_hbuf_follow = 0;
  }
}

static void
enter_conv_state(struct anthy_input_context* ictx)
{
  int ret;
  struct anthy_conv_stat acs;
  struct a_segment* as_tail, ** as_tailp;
  int i;
  int last_pos;

  ictx->state = ANTHY_INPUT_ST_CONV;

  terminate_rk(ictx);

  join_noconv_string(ictx);

  if (ictx->n_hbuf == 0) {
    ensure_buffer(&ictx->commit, &ictx->s_commit, ictx->n_commit + 1);
    ictx->commit[ictx->n_commit++] = ' ';
    enter_none_state(ictx);
    return;
  }

  ensure_buffer(&ictx->hbuf, &ictx->s_hbuf, ictx->n_hbuf + 1);
  ictx->hbuf[ictx->n_hbuf] = '\0';

  ictx->enum_cand_count = 0;
  ictx->actx = anthy_create_context();
  anthy_context_set_encoding(ictx->actx, ANTHY_EUC_JP_ENCODING);
  if (!ictx->actx) {
    enter_none_state(ictx);
    anthy_input_errno = AIE_NOMEM;
    return;
  }
  anthy_reset_context(ictx->actx);
  ret = anthy_set_string(ictx->actx, ictx->hbuf);
  if (ret < 0) {
    anthy_release_context(ictx->actx);
    enter_none_state(ictx);
    return;
  }

  anthy_get_stat(ictx->actx, &acs);
  as_tail = NULL;
  as_tailp = &ictx->segment;
  last_pos = 0;
  for (i = 0; i < acs.nr_segment; i++) {
    struct a_segment* as;
    as = (struct a_segment*) malloc(sizeof(struct a_segment));
    as->index = i;
    as->pos = last_pos;
    anthy_get_segment_stat(ictx->actx, i, &as->ass);
    last_pos += as->ass.seg_len;
    as->cand = 0;
    as->prev = as_tail;
    *as_tailp = as;
    as->next = NULL;
    as_tailp = &as->next;
    as_tail = as;
  }
  ictx->cur_segment = ictx->segment;
  ictx->last_gotten_cand = 0;
}

static void
enter_conv_state_noinit(struct anthy_input_context* ictx)
{
  ictx->state = ANTHY_INPUT_ST_CONV;
}

static void
enter_cseg_state(struct anthy_input_context* ictx)
{
  ictx->state = ANTHY_INPUT_ST_CSEG;
  ictx->enum_cand_count = 0;
}

static void
leave_cseg_state(struct anthy_input_context* ictx)
{
  /* do nothing */
  (void)ictx;
}

static int
cmdh_map_select(struct anthy_input_context* ictx, int map)
{
  switch (map) {
  case ANTHY_INPUT_MAP_ALPHABET:
    ictx->map_no = RKMAP_ASCII;
    break;
  case ANTHY_INPUT_MAP_WALPHABET:
    ictx->map_no = RKMAP_WASCII;
    break;
  case ANTHY_INPUT_MAP_HIRAGANA:
    ictx->map_no = RKMAP_HIRAGANA;
    break;
  case ANTHY_INPUT_MAP_KATAKANA:
    ictx->map_no = RKMAP_KATAKANA;
    break;
  case ANTHY_INPUT_MAP_HANKAKU_KANA:
    ictx->map_no = RKMAP_HANKAKU_KANA;
    break;
  default:
    anthy_input_errno = AIE_INVAL;
    return -1;
  }

  rk_select_registered_map(ictx->rkctx, ictx->map_no);

  return 0;
}

static struct anthy_input_segment* 
cmdh_get_candidate(struct anthy_input_context* ictx, int cand_no)
{
  struct a_segment* cs;
  struct anthy_input_segment* seg;
  int len;

  cs = ictx->cur_segment;
  if (cand_no >= cs->ass.nr_candidate) {
    anthy_input_errno = AIE_INVAL;
    return NULL;
  }
  ictx->last_gotten_cand = cand_no;

  seg = (struct anthy_input_segment*) 
    malloc(sizeof(struct anthy_input_segment));
  len = anthy_get_segment(ictx->actx, cs->index, cand_no, NULL, 0);
  seg->str = (char*) malloc(len + 1);
  anthy_get_segment(ictx->actx, cs->index, cand_no, seg->str, len + 1);
  seg->cand_no = cand_no;
  seg->noconv_len = anthy_get_segment(ictx->actx, cs->index, 
				      NTH_UNCONVERTED_CANDIDATE, NULL, 0);
  seg->nr_cand = cs->ass.nr_candidate;
  seg->flag = ANTHY_INPUT_SF_CURSOR;
  if (ictx->enum_cand_count >= ictx->enum_cand_limit)
	  seg->flag |= (ictx->enum_reverse ?
			ANTHY_INPUT_SF_ENUM_REVERSE : ANTHY_INPUT_SF_ENUM);

  return seg;
}

static void
do_cmd_commit(struct anthy_input_context* ictx)
{
  struct a_segment* as;

  for (as = ictx->segment; as; as = as->next) {
    int len;
    
    len = anthy_get_segment(ictx->actx, as->index, as->cand, NULL, 0);
    ensure_buffer(&ictx->commit, &ictx->s_commit, ictx->n_commit + len + 1);
    anthy_get_segment(ictx->actx, as->index, as->cand, 
		      ictx->commit + ictx->n_commit, len + 1);
    ictx->n_commit += len;
    anthy_commit_segment(ictx->actx, as->index, as->cand);
  }
}

static int
cmdh_select_candidate(struct anthy_input_context* ictx, 
		      int cand_no)
{
  struct a_segment* cs;

  cs = ictx->cur_segment;
  if (cand_no >= cs->ass.nr_candidate) {
    anthy_input_errno = AIE_INVAL;
    return -1;
  }
  cs->cand = cand_no;
  
  if (cs->next) {
    ictx->cur_segment = cs->next;
    ictx->last_gotten_cand = ictx->cur_segment->cand;
    ictx->enum_cand_count = 0;
  } else {
    ictx->last_gotten_cand = ictx->cur_segment->cand;
    ictx->enum_cand_count = 0;
  }

  return 0;
}

static void
do_cmd_push_key(struct anthy_input_context* ictx, const char* str)
{
  const char* p;

  for (p = str; *p; p++) {
    if (isspace((int)(unsigned char) *p) && *p != ' ')
      continue;

    rk_push_key(ictx->rkctx, *p);
    read_rk_result(ictx);
  }
}

static void
cmd_push_key(struct anthy_input_context* ictx, const char* str)
{
  do_cmd_push_key(ictx, str);
}

static void
cmd_move_cursor(struct anthy_input_context* ictx, int d)
{
  if (rk_get_pending_str(ictx->rkctx, NULL, 0) > 1) {
    rk_flush(ictx->rkctx);
    return;
  }

  if (d > 0) {
    char* p;
    int len;
    if (ictx->n_hbuf_follow == 0)
      return;
    for (p = ictx->hbuf_follow; 
	 p < ictx->hbuf_follow + ictx->n_hbuf_follow && d > 0; p++, d--) {
      if (p < ictx->hbuf_follow + ictx->n_hbuf_follow - 1 && is_eucchar(p))
	p++;
    }
    len = p - ictx->hbuf_follow;
    ensure_buffer(&ictx->hbuf, &ictx->s_hbuf, ictx->n_hbuf + len);
    memcpy(ictx->hbuf + ictx->n_hbuf, ictx->hbuf_follow, len);
    ictx->n_hbuf += len;
    ictx->n_hbuf_follow -= len;
    memmove(ictx->hbuf_follow, p, ictx->n_hbuf_follow);
  } else {
    char* p;
    int len;
    if (ictx->n_hbuf == 0)
      return;
    for (p = ictx->hbuf + ictx->n_hbuf; 
	 p > ictx->hbuf && d < 0; p--, d++) {
      if (p - 1 > ictx->hbuf && is_eucchar(p - 2))
	p--;
    }
    len = (ictx->hbuf + ictx->n_hbuf) - p;
    ensure_buffer(&ictx->hbuf_follow, &ictx->s_hbuf_follow, 
		  ictx->n_hbuf_follow + len);
    if (ictx->n_hbuf_follow > 0)
      memmove(ictx->hbuf_follow + len, ictx->hbuf_follow, ictx->n_hbuf_follow);
    memcpy(ictx->hbuf_follow, p, len);
    ictx->n_hbuf_follow += len;
    ictx->n_hbuf -= len;
  }
}

static void
cmd_backspace(struct anthy_input_context* ictx)
{
  int len;

  len = rk_get_pending_str(ictx->rkctx, NULL, 0);
  if (len > 1) {
    char* buf;
    /* 確定されていないローマ字があるので、最後の文字をカット */
    len--;

    buf = (char*) malloc(len);
    rk_get_pending_str(ictx->rkctx, buf, len);
    rk_flush(ictx->rkctx);
    do_cmd_push_key(ictx, buf);    
    free(buf);
  } else {
    if (brk_roman_get_previous_pending(ictx->rkctx)) {
      char *buf;
      buf = strdup(brk_roman_get_previous_pending(ictx->rkctx));
      ictx->n_hbuf -= brk_roman_get_decided_len(ictx->rkctx);

      rk_flush(ictx->rkctx);
      do_cmd_push_key(ictx,buf);
      free(buf);
    } else {
      if (ictx->n_hbuf >= 2 && is_eucchar(ictx->hbuf + ictx->n_hbuf - 2)) {
	ictx->n_hbuf -= 2;
      } else if (ictx->n_hbuf >= 1) {
	ictx->n_hbuf--;
      }
    }
  }

  if (ictx->n_hbuf + ictx->n_hbuf_follow <= 0 && len <= 1) {
    leave_edit_state(ictx);
    enter_none_state(ictx);
  }
}

static void
cmd_delete(struct anthy_input_context* ictx)
{
  int len;

  if (rk_get_pending_str(ictx->rkctx, NULL, 0) > 1)
    return;
  if (ictx->n_hbuf_follow <= 0)
    return;

  len = ictx->n_hbuf_follow >= 2 && is_eucchar(ictx->hbuf_follow) ? 2 : 1;

  if (ictx->n_hbuf_follow <= len)
    ictx->n_hbuf_follow = 0;
  else {
    ictx->n_hbuf_follow -= len;
    memmove(ictx->hbuf_follow, ictx->hbuf_follow + len, ictx->n_hbuf_follow);
  }

  if (ictx->n_hbuf + ictx->n_hbuf_follow <= 0) {
    leave_edit_state(ictx);
    enter_none_state(ictx);
  }
}

static void
cmd_commit_unconv(struct anthy_input_context* ictx)
{
  ensure_buffer(&ictx->commit, &ictx->s_commit, 
		ictx->n_commit + ictx->n_hbuf + ictx->n_hbuf_follow);
  memcpy(ictx->commit + ictx->n_commit, ictx->hbuf, ictx->n_hbuf);
  ictx->n_commit += ictx->n_hbuf;
  if (ictx->n_hbuf_follow > 0)
    memcpy(ictx->commit + ictx->n_commit, 
	   ictx->hbuf_follow, ictx->n_hbuf_follow);
  ictx->n_commit += ictx->n_hbuf_follow;
}

static void
cmd_resize(struct anthy_input_context* ictx, int d)
{
  int i;
  struct anthy_conv_stat acs;
  struct a_segment* as;
  int last_pos;

  anthy_resize_segment(ictx->actx, ictx->cur_segment->index, d);
  anthy_get_stat(ictx->actx, &acs);

  anthy_get_segment_stat(ictx->actx, 
			 ictx->cur_segment->index, &ictx->cur_segment->ass);
  ictx->cur_segment->cand = NTH_UNCONVERTED_CANDIDATE;
  last_pos = ictx->cur_segment->ass.seg_len;
  for (as = ictx->cur_segment, i = as->index + 1; i < acs.nr_segment; i++) {
    if (as->next == NULL) {
      struct a_segment* as2;
      
      as2 = (struct a_segment*) malloc(sizeof(struct a_segment));
      as2->index = i;
      as2->prev = as;
      as->next = as2;
      as2->next = NULL;
      as = as2;
    } else 
      as = as->next;
    as->pos = last_pos;
    anthy_get_segment_stat(ictx->actx, i, &as->ass);
    last_pos += as->ass.seg_len;
    as->cand = NTH_UNCONVERTED_CANDIDATE;
  }
  ictx->last_gotten_cand = NTH_UNCONVERTED_CANDIDATE;

  for (as = as->next; as; ) {
    struct a_segment* next;
    next = as->next;
    as->prev->next = NULL;
    free(as);
    as = next;
  }
}

static void
commit_noconv_string(struct anthy_input_context* ictx)
{
  join_noconv_string(ictx);
  ensure_buffer(&ictx->commit, &ictx->s_commit, 
		ictx->n_commit + ictx->n_hbuf + 1);
  	/* +1 is just for an optimization */
  memcpy(ictx->commit + ictx->n_commit, 
	 ictx->hbuf, ictx->n_hbuf);
  ictx->n_commit += ictx->n_hbuf;
  ictx->n_hbuf = 0;
}

static void
cmd_commit(struct anthy_input_context* ictx)
{
  do_cmd_commit(ictx);
}

static void
cmd_next_candidate(struct anthy_input_context* ictx)
{
  struct a_segment* as;

  ictx->enum_cand_count++;

  as = ictx->cur_segment;

  if (!ictx->enum_reverse)
    as->cand = ictx->last_gotten_cand;
  else
    ictx->enum_reverse = 0;

  if (as->cand == NTH_UNCONVERTED_CANDIDATE) {
    while (as) {
      if (as->cand == NTH_UNCONVERTED_CANDIDATE) {
	as->cand = 0;
      }
      as = as->next;
    }
    ictx->last_gotten_cand = 0;
  } else {
    if (++as->cand >= as->ass.nr_candidate)
      as->cand = 0;
    ictx->last_gotten_cand = as->cand;
  }
}

static void
cmd_prev_candidate(struct anthy_input_context* ictx)
{
  struct a_segment* as;

  ictx->enum_cand_count++;

  as = ictx->cur_segment;

  if (ictx->enum_reverse)
    as->cand = ictx->last_gotten_cand;
  else
    ictx->enum_reverse = 1;

  if (as->cand == NTH_UNCONVERTED_CANDIDATE) {
    while (as) {
      if (as->cand == NTH_UNCONVERTED_CANDIDATE) {
	as->cand = 0;
      }
      as = as->next;
    }
    ictx->last_gotten_cand = 0;
  } else {
    if (--as->cand < 0) 
      as->cand = as->ass.nr_candidate - 1;
    ictx->last_gotten_cand = as->cand;
  }
}

static void
cmd_move_selection(struct anthy_input_context* ictx, int d)
{
  if (d > 0) 
    while (d-- > 0 && ictx->cur_segment->next) {
      ictx->enum_cand_count = 0;
      ictx->cur_segment = ictx->cur_segment->next;
      ictx->last_gotten_cand = ictx->cur_segment->cand;
    }
  else
    while (d++ < 0 && ictx->cur_segment->prev) {
      ictx->enum_cand_count = 0;
      ictx->cur_segment = ictx->cur_segment->prev;
      ictx->last_gotten_cand = ictx->cur_segment->cand;
    }
}

static void
cmd_move_to_bol_seg(struct anthy_input_context* ictx)
{
  ictx->cur_segment = ictx->segment;
  ictx->enum_cand_count = 0;  
  ictx->last_gotten_cand = ictx->cur_segment->cand;
}

static void
cmd_move_to_eol_seg(struct anthy_input_context* ictx)
{
  while (ictx->cur_segment->next)
    ictx->cur_segment = ictx->cur_segment->next;
  ictx->enum_cand_count = 0;  
  ictx->last_gotten_cand = ictx->cur_segment->cand;
}

static void
cmd_unhiragana_candidate(struct anthy_input_context* ictx)
{
  struct a_segment* as;

  for (as = ictx->cur_segment->next; as; as = as->next)
    as->cand = 0;
}

static void
cmd_move_to_bol(struct anthy_input_context* ictx)
{
  terminate_rk(ictx);

  if (ictx->hbuf_follow == NULL) { /* 最適化 */
    ictx->hbuf_follow = ictx->hbuf;
    ictx->n_hbuf_follow = ictx->n_hbuf;
    ictx->s_hbuf_follow = ictx->s_hbuf;
    ictx->hbuf = NULL;
    ictx->n_hbuf = 0;
    ictx->s_hbuf = 0;
    return;
  }

  ensure_buffer(&ictx->hbuf_follow, &ictx->s_hbuf_follow,
		ictx->n_hbuf + ictx->n_hbuf_follow);
  memmove(ictx->hbuf_follow + ictx->n_hbuf, 
	  ictx->hbuf_follow, ictx->n_hbuf_follow);
  memcpy(ictx->hbuf_follow, ictx->hbuf, ictx->n_hbuf);
  ictx->n_hbuf_follow += ictx->n_hbuf;
  ictx->n_hbuf = 0;
}

static void
cmd_move_to_eol(struct anthy_input_context* ictx)
{
  terminate_rk(ictx);

  if (ictx->hbuf == NULL) { /* 最適化 */
    ictx->hbuf = ictx->hbuf_follow;
    ictx->n_hbuf = ictx->n_hbuf_follow;
    ictx->s_hbuf = ictx->s_hbuf_follow;
    ictx->hbuf_follow = NULL;
    ictx->n_hbuf_follow = 0;
    ictx->s_hbuf_follow = 0;
    return;
  }

  ensure_buffer(&ictx->hbuf, &ictx->s_hbuf, 
		ictx->n_hbuf + ictx->n_hbuf_follow);
  memcpy(ictx->hbuf + ictx->n_hbuf, ictx->hbuf_follow, ictx->n_hbuf_follow);
  ictx->n_hbuf += ictx->n_hbuf_follow;
  ictx->n_hbuf_follow = 0;
}

static void
cmd_cut(struct anthy_input_context* ictx)
{
  char* tmp_str;
  int   tmp_int;

  terminate_rk(ictx);

  /* バッファの入れ換えで済ませる */
  tmp_str = ictx->cut;
  tmp_int = ictx->s_cut;
  ictx->cut = ictx->hbuf_follow;
  ictx->n_cut = ictx->n_hbuf_follow;
  ictx->s_cut = ictx->s_hbuf_follow;
  ictx->hbuf_follow = tmp_str;
  ictx->n_hbuf_follow = 0;
  ictx->s_hbuf_follow = tmp_int;
}
		
/*****************************************************************/

/* pure function */
struct anthy_input_context* 
anthy_input_create_context(struct anthy_input_config* cfg)
{
  struct anthy_input_context* ictx;
  int i;

  ictx = 
    (struct anthy_input_context*) malloc(sizeof(struct anthy_input_context));
  ictx->state = ANTHY_INPUT_ST_NONE;
  ictx->rkctx = rk_context_create(cfg->break_into_roman);
  for (i = 0; i < NR_RKMAP; i++)
    rk_register_map(ictx->rkctx, i, cfg->rk_map[i]);
  ictx->map_no = RKMAP_HIRAGANA;
  rk_select_registered_map(ictx->rkctx, ictx->map_no);
  ictx->hbuf = NULL;
  ictx->n_hbuf = 0;
  ictx->s_hbuf = 0;
  ictx->hbuf_follow = NULL;
  ictx->n_hbuf_follow = 0;
  ictx->s_hbuf_follow = 0;
  ictx->enum_cand_limit = DEFAULT_ENUM_CAND_LIMIT;
  ictx->enum_cand_count = 0;
  ictx->actx = NULL;
  ictx->segment = NULL;
  ictx->cur_segment = NULL;
  ictx->commit = NULL;
  ictx->n_commit = 0;
  ictx->s_commit = 0;
  ictx->cut = NULL;
  ictx->n_cut = 0;
  ictx->s_cut = 0;
  ictx->cfg = cfg;
  ictx->next_cfg_owner = cfg->owners;
  cfg->owners = ictx;
  return ictx;
}

void
anthy_input_free_context(struct anthy_input_context* ictx)
{
  struct anthy_input_context **p;

  reset_anthy_input_context(ictx);
  rk_context_free(ictx->rkctx);

  for (p = &ictx->cfg->owners; *p; p = &(*p)->next_cfg_owner)
    if (*p == ictx) {
      *p = ictx->next_cfg_owner;
      break;
    }

  free(ictx->hbuf);
  free(ictx->hbuf_follow);
  free(ictx->commit);
  free(ictx->cut);
  free(ictx);
}

void
anthy_input_free_preedit(struct anthy_input_preedit* pedit)
{
  struct anthy_input_segment* p, * q;

  free(pedit->commit);
  free(pedit->cut_buf);
  for (p = pedit->segment; p; p = q) {
    q = p->next;
    anthy_input_free_segment(p);
  }
  free(pedit);
}

void
anthy_input_free_segment(struct anthy_input_segment* seg)
{
  free(seg->str);
  free(seg);
}

void
anthy_input_str(struct anthy_input_context* ictx, const char* str)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:
    enter_edit_state(ictx);
    cmd_push_key(ictx, str);
    if (ictx->map_no == RKMAP_ASCII ||
	ictx->map_no == RKMAP_WASCII) {
      commit_noconv_string(ictx);
      leave_edit_state(ictx);
      enter_none_state(ictx);
    }
    break;
  case ANTHY_INPUT_ST_EDIT:
    cmd_push_key(ictx, str);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_commit(ictx);
    leave_conv_state(ictx);
    enter_edit_state(ictx);
    cmd_push_key(ictx, str);
    break;
  case ANTHY_INPUT_ST_CSEG:
    cmd_commit(ictx);
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    leave_conv_state(ictx);
    enter_edit_state(ictx);
    cmd_push_key(ictx, str);
    break;
  }
}

void
anthy_input_next_candidate(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:
    break;
  case ANTHY_INPUT_ST_EDIT:
    enter_conv_state(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_next_candidate(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    cmd_unhiragana_candidate(ictx);
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    cmd_next_candidate(ictx);
    break;
  }
}


void
anthy_input_prev_candidate(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:
    break;
  case ANTHY_INPUT_ST_EDIT:
    enter_conv_state(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_prev_candidate(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    cmd_prev_candidate(ictx);
    break;
  }
}

void
anthy_input_quit(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    leave_edit_state(ictx);
    enter_none_state(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    leave_conv_state(ictx);
    enter_edit_state_noinit(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    leave_conv_state(ictx);
    enter_edit_state_noinit(ictx);
    break;
  }
}

void
anthy_input_erase_prev(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    cmd_backspace(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    leave_conv_state(ictx);
    enter_edit_state_noinit(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    leave_conv_state(ictx);
    enter_edit_state_noinit(ictx);
    break;
  }
}

void
anthy_input_erase_next(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    cmd_delete(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    break;
  case ANTHY_INPUT_ST_CSEG:
    break;
  }
}

void
anthy_input_commit(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    terminate_rk(ictx);
    cmd_commit_unconv(ictx);
    leave_edit_state(ictx);
    enter_none_state(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_commit(ictx);
    leave_conv_state(ictx);
    enter_none_state(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    cmd_commit(ictx);
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    leave_conv_state(ictx);
    enter_none_state(ictx);
    break;
  }
}

void
anthy_input_move(struct anthy_input_context* ictx, int lr)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    cmd_move_cursor(ictx, lr);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_move_selection(ictx, lr);
    break;
  case ANTHY_INPUT_ST_CSEG:
    cmd_unhiragana_candidate(ictx);
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    cmd_move_selection(ictx, lr);
    break;
  }
}

void
anthy_input_resize(struct anthy_input_context* ictx, int lr)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    break;
  case ANTHY_INPUT_ST_CONV:
    enter_cseg_state(ictx);
    cmd_resize(ictx, lr);
    break;
  case ANTHY_INPUT_ST_CSEG:
    cmd_resize(ictx, lr);
    break;
  }
}

void
anthy_input_beginning_of_line(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    cmd_move_to_bol(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_move_to_bol_seg(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    break;
  }
}  

void
anthy_input_end_of_line(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    cmd_move_to_eol(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_move_to_eol_seg(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    break;
  }
}  

void
anthy_input_cut(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
    break;
  case ANTHY_INPUT_ST_EDIT:
    cmd_cut(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    break;
  case ANTHY_INPUT_ST_CSEG:
    break;
  }
}

/* key oriented function */
void
anthy_input_key(struct anthy_input_context* ictx, int c)
{
  char buf[2];
  
  buf[0] = (char) c;
  buf[1] = '\0';
  anthy_input_str(ictx, buf);
}

void
anthy_input_space(struct anthy_input_context* ictx)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:
    enter_edit_state(ictx);
    do_cmd_push_key(ictx, " ");
    commit_noconv_string(ictx);
    leave_edit_state(ictx);
    enter_none_state(ictx);      
    break;
  case ANTHY_INPUT_ST_EDIT:
    terminate_rk(ictx);
    if (rk_selected_map(ictx->rkctx) == RKMAP_SHIFT_ASCII)
      do_cmd_push_key(ictx, " ");
    else
      enter_conv_state(ictx);
    break;
  case ANTHY_INPUT_ST_CONV:
    cmd_next_candidate(ictx);
    break;
  case ANTHY_INPUT_ST_CSEG:
    cmd_unhiragana_candidate(ictx);
    leave_cseg_state(ictx);
    enter_conv_state_noinit(ictx);
    cmd_next_candidate(ictx);
    break;
  }
}

/* meta function command */

int
anthy_input_get_state(struct anthy_input_context* ictx)
{
  return ictx->state;
}

static struct anthy_input_segment *
alloc_segment(int flag, int len, int noconv_len)
{
  struct anthy_input_segment *seg;
  seg = (struct anthy_input_segment*)
    malloc(sizeof(struct anthy_input_segment));
  seg->flag = flag;
  seg->cand_no = -1;
  seg->nr_cand = -1;
  seg->noconv_len = noconv_len;
  if (len) {
    seg->str = (char *)malloc(len);
  } else {
    seg->str = NULL;
  }
  seg->next = NULL;
  return seg;
}

static void
get_edit_mode_preedit(struct anthy_input_context* ictx,
		      struct anthy_input_preedit* pedit)
{
  struct anthy_input_segment** p;
  int len;
  /* 左の文字列pending|カーソル|右の文字列 */

  p = &pedit->segment;

  /* left */
  if (ictx->n_hbuf > 0) {
    *p = alloc_segment(ANTHY_INPUT_SF_EDITING, ictx->n_hbuf + 1,
		       ictx->n_hbuf);

    memcpy((*p)->str, ictx->hbuf, ictx->n_hbuf);
    (*p)->str[ictx->n_hbuf] = '\0';
    p = &(*p)->next;
  }

  if (ictx->cfg->preedit_mode) {
    len = rk_partial_result(ictx->rkctx, NULL, 0);
    if (len > 1) {
      *p = alloc_segment(ANTHY_INPUT_SF_PENDING, len, len - 1);

      rk_partial_result(ictx->rkctx, (*p)->str, len);
      p = &(*p)->next;
    }
  } else {
    len = rk_get_pending_str(ictx->rkctx, NULL, 0);
    if (len > 1) {
      *p = alloc_segment(ANTHY_INPUT_SF_PENDING, len, len - 1);
      
      rk_get_pending_str(ictx->rkctx, (*p)->str, len);
      p = &(*p)->next;
    }
  }

  /* cursor */
  *p = alloc_segment(ANTHY_INPUT_SF_CURSOR, 0, 0);
  pedit->cur_segment = *p;
  p = &(*p)->next;

  /* right */
  if (ictx->n_hbuf_follow > 0) {
    *p = alloc_segment(ANTHY_INPUT_SF_EDITING,
		       ictx->n_hbuf_follow + 1,
		       ictx->n_hbuf_follow);
    memcpy((*p)->str, ictx->hbuf_follow, ictx->n_hbuf_follow);
    (*p)->str[ictx->n_hbuf_follow] = '\0';
  }
}

struct anthy_input_preedit*
anthy_input_get_preedit(struct anthy_input_context* ictx)
{
  struct anthy_input_preedit* pedit;

  pedit = (struct anthy_input_preedit*) 
    malloc(sizeof(struct anthy_input_preedit));

  pedit->state = ictx->state;

  /* 未コミットの文字列 */
  if (ictx->n_commit > 0) {
    pedit->commit = (char*) malloc(ictx->n_commit + 1);
    memcpy(pedit->commit, ictx->commit, ictx->n_commit);
    pedit->commit[ictx->n_commit] = '\0';
    ictx->n_commit = 0;
  } else {
    pedit->commit = NULL;
  }

  /* カットバッファの文字列 */
  if(ictx->n_cut > 0) {
    pedit->cut_buf = (char*) malloc(ictx->n_cut + 1);
    memcpy(pedit->cut_buf, ictx->cut, ictx->n_cut);
    pedit->cut_buf[ictx->n_cut] = '\0';
    ictx->n_cut = 0;
  } else {
    pedit->cut_buf = NULL;
  }

  pedit->segment = NULL;
  pedit->cur_segment = NULL;
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
  case ANTHY_INPUT_ST_NONE:
    break;
  case ANTHY_INPUT_ST_EDIT:
    get_edit_mode_preedit(ictx, pedit);
    break;
  case ANTHY_INPUT_ST_CONV:
  case ANTHY_INPUT_ST_CSEG:
    {
      struct anthy_input_segment** p;
      struct a_segment* as;
      
      for (as = ictx->segment, p = &pedit->segment; as; as = as->next) {
	/* 各文節に対して */
	int len, noconv_len;
	
	noconv_len = anthy_get_segment(ictx->actx, as->index, 
				       NTH_UNCONVERTED_CANDIDATE,
				       NULL, 0);
	len = anthy_get_segment(ictx->actx, as->index, as->cand, NULL, 0);
	*p = alloc_segment(ANTHY_INPUT_SF_NONE, len + 1, noconv_len);

	anthy_get_segment(ictx->actx, as->index, as->cand, (*p)->str, len + 1);
	(*p)->cand_no = as->cand;
	(*p)->nr_cand = as->ass.nr_candidate;
	(*p)->next = NULL;
	
	if (as == ictx->cur_segment) {
	  pedit->cur_segment = *p;
	  (*p)->flag |= ANTHY_INPUT_SF_CURSOR;
	  if (ictx->enum_cand_count >= ictx->enum_cand_limit)
	    (*p)->flag |= (ictx->enum_reverse ?
			   ANTHY_INPUT_SF_ENUM_REVERSE : ANTHY_INPUT_SF_ENUM);

	  if (ictx->state == ANTHY_INPUT_ST_CSEG) {
	    struct a_segment* as1;
	    
	    for (as1 = as->next, len = 0; as1; as1 = as1->next)
	      len += anthy_get_segment(ictx->actx, as1->index,
				       NTH_UNCONVERTED_CANDIDATE, NULL, 0);
	    if (len > 0) {
	      char* s;

	      p = &(*p)->next;
	      *p = alloc_segment(ANTHY_INPUT_SF_FOLLOWING, len + 1, len);
	      for (as1 = as->next, s = (*p)->str; as1; as1 = as1->next) {
		anthy_get_segment(ictx->actx, as1->index, 
				  NTH_UNCONVERTED_CANDIDATE,
				  s, len - (s - (*p)->str) + 1);
		s += anthy_get_segment(ictx->actx, as1->index, 
				       NTH_UNCONVERTED_CANDIDATE, NULL, 0);
	      }
	      (*p)->str[len] = '\0';
	      (*p)->next = NULL;
	    }
	    break;
	  }
	}

	p = &(*p)->next;
      }
    }
    break;
  }

  return pedit;
}

int
anthy_input_map_select(struct anthy_input_context* ictx, int map)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
    break;
  case ANTHY_INPUT_ST_NONE:  
  case ANTHY_INPUT_ST_EDIT:
  case ANTHY_INPUT_ST_CONV:
  case ANTHY_INPUT_ST_CSEG:
    return cmdh_map_select(ictx, map);
    break;
  }

  anthy_input_errno = AIE_INVAL;
  return -1;
}

int
anthy_input_get_selected_map(struct anthy_input_context* ictx)
{
  return ictx->map_no;
}

struct anthy_input_segment* 
anthy_input_get_candidate(struct anthy_input_context* ictx, int cand_no)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_CONV:
    return cmdh_get_candidate(ictx, cand_no);
    break;
  case ANTHY_INPUT_ST_OFF:
  case ANTHY_INPUT_ST_NONE:  
  case ANTHY_INPUT_ST_EDIT:
  case ANTHY_INPUT_ST_CSEG:
    break;
  }

  anthy_input_errno = AIE_INVAL;
  return NULL;
}

int
anthy_input_select_candidate(struct anthy_input_context* ictx, int cand)
{
  switch (ictx->state) {
  case ANTHY_INPUT_ST_OFF:
  case ANTHY_INPUT_ST_NONE:  
  case ANTHY_INPUT_ST_EDIT:
    break;
  case ANTHY_INPUT_ST_CONV:
    return cmdh_select_candidate(ictx, cand);
    break;
  case ANTHY_INPUT_ST_CSEG:
    break;
  }

  anthy_input_errno = AIE_INVAL;
  return -1;
}

int
anthy_input_edit_toggle_config(struct anthy_input_config *cfg, char tg)
{
  return anthy_input_do_edit_toggle_option(cfg->rk_option, tg);
}

int
anthy_input_edit_rk_config(struct anthy_input_config *cfg, int map,
			   const char *from, const char *to, const char *follow)
{
  return
    anthy_input_do_edit_rk_option(cfg->rk_option, map,
				  from, to, follow);
}

int
anthy_input_clear_rk_config(struct anthy_input_config *cfg,
			    int use_default)
{
  return
    anthy_input_do_clear_rk_option(cfg->rk_option, use_default);
}

int
anthy_input_break_into_roman_config(struct anthy_input_config *cfg,
				    int brk)
{
  int old_val;
  old_val = cfg->break_into_roman;
  cfg->break_into_roman = brk;
  return old_val;
}

int
anthy_input_preedit_mode_config(struct anthy_input_config *cfg,
				int val)
{
  int old_val;
  old_val = cfg->preedit_mode;
  cfg->preedit_mode = val;
  return old_val;
}

void
anthy_input_change_config(struct anthy_input_config* cfg)
{
  struct anthy_input_context* p;

  struct rk_map* h_map = cfg->rk_map[RKMAP_HIRAGANA];
  struct rk_map* k_map = cfg->rk_map[RKMAP_KATAKANA];
  struct rk_map* s_map = cfg->rk_map[RKMAP_SHIFT_ASCII];
  struct rk_map* hk_map = cfg->rk_map[RKMAP_HANKAKU_KANA];

  cfg->rk_map[RKMAP_HIRAGANA] = make_rkmap_hiragana(cfg->rk_option);
  cfg->rk_map[RKMAP_KATAKANA] = make_rkmap_katakana(cfg->rk_option);
  cfg->rk_map[RKMAP_SHIFT_ASCII] = make_rkmap_shiftascii(cfg->rk_option);
  cfg->rk_map[RKMAP_HANKAKU_KANA] = make_rkmap_hankaku_kana(cfg->rk_option);

  /* このconfigを共有するコンテキストすべてに対して */
  for (p = cfg->owners; p; p = p->next_cfg_owner) {
    reset_anthy_input_context(p);
    rk_register_map(p->rkctx, RKMAP_HIRAGANA, cfg->rk_map[RKMAP_HIRAGANA]);
    rk_register_map(p->rkctx, RKMAP_KATAKANA, cfg->rk_map[RKMAP_KATAKANA]);
    rk_register_map(p->rkctx, RKMAP_SHIFT_ASCII, 
		    cfg->rk_map[RKMAP_SHIFT_ASCII]);
    rk_register_map(p->rkctx, RKMAP_HANKAKU_KANA, 
		    cfg->rk_map[RKMAP_HANKAKU_KANA]);
    rk_select_registered_map(p->rkctx, RKMAP_HIRAGANA);
  }

  rk_map_free(h_map);
  rk_map_free(k_map);
  rk_map_free(s_map);
  rk_map_free(hk_map);
}

struct anthy_input_config*
anthy_input_create_config(void)
{
  struct anthy_input_config* cfg;

  cfg = (struct anthy_input_config*) malloc(sizeof(struct anthy_input_config));

  cfg->rk_option = anthy_input_create_rk_option();
  cfg->break_into_roman = 0;
  cfg->preedit_mode = 0;
  cfg->rk_map[RKMAP_ASCII] = make_rkmap_ascii(cfg->rk_option);
  cfg->rk_map[RKMAP_SHIFT_ASCII] = make_rkmap_shiftascii(cfg->rk_option);
  cfg->rk_map[RKMAP_HIRAGANA] = make_rkmap_hiragana(cfg->rk_option);
  cfg->rk_map[RKMAP_KATAKANA] = make_rkmap_katakana(cfg->rk_option);
  cfg->rk_map[RKMAP_WASCII] = make_rkmap_wascii(cfg->rk_option);
  cfg->rk_map[RKMAP_HANKAKU_KANA] = make_rkmap_hankaku_kana(cfg->rk_option);
  cfg->owners = NULL;

  return cfg;
}

void
anthy_input_free_config(struct anthy_input_config* cfg)
{
  int err;

  /* このconfigを共有する全てのcontextを事前に解放する事 */
  assert(!cfg->owners);

  rk_map_free(cfg->rk_map[RKMAP_ASCII]);
  rk_map_free(cfg->rk_map[RKMAP_SHIFT_ASCII]);
  rk_map_free(cfg->rk_map[RKMAP_HIRAGANA]);
  rk_map_free(cfg->rk_map[RKMAP_KATAKANA]);
  rk_map_free(cfg->rk_map[RKMAP_WASCII]);
  rk_map_free(cfg->rk_map[RKMAP_HANKAKU_KANA]);

  err = anthy_input_free_rk_option(cfg->rk_option);
  free(cfg);
}

int
anthy_input_init(void)
{
  return anthy_init();
}

void
anthy_input_set_personality(const char *personality)
{
  anthy_set_personality(personality);
}

anthy_context_t
anthy_input_get_anthy_context(struct anthy_input_context *ictx)
{
  return ictx->actx;
}
