/*
 * Anthyの辞書ライブラリの中心
 *
 * anthy_get_seq_ent_from_xstr()で辞書をひく
 *
 * Copyright (C) 2000-2007 TABATA Yusuke
 * Copyright (C) 2005-2006 YOSHIDA Yuichi
 *
 */
/*
  This library is free software; you can redistribute it and/or
  modify it under the terms of the GNU Lesser General Public
  License as published by the Free Software Foundation; either
  version 2 of the License, or (at your option) any later version.

  This library 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
  Lesser General Public License for more details.

  You should have received a copy of the GNU Lesser General Public
  License along with this library; if not, write to the Free Software
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
 */
#include <stdlib.h>
#include <string.h>

#include <anthy/anthy.h>
#include <anthy/dic.h>
#include <anthy/conf.h>
#include <anthy/record.h>
#include <anthy/alloc.h>
#include <anthy/logger.h>
#include <anthy/xchar.h>
#include <anthy/feature_set.h>
#include <anthy/textdict.h>

#include <anthy/diclib.h>

#include "dic_ent.h"
#include "dic_personality.h"
#include "dic_main.h"

/**/
static int dic_init_count;

/* 辞書 */
/* 全personalityで共有されるファイル辞書 */
static struct word_dic *master_dic_file;

/* 各パーソナリティごとの辞書 */
struct mem_dic *anthy_current_personal_dic_cache;/* キャッシュ */
/**/
struct record_stat *anthy_current_record;

struct seq_ent *
anthy_validate_seq_ent(struct seq_ent *seq, xstr *xs, int is_reverse)
{
  if (!seq) {
    return NULL;
  }
  if (seq->nr_dic_ents == 0 && seq->nr_compound_ents == 0) {
    /* 無効なエントリを作成したのでcacheから削除 */
    anthy_mem_dic_release_seq_ent(anthy_current_personal_dic_cache,
				  xs, is_reverse);
    return NULL;
  }

  return seq;
}

struct seq_ent *
anthy_cache_get_seq_ent(xstr *xs, int is_reverse)
{
  struct seq_ent *seq;

  /* キャッシュ中に既にあればそれを返す */
  seq = anthy_mem_dic_find_seq_ent_by_xstr(anthy_current_personal_dic_cache,
					   xs, is_reverse);
  if (seq) {
    return seq;
  }

  /* キャッシュ中に無いので確保 */
  return anthy_mem_dic_alloc_seq_ent_by_xstr(anthy_current_personal_dic_cache,
					     xs, is_reverse);
}

int
anthy_dic_check_word_relation(int from, int to)
{
  return anthy_word_dic_check_word_relation(master_dic_file, from, to);
}

static seq_ent_t
do_get_seq_ent_from_xstr(xstr *xs, int is_reverse)
{
  struct seq_ent *seq;
  /* キャッシュから取り出す */
  seq = anthy_cache_get_seq_ent(xs, is_reverse);
  seq = anthy_validate_seq_ent(seq, xs, is_reverse);
  if (!seq) {
    /* 数字などの辞書に無い文字列を検索する */
    return anthy_get_ext_seq_ent_from_xstr(xs, is_reverse);
  }
  return seq;
}

static xstr *
convert_vu(xstr *xs)
{
  int i, v = 0;
  int j;

    /* 「ヴ」の出現を数える */
  for (i = 0; i < xs->len; i++) {
    if (xs->str[i] == KK_VU) {
      v++;
    }
  }
  if (v > 0) {
    xstr *nx = malloc(sizeof(xstr));
    nx->len = xs->len + v;
    nx->str = malloc(sizeof(xchar)*nx->len);
    j = 0;
    /* 「ヴ」を「う゛」に変換しつつコピーする */
    for (i = 0; i < xs->len; i++) {
      if (xs->str[i] == KK_VU) {
	nx->str[j] = HK_U;
	j++;
	nx->str[j] = HK_DDOT;
	j++;
      } else {
	nx->str[j] = xs->str[i];
	j++;
      }
    }
    return nx;
  }
  return NULL;
}

seq_ent_t
anthy_get_seq_ent_from_xstr(xstr *xs, int is_reverse)
{
  struct seq_ent *se;

  if (!xs) {
    return NULL;
  }
  if (!is_reverse) {
    xstr *nx = convert_vu(xs);
    /* 「ヴ」の混ざった順変換の場合、「う゛」に直して検索する
     *   上位のレイヤーではユーザの与えた文字列をそのまま保持することが
     *   期待されるので、変換はここで行なう。
     */
    if (nx) {
      se = do_get_seq_ent_from_xstr(nx, 0);
      anthy_free_xstr(nx);
      return se;
    }
  }
  /* 「ヴ」が出現しない、もしくは逆変換の場合 */
  return do_get_seq_ent_from_xstr(xs, is_reverse);
}

static void
gang_elm_dtor(void *p)
{
  struct gang_elm *ge = p;
  free(ge->key);
}

static int
find_gang_elm(allocator ator, struct gang_elm *head, xstr *xs)
{
  char *str = anthy_xstr_to_cstr(xs, ANTHY_UTF8_ENCODING);
  struct gang_elm *ge;
  for (ge = head->tmp.next; ge; ge = ge->tmp.next) {
    if (!strcmp(ge->key, str)) {
      free(str);
      return 0;
    }
  }
  ge = anthy_smalloc(ator);
  ge->xs = *xs;
  ge->key = str;
  ge->tmp.next = head->tmp.next;
  head->tmp.next = ge;
  return 1;
}

static int
gang_elm_compare_func(const void *p1, const void *p2)
{
  const struct gang_elm * const *s1 = p1;
  const struct gang_elm * const *s2 = p2;
  return strcmp((*s1)->key, (*s2)->key);
}

struct gang_scan_context {
  /**/
  int nr;
  struct gang_elm **array;
  /**/
  int nth;
};

static int
is_ext_ent(struct seq_ent *seq)
{
  if (!seq->md) {
    return 1;
  }
  return 0;
}

static void
scan_misc_dic(struct gang_elm **array, int nr, int is_reverse)
{
  int i;
  for (i = 0; i < nr; i++) {
    xstr *xs = &array[i]->xs;
    struct seq_ent *seq;
    seq = anthy_cache_get_seq_ent(xs, is_reverse);
    /* 個人辞書からの取得(texttrie(旧形式)と未知語辞書) */
    if (seq) {
      anthy_copy_words_from_private_dic(seq, xs, is_reverse);
      anthy_validate_seq_ent(seq, xs, is_reverse);
    }
  }
}

static void
load_word(xstr *xs, const char *n, int is_reverse)
{
  struct seq_ent *seq = anthy_get_seq_ent_from_xstr(xs, 0);
  xstr *word_xs;
  wtype_t wt;  
  struct word_line wl;
  if (!seq || is_ext_ent(seq)) {
    seq = anthy_mem_dic_alloc_seq_ent_by_xstr(anthy_current_personal_dic_cache,
					      xs, is_reverse);
  }
  if (anthy_parse_word_line(n, &wl)) {
    return ;
  }
  word_xs = anthy_cstr_to_xstr(wl.word, ANTHY_UTF8_ENCODING);
  if (anthy_type_to_wtype(wl.wt, &wt)) {
    anthy_mem_dic_push_back_dic_ent(seq, 0, word_xs, wt,
				    NULL, wl.freq, 0);
  }

  anthy_free_xstr(word_xs);
}

static int
gang_scan(void *p, int offset, const char *key, const char *n)
{
  struct gang_scan_context *gsc = p;
  struct gang_elm *elm;
  int r;
  (void)offset;
  while (1) {
    if (gsc->nth >= gsc->nr) {
      return 0;
    }
    elm = gsc->array[gsc->nth];
    r = strcmp(elm->key, key);
    if (r == 0) {
      /* find it */
      load_word(&elm->xs, n, 0);
      /* go next in dictionary */
      return 0;
    } else if (r > 0) {
      /* go next in dictionary */
      return 0;
    } else {
      /* go next in lookup */
      gsc->nth ++;
    }
  }
  return 0;
}

static void
scan_dict(struct textdict *td, int nr, struct gang_elm **array)
{
  struct gang_scan_context gsc;
  gsc.nr = nr;
  gsc.array = array;
  gsc.nth = 0;
  anthy_textdict_scan(td, 0, &gsc, gang_scan);
}

struct scan_arg {
  struct gang_elm **array;
  int nr;
};

static void
request_scan(struct textdict *td, void *arg)
{
  struct scan_arg *sarg = (struct scan_arg *)arg;
  scan_dict(td, sarg->nr, sarg->array);
}

static void
do_gang_load_dic(xstr *sentence, int is_reverse)
{
  allocator ator = anthy_create_allocator(sizeof(struct gang_elm),
					  gang_elm_dtor);
  int from, len;
  xstr xs;
  int i, nr;
  struct gang_elm head;
  struct gang_elm **array, *cur;
  struct scan_arg sarg;
  head.tmp.next = NULL;
  nr = 0;
  for (from = 0; from < sentence->len ; from ++) {
    for (len = 1; len < 32 && from + len <= sentence->len; len ++) {
      xs.str = &sentence->str[from];
      xs.len = len;
      nr += find_gang_elm(ator, &head, &xs);
    }
  }
  array = malloc(sizeof(struct gang_elm *) * nr);
  cur = head.tmp.next;
  for (i = 0; i < nr; i++) {
    array[i] = cur;
    cur = cur->tmp.next;
  }
  qsort(array, nr, sizeof(struct gang_elm *), gang_elm_compare_func);
  /**/
  anthy_gang_fill_seq_ent(master_dic_file, array, nr, is_reverse);
  /**/
  scan_misc_dic(array, nr, is_reverse);
  /* 個人辞書から読む */
  sarg.nr = nr;
  sarg.array = array;
  anthy_ask_scan(request_scan, (void *)&sarg);
  /**/
  free(array);
  anthy_free_allocator(ator);
}

void
anthy_gang_load_dic(xstr *sentence, int is_reverse)
{
  xstr *nx;
  if (!is_reverse && (nx = convert_vu(sentence))) {
    do_gang_load_dic(nx, is_reverse);
    anthy_free_xstr(nx);
  } else {
    do_gang_load_dic(sentence, is_reverse);
  }
}

/*
 * seq_entの取得
 ************************
 * seq_entの各種情報の取得
 */
int
anthy_get_nr_dic_ents(seq_ent_t se, xstr *xs)
{
  struct seq_ent *s = se;
  if (!s) {
    return 0;
  }
  if (!xs) {
    return s->nr_dic_ents;
  }
  return s->nr_dic_ents + anthy_get_nr_dic_ents_of_ext_ent(se, xs);
}

int
anthy_get_nth_dic_ent_str(seq_ent_t se, xstr *orig,
			  int n, xstr *x)
{
  if (!se) {
    return -1;
  }
  if (n >= se->nr_dic_ents) {
    return anthy_get_nth_dic_ent_str_of_ext_ent(se, orig,
						n - se->nr_dic_ents, x);
  }
  x->len = se->dic_ents[n]->str.len;
  x->str = anthy_xstr_dup_str(&se->dic_ents[n]->str);
  return 0;
}

int
anthy_get_nth_dic_ent_is_compound(seq_ent_t se, int nth)
{
  if (!se) {
    return 0;
  }
  if (nth >= se->nr_dic_ents) {
    return 0;
  }
  return se->dic_ents[nth]->is_compound;
}

int
anthy_get_nth_dic_ent_freq(seq_ent_t se, int nth)
{
  struct seq_ent *s = se;
  if (!s) {
    return 0;
  }
  if (!s->dic_ents) {
    return anthy_get_nth_dic_ent_freq_of_ext_ent(se, nth);
  }
  if (s->nr_dic_ents <= nth) {
    return anthy_get_nth_dic_ent_freq_of_ext_ent(se, nth - se->nr_dic_ents);
  }
  return s->dic_ents[nth]->freq;
}

int
anthy_get_nth_dic_ent_wtype(seq_ent_t se, xstr *xs,
			    int n, wtype_t *w)
{
  struct seq_ent *s = se;
  if (!s) {
    *w = anthy_wt_none;
    return -1;
  }
  if (s->nr_dic_ents <= n) {
    int r;
    r = anthy_get_nth_dic_ent_wtype_of_ext_ent(xs, n - s->nr_dic_ents, w);
    if (r == -1) {
      *w = anthy_wt_none;
    }
    return r;
  }
  *w =  s->dic_ents[n]->type;
  return 0;
}

int
anthy_get_seq_ent_pos(seq_ent_t se, int pos)
{
  int i, v=0;
  struct seq_ent *s = se;
  if (!s) {
    return 0;
  }
  if (s->nr_dic_ents == 0) {
    return anthy_get_ext_seq_ent_pos(se, pos);
  }
  for (i = 0; i < s->nr_dic_ents; i++) {
    if (anthy_wtype_get_pos(s->dic_ents[i]->type) == pos) {
      v += s->dic_ents[i]->freq;
      if (v == 0) {
	v = 1;
      }
    }
  }
  return v;
}

int
anthy_get_seq_ent_ct(seq_ent_t se, int pos, int ct)
{
  int i, v=0;
  struct seq_ent *s = se;
  if (!s) {
    return 0;
  }
  if (s->nr_dic_ents == 0) {
    return anthy_get_ext_seq_ent_ct(s, pos, ct);
  }
  for (i = 0; i < s->nr_dic_ents; i++) {
    if (anthy_wtype_get_pos(s->dic_ents[i]->type)== pos &&
	anthy_wtype_get_ct(s->dic_ents[i]->type)==ct) {
      v += s->dic_ents[i]->freq;
      if (v == 0) {
	v = 1;
      }
    }
  }
  return v;
}

/*
 * wtの品詞を持つ単語の中で最大の頻度を持つものを返す
 */
int
anthy_get_seq_ent_wtype_freq(seq_ent_t seq, wtype_t wt)
{
  int i, f;

  if (!seq) {
    return 0;
  }
  /**/
  if (seq->nr_dic_ents == 0) {
    return anthy_get_ext_seq_ent_wtype(seq, wt);
  }

  f = 0;
  /* 単語 */
  for (i = 0; i < seq->nr_dic_ents; i++) {
    if (seq->dic_ents[i]->order == 0 &&
	anthy_wtype_include(wt, seq->dic_ents[i]->type)) {
      if (f < seq->dic_ents[i]->freq) {
	f = seq->dic_ents[i]->freq;
      }
    }
  }
  return f;
}

/*
 * wtの品詞を持つ複合語の中で最大の頻度を持つものを返す
 */
int
anthy_get_seq_ent_wtype_compound_freq(seq_ent_t se, wtype_t wt)
{
  int i,f;
  struct seq_ent *s = se;
  if (!s) {
    return 0;
  }
  /**/
  f = 0;
  for (i = 0; i < s->nr_dic_ents; i++) {
    if (!anthy_get_nth_dic_ent_is_compound(se, i)) {
      continue;
    }
    if (anthy_wtype_include(wt, s->dic_ents[i]->type)) {
      if (f < s->dic_ents[i]->freq) {
	f = s->dic_ents[i]->freq;
      }
    }
  }
  return f;
}

int
anthy_get_seq_ent_indep(seq_ent_t se)
{
  int i;
  struct seq_ent *s = se;
  if (!s) {
    return 0;
  }
  if (s->nr_dic_ents == 0) {
    return anthy_get_ext_seq_ent_indep(s);
  }
  for (i = 0; i < s->nr_dic_ents; i++) {
    if (anthy_wtype_get_indep(s->dic_ents[i]->type)) {
      return 1;
    }
  }
  return 0;
}

int
anthy_has_compound_ents(seq_ent_t se)
{
  if (!se) {
    return 0;
  }
  return se->nr_compound_ents;
}

/* compundでない候補を持っているか */
int
anthy_has_non_compound_ents(seq_ent_t se)
{
  if (!se) {
    return 0;
  }
  if (se->nr_dic_ents == 0) {
    return 1;
  }
  return se->nr_dic_ents - se->nr_compound_ents;
}

compound_ent_t
anthy_get_nth_compound_ent(seq_ent_t se, int nth)
{
  if (!se) {
    return NULL;
  }
  if (nth >= 0 && nth < se->nr_dic_ents) {
    return se->dic_ents[nth];
  }
  return NULL;
}

struct elm_compound {
  int len;
  xstr str;
};

/* 要素に対応する読みの長さを返す */
static int
get_element_len(xchar xc)
{
  if (xc > '0' && xc <= '9') {
    return xc - '0';
  }
  if (xc >= 'a' && xc <= 'z') {
    return xc - 'a' + 10;
  }
  return 0;
}

static struct elm_compound *
get_nth_elm_compound(compound_ent_t ce, struct elm_compound *elm, int nth)
{
  int off = 0;
  int i, j;
  for (i = 0; i <= nth; i++) {
    /* nth番目の要素の先頭へ移動する */
    while (!(ce->str.str[off] == '_' &&
	     get_element_len(ce->str.str[off+1]) > 0)) {
      off ++;
      if (off + 1 >= ce->str.len) {
	return NULL;
      }
    }
    /* 構造体へ情報を取り込む */
    elm->len = get_element_len(ce->str.str[off+1]);
    elm->str.str = &ce->str.str[off+2];
    elm->str.len = ce->str.len - off - 2;
    for (j = 0; j < elm->str.len; j++) {
      if (elm->str.str[j] == '_') {
	elm->str.len = j;
	break;
      }
    }
    off ++;
  }
  return elm;
}

int
anthy_compound_get_nr_segments(compound_ent_t ce)
{
  struct elm_compound elm;
  int i;
  if (!ce) {
    return 0;
  }
  for (i = 0; get_nth_elm_compound(ce, &elm, i); i++);
  return i;
}

int
anthy_compound_get_nth_segment_len(compound_ent_t ce, int nth)
{
  struct elm_compound elm;
  if (get_nth_elm_compound(ce, &elm, nth)) {
    return elm.len;
  }
  return 0;
}

int
anthy_compound_get_nth_segment_xstr(compound_ent_t ce, int nth, xstr *xs)
{
  struct elm_compound elm;
  if (get_nth_elm_compound(ce, &elm, nth)) {
    if (xs) {
      *xs = elm.str;
      return 0;
    }
  }
  return -1;
}

int
anthy_compound_get_wtype(compound_ent_t ce, wtype_t *w)
{
  *w = ce->type;
  return 0;
}

int
anthy_compound_get_freq(compound_ent_t ce)
{
  return ce->freq;
}

/* フロントエンドから呼ばれる */
void
anthy_lock_dic(void)
{
  anthy_priv_dic_lock();
  anthy_priv_dic_update();
}

/* フロントエンドから呼ばれる */
void
anthy_unlock_dic(void)
{
  anthy_priv_dic_unlock();
}


dic_session_t
anthy_dic_create_session(void)
{
  return anthy_create_mem_dic();
}

void
anthy_dic_activate_session(dic_session_t d)
{
  anthy_current_personal_dic_cache = d;
}

void
anthy_dic_release_session(dic_session_t d)
{
  anthy_release_mem_dic(d);
}

void
anthy_dic_set_personality(const char *id)
{
  anthy_current_record = anthy_create_record(id);
  anthy_current_personal_dic_cache = anthy_create_mem_dic();
  anthy_init_private_dic(id);
}


/** 辞書サブシステムを初期化
 */
int
anthy_init_dic(void)
{
  if (dic_init_count) {
    dic_init_count ++;
    return 0;
  }
  if (anthy_init_diclib() == -1) {
    return -1;
  }

  anthy_init_wtypes();
  anthy_init_mem_dic();
  anthy_init_record();
  anthy_init_ext_ent();
  anthy_init_features();

  anthy_init_word_dic();
  master_dic_file = anthy_create_word_dic();
  if (!master_dic_file) {
    anthy_log(0, "Failed to create file dic.\n");
    return -1;
  }
  dic_init_count ++;
  return 0;
}

/** 辞書サブシステムをすべて解放
 */
void
anthy_quit_dic(void)
{
  dic_init_count --;
  if (dic_init_count) {
    return;
  }
  if (anthy_current_record) {
    anthy_release_record(anthy_current_record);
  }
  anthy_release_private_dic();
  anthy_current_record = NULL;
  anthy_quit_mem_dic();
  anthy_quit_diclib();
}

