/*
 * 文節の自立語部(接頭辞、接尾辞含む)に続く
 * 助詞、助動詞などの付属語のパターンをたどる。
 * パターンはグラフとして設定ファイルに用意する。
 *
 *
 *  +------+
 *  |      |
 *  |branch+--cond--+--transition--> node
 *  |      |        +--transition--> node
 *  | NODE |
 *  |      |
 *  |branch+--cond-----transition--> node
 *  |      |
 *  |branch+--cond-----transition--> node
 *  |      |
 *  +------+
 *
 * Copyright (C) 2000-2007 TABATA Yusuke
 * Copyright (C) 2006 YOSHIDA Yuichi
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "config.h"
#include <anthy/anthy.h>

#include <anthy/conf.h>
#include <anthy/ruleparser.h>
#include <anthy/xstr.h>
#include <anthy/filemap.h>
#include <anthy/logger.h>
#include <anthy/segclass.h>
#include <anthy/splitter.h>
#include <anthy/wtype.h>
#include <anthy/diclib.h>
#include "wordborder.h"

/* 遷移グラフ */
static struct dep_dic ddic;


static void
match_branch(struct splitter_context *sc,
	     struct word_list *tmpl,
	     xstr *xs, struct dep_branch *db);
static void
match_nodes(struct splitter_context *sc,
	    struct word_list *wl,
	    xstr follow_str, int node);


static int
anthy_xstrcmp_with_ondisk(xstr *xs,
			  ondisk_xstr *dxs)
{
  int *d = (int *)dxs;
  int len = anthy_dic_ntohl(d[0]);
  int i;
  xchar c;
  if (len != xs->len) {
    return 1;
  }
  d++;
  for (i = 0; i < len; i++) {
    c = anthy_dic_ntohl(d[i]);
    if (xs->str[i] != c) {
      return 1;
    }
  }
  return 0;
}

static ondisk_xstr *
anthy_next_ondisk_xstr(ondisk_xstr *dxs)
{
  int *d = (int *)dxs;
  int len = anthy_dic_ntohl(d[0]);
  return &d[len+1];
}

static int
anthy_ondisk_xstr_len(ondisk_xstr *dxs)
{
  int *d = (int *)dxs;
  return anthy_dic_ntohl(d[0]);
}

/*
 * 各ノードにおける遷移条件をテストする
 *
 * wl 自立語部のword_list
 * follow_str 自立語部以降の文字列
 * node ルールの番号
 */
static void
match_nodes(struct splitter_context *sc,
	    struct word_list *wl,
	    xstr follow_str, int node)
{
  struct dep_node *dn = &ddic.nodes[node];
  struct dep_branch *db;
  int i,j;

  /* 各ルールの */
  for (i = 0; i < dn->nr_branch; i++) {
    ondisk_xstr *dep_xs;
    db = &dn->branch[i];
    dep_xs = db->xstrs;

    /* 各遷移条件 */
    for (j = 0; j < db->nr_strs;
	 j++, dep_xs = anthy_next_ondisk_xstr(dep_xs)) {
      xstr cond_xs;
      /* 付属語の方が遷移条件より長いことが必要 */
      if (follow_str.len < anthy_ondisk_xstr_len(dep_xs)) {
	continue;
      }
      /* 遷移条件の部分を切り出す */
      cond_xs.str = follow_str.str;
      cond_xs.len = anthy_ondisk_xstr_len(dep_xs);

      /* 遷移条件と比較する */
      if (!anthy_xstrcmp_with_ondisk(&cond_xs, dep_xs)) {
	/* 遷移条件にmatchした */
	struct word_list new_wl = *wl;
	struct part_info *part = &new_wl.part[PART_DEPWORD];
	xstr new_follow;

	part->len += cond_xs.len;
	new_follow.str = &follow_str.str[cond_xs.len];
	new_follow.len = follow_str.len - cond_xs.len;
	/* 遷移してみる */
	match_branch(sc, &new_wl, &new_follow, db);
      }
    }
  }
}

/*
 * 各遷移を実行してみる
 *
 * tmpl ここまでに構成したword_list
 * xs 残りの文字列
 * db 現在調査中のbranch
 */
static void
match_branch(struct splitter_context *sc,
	     struct word_list *tmpl,
	     xstr *xs, struct dep_branch *db)
{
  struct part_info *part = &tmpl->part[PART_DEPWORD];
  int i;

  /* 遷移先を順にトライする */
  for (i = 0; i < db->nr_transitions; i++) {
    /**/
    int head_pos = tmpl->head_pos; /* 品詞の情報 */
    int features = tmpl->mw_features;
    enum dep_class dc = part->dc;
    /**/
    struct dep_transition *transition = &db->transition[i];

    tmpl->tail_ct = anthy_dic_ntohl(transition->ct);
    /* 遷移の活用形と品詞 */
    if (anthy_dic_ntohl(transition->dc) != DEP_NONE) {
      part->dc = anthy_dic_ntohl(transition->dc);
    }
    /* 名詞化する動詞等で品詞名を上書き */
    if (anthy_dic_ntohl(transition->head_pos) != POS_NONE) {
      tmpl->head_pos = anthy_dic_ntohl(transition->head_pos);
    }
    if (transition->weak) {
      tmpl->mw_features |= MW_FEATURE_WEAK_CONN;
    }

    /* 遷移か終端か */
    if (anthy_dic_ntohl(transition->next_node)) {
      /* 遷移 */
      match_nodes(sc, tmpl, *xs, anthy_dic_ntohl(transition->next_node));
    } else {
      struct word_list *wl;

      /* 
       * 終端ノードに到達したので、
       * それをword_listとしてコミット
       */
      wl = anthy_alloc_word_list(sc);
      *wl = *tmpl;
      wl->len += part->len;

      /**/
      anthy_commit_word_list(sc, wl);
    }
    /* 書き戻し */
    part->dc = dc;
    tmpl->head_pos = head_pos;
    tmpl->mw_features = features;
  }
}

/** 検索開始
 */
void
anthy_scan_node(struct splitter_context *sc,
		struct word_list *tmpl,
		xstr *follow, int node)
{
  /* 付属語の付いていない状態から検索を開始する */
  match_nodes(sc, tmpl, *follow, node);
}




static void
read_xstr(struct dep_dic* ddic, int* offset)
{
  int len = anthy_dic_ntohl(*(int*)&ddic->file_ptr[*offset]);
  *offset += sizeof(int);
  *offset += sizeof(xchar) * len;
}

static void
read_branch(struct dep_dic* ddic, struct dep_branch* branch, int* offset)
{
  int i;

  /* 遷移条件の数を読む */
  branch->nr_strs = anthy_dic_ntohl(*(int*)&ddic->file_ptr[*offset]);
  *offset += sizeof(int);
  /* 遷移条件の文字列を読み取る */
  branch->xstrs = (ondisk_xstr *)&ddic->file_ptr[*offset];

  for (i = 0; i < branch->nr_strs; ++i) {
    read_xstr(ddic, offset);
  }

  branch->nr_transitions = anthy_dic_ntohl(*(int*)&ddic->file_ptr[*offset]);
  *offset += sizeof(int);
  branch->transition = (struct dep_transition*)&ddic->file_ptr[*offset];
  *offset += sizeof(struct dep_transition) * branch->nr_transitions;
}

static void
read_node(struct dep_dic* ddic, struct dep_node* node, int* offset)
{
  int i;
  node->nr_branch = anthy_dic_ntohl(*(int*)&ddic->file_ptr[*offset]);
  *offset += sizeof(int);
    
  node->branch = malloc(sizeof(struct dep_branch) * node->nr_branch);
  for (i = 0; i < node->nr_branch; ++i) {
    read_branch(ddic, &node->branch[i], offset);
  }
}

static void
read_file(void)
{
  int i;

  int offset = 0;

  ddic.file_ptr = anthy_file_dic_get_section("dep_dic");

  /* 最初にルールの数 */
  ddic.nrRules = anthy_dic_ntohl(*(int*)&ddic.file_ptr[offset]);
  offset += sizeof(int);

  /* 各ルールの定義 */
  ddic.rules = (struct ondisk_wordseq_rule*)&ddic.file_ptr[offset];
  offset += sizeof(struct ondisk_wordseq_rule) * ddic.nrRules;
  /* ノードの数 */
  ddic.nrNodes = anthy_dic_ntohl(*(int*)&ddic.file_ptr[offset]);
  offset += sizeof(int);

  /* 各ノードを読み込む */
  ddic.nodes = malloc(sizeof(struct dep_node) * ddic.nrNodes);
  for (i = 0; i < ddic.nrNodes; ++i) {
    read_node(&ddic, &ddic.nodes[i], &offset);
  }
}

int
anthy_get_nr_dep_rule()
{
  return ddic.nrRules;
}

void
anthy_get_nth_dep_rule(int index, struct wordseq_rule *rule)
{
  /* ファイル上の情報からデータを取り出す */
  struct ondisk_wordseq_rule *r = &ddic.rules[index];
  rule->wt = anthy_get_wtype(r->wt[0], r->wt[1], r->wt[2],
			     r->wt[3], r->wt[4], r->wt[5]);
  rule->node_id = anthy_dic_ntohl(r->node_id);
}

int
anthy_init_depword_tab()
{
  read_file();
  return 0;
}

void
anthy_quit_depword_tab(void)
{
  int i;
  for (i = 0; i < ddic.nrNodes; i++) {
    struct dep_node* node = &ddic.nodes[i];
    free(node->branch);
  }
  free(ddic.nodes);
}

