/* Copyright (c) 2000-2003                  */
/*   Yamashita Lab., Ritsumeikan University */
/*   All rights reserved                    */

#include	<stdio.h>
#include	<stdlib.h>
#include	<string.h>
#include	"synthesis.h"
#include	"accent.h"
#include	"tag.h"

int match_hinshi(char *,char *);
int TmpMsg(char *,...);
int LogMsg(char *,...);
int ErrMsg(char *,...);
void restart(int);
void init_accent_info( MORPH * );
void parse_aConType( char *, MORPH * );
int getAccent( char *, int, int );
void parse_accent_info(char *,MORPH *);
void print_morph_yomi(MORPH *);
MORA *new_mora();
int parse_XMLtag( char *, char *, TAGOPTIONS *, int *);
void make_sil_morph( SILENCE );
void make_mora();
APHRASE *new_aphrase();
void add_to_aphrase( MORPH *, APHRASE * );
void refresh_mora();
void refresh_aphrase();
char *aformName( int );

MORPH* new_morph( SILENCE );

struct {
	int 	id;
	char	*name;
} attributeTable[] = {
	{ W_PRON,       "pron" },
	{ W_POS,        "pos" },
	{ W_CTYPE,      "ctype" },
	{ W_CFORM,      "cform" },
	{ W_INFO,       "info" },
	{ W_FORM,       "form" },
	{ W_ORTH,       "orth" },
	{ W_ACCENT,     "accent" },
	{ W_INDEX_FORM, "indexForm" },
	{ W_INDEX_ORTH, "indexOrth" },
	{ W_A_TYPE,     "aType"},
	{ W_A_CON_TYPE, "aConType" },
	{ W_C_TYPE,     "cType" },
	{ W_C_FORM,     "cForm" },
	{ W_LEX,        "lex" }
};

#define NUM_ATTRIBUTE	(sizeof(attributeTable)/sizeof(attributeTable[0]))

/* 一般的なカテゴリーは後に宣言しておく必要あり。
   「名詞-固有名詞」は「名詞」より先に。 */

struct {
	int 	id;
	char	*name;
} hinshiTable[] = {
	{ H_MEISHI_FUTSUU_IPPAN,   "名詞-普通名詞-一般" },
	{ H_MEISHI_FUTSUU_SAHEN,   "名詞-普通名詞-サ変可能" },
	{ H_MEISHI_FUTSUU_KEIJOU,  "名詞-普通名詞-形状詞可能" },
	{ H_MEISHI_FUTSUU_FUKUSHI, "名詞-普通名詞-副詞可能" },
	{ H_MEISHI_FUTSUU,         "名詞-普通名詞" },
	{ H_MEISHI_FUTSUU_IPPAN,   "名詞-一般" },       /* unidic-0 */
	{ H_MEISHI_FUTSUU_FUKUSHI, "名詞-副詞可能" },   /* unidic-0 */
	{ H_MEISHI_FUTSUU_SAHEN,   "名詞-サ変可能" },   /* unidic-0 */
	{ H_MEISHI_KOYUU_IPPAN,    "名詞-固有名詞-一般" },
	{ H_MEISHI_KOYUU_JINMEI,   "名詞-固有名詞-人名" },
	{ H_MEISHI_KOYUU_CHIMEI,   "名詞-固有名詞-地名" },
	{ H_MEISHI_KOYUU_SOSHIKI,  "名詞-固有名詞-組織名" },
	{ H_MEISHI_KOYUU,          "名詞-固有名詞" },
	{ H_MEISHI_KAZU,           "名詞-数詞" },
	{ H_MEISHI,                "名詞" },
	{ H_DAIMEISHI,             "代名詞" },
	{ H_KEIJOUSHI,             "形状詞" },
	{ H_RENTAISHI,             "連体詞" },
	{ H_FUKUSHI,               "副詞" },
	{ H_SETSUZOKUSHI,          "接続詞" },
	{ H_KANDOUSHI_IPPAN,       "感動詞-一般" },
	{ H_KANDOUSHI_FILLER,      "感動詞-フィラー" },
	{ H_KANDOUSHI_FILLER,      "フィラー" },         /* unidic-0 */
	{ H_KANDOUSHI,             "感動詞" },

	{ H_DOUSHI_IPPAN,          "動詞-一般" },
	{ H_DOUSHI_HIJIRITSU,      "動詞-非自立可能" },
	{ H_DOUSHI,                "動詞" },
	{ H_KEIYOUSHI_IPPAN,       "形容詞-一般" },
	{ H_KEIYOUSHI_HIJIRITSU,   "形容詞-非自立可能" },
	{ H_KEIYOUSHI,             "形容詞" },
	{ H_JODOUSHI,              "助動詞" },
	{ H_JOSHI_KAKUJOSHI,       "助詞-格助詞" },
	{ H_JOSHI_FUKUJOSHI,       "助詞-副助詞" },
	{ H_JOSHI_KAKARIJOSHI,     "助詞-係助詞" },
	{ H_JOSHI_SETSUZOKUJOSHI,  "助詞-接続助詞" },
	{ H_JOSHI_SHUUJOSHI,       "助詞-終助詞" },
	{ H_JOSHI_JUNTAIJOSHI,     "助詞-準体助詞" },
	{ H_JOSHI,                 "助詞" },
	{ H_SETTOUJI,              "接頭辞" },
	{ H_SETSUBIJI,             "接尾辞" },

	{ H_KIGOU,                 "記号" },
	{ H_KIGOU,                 "その他-記号" },		/* unidic-0 */
	{ H_KUTEN,                 "その他-句点" },
	{ H_TOUTEN,                "その他-読点" },
	{ H_KAKKO_HIRAKU,          "その他-括弧開" },
	{ H_KAKKO_TOJIRU,          "その他-括弧閉" },
	{ H_KUTEN,                 "その他-記号-句点" },	/* unidic-0 */
	{ H_TOUTEN,                "その他-記号-読点" },	/* unidic-0 */
	{ H_KAKKO_HIRAKU,          "その他-記号-括弧開" },	/* unidic-0 */
	{ H_KAKKO_TOJIRU,          "その他-記号-括弧閉" },	/* unidic-0 */
	{ H_SONOTA,                "その他" },
	{ H_MICHIGO,               "未知語" }

};

#define	NUM_HINSHI	(sizeof(hinshiTable)/sizeof(hinshiTable[0]))


/* 最初に一度だけ */
void init_morph()
{
	mphead = mptail = NULL;
}

void free_submorph( MORPH *morph )
{
	MORPH *next;

	while( morph )  {
		next = morph->next;
		free( morph->kanji );
		if( morph->pron != NULL )  free( morph->pron );
		free( morph );
		morph = next;
	}
}

/* 入力文ごとに */
/* 使っている形態素のセルの開放 */
void refresh_morph()
{
	MORPH *morph, *next;

	morph = mphead;
	while( morph )  {
		next = morph->next;
		free( morph->kanji );
		if( morph->pron != NULL )  free( morph->pron );
		if( morph->submorph != NULL )  free_submorph( morph->submorph );
		free( morph );
		morph = next;
	}
	mphead = mptail = NULL;
}

/* 品詞分類の取り出し */
int hinshiID( char *h )
{
	int 	i;
	for( i=0; i<NUM_HINSHI; ++i )  {
		if( match_hinshi( h, hinshiTable[i].name ) )  return hinshiTable[i].id;
	}
	ErrMsg( "Unknown hinshi ... %s\n", h );
	return( H_MEISHI );
/*	restart(1);	*/
/*	return -1;	*/
}

int match_hinshi( char *h, char *hname )
{
	/* 登録されている品詞名を先頭から照合。残りは無視する。*/
	while( *hname  )  {
		if( *h != *hname )  return 0;
		++h;  ++hname;
	}
	return 1;	/* matched */
}

/* 「一段」かどうかだけの判断 */
int katsuyogataID( char *k )
{
	if( strncmp(k,"一段",4) == 0 )  {
		return KATA_ICHIDAN;
	} else {
		return -1;
	}
}

/* 「連用形」と「未然形」だけの判断 */
int katsuyokeiID( char *k )
{
	if( strncmp(k,"連用",4) == 0 )  {
		return KEI_RENYOU;
	} else if( strncmp(k,"未然",4) == 0 )  {
		return KEI_MIZEN;
	} else {
		return -1;
	}
}

void init_morph_data( MORPH *morph, SILENCE sil )
{
	int i;

	morph->parent = NULL;

	morph->kanji = NULL;
	morph->pron = NULL;
	morph->nmora = 0;
	morph->nbyte = 0;
	morph->hinshiID = -1;
	morph->katsuyogataID = -1;
	morph->katsuyokeiID = -1;
	morph->accentType = -1;
	morph->n_accent = 0;
	for( i=0; i<MAX_ACCENT; ++i )  {
		morph->accent[i].prepos = '-';
		morph->accent[i].form = -1;
		morph->accent[i].ctype = -999;
		morph->accent[i].ctype2 = -999;
	}
	morph->submorph = NULL;
	morph->silence = sil;
	morph->mrhead = morph->mrtail = NULL;
}

MORPH *pre_submorph;

MORPH *new_submorph()
{
	MORPH *submorph;

	submorph = (MORPH *) malloc( sizeof(MORPH) );
	if( ! submorph )  {
		ErrMsg( "* malloc error for 'submorph'\n" );
		restart(1);
	}

/* 作った形態素セルをチェーンの中に入れる */
	if( mptail->submorph == NULL )  {
		mptail->submorph = submorph;
	} else {
		pre_submorph->next = submorph;
	}
	submorph->prev = submorph->next = NULL;
	pre_submorph = submorph;
	init_morph_data( submorph, NON );

	return submorph;
}

MORPH *new_morph(SILENCE sil)
{
	MORPH *morph;

	morph = (MORPH *) malloc( sizeof(MORPH) );
	if( ! morph )  {
		ErrMsg( "* malloc error for 'morph'\n" );
		restart(1);
	}

/* 作った形態素セルをチェーンの中に入れる */
	if( mphead == NULL )  {
		/* 一つめのセル */
		mphead = mptail = morph;
		morph->prev = morph->next = NULL;
	} else {
		/* tail の後ろに追加 */
		mptail->next = morph;
		morph->prev = mptail;
		morph->next = NULL;
		mptail = morph;
	}
	init_morph_data( morph, sil );

	return morph;
}

int attributeID( char *attr )
{
	int 	i;
	for( i=0; i<NUM_ATTRIBUTE; ++i )  {
		if( strcmp( attr, attributeTable[i].name )==0 )  return attributeTable[i].id;
	}
	return -1;
}

/*-----------------------------------------------------------------------*/
/*  unidic-0.1.2 による解析結果の処理                                    */
/*-----------------------------------------------------------------------*/

/* line[] は
	<W orth="音声" form="オンセイ" pron="オンセイ" pos="名詞-一般" 
		ctype="無活用" cform="基本形-一般" info="accent=1">音声</W>
の形式 */
/* 形態素の文字数(バイト数)を返す */
int parse_chasen_morph( char *line )
{
	int 	i, n, alone, nbyte;
	char	tagname[128], *attr, *val;
	char	acc[256];
	MORPH	*morph;
	TAGOPTIONS	op[10];

	/* 二つ目以降の文の始まり */
	if( mptail->silence == SILE )  make_sil_morph( SILB );

	acc[0] = '\0';
	nbyte = 0;
	morph = new_morph(NON);

	n = parse_XMLtag( line, tagname, op, &alone );

	for( i=0; i<n; ++i )  {
		attr = op[i].attr;   val = op[i].val;
/*		TmpMsg( "%s='%s'\n", attr, val );	*/
		switch( attributeID( attr ) )  {
		case W_ORTH:
			morph->kanji = (char *) malloc( sizeof(char) * (strlen(val)+1) );
			if( ! morph->kanji )  {
				ErrMsg( "* malloc error for 'morph.kanji'\n" );
				restart(1);
			}
			strcpy( morph->kanji, val );
			nbyte = morph->nbyte = strlen( val );
			break;
		case W_PRON:
			morph->pron = (char *) malloc( sizeof(char) * (strlen(val)+1) );
			if( ! morph->pron )  {
				ErrMsg( "* malloc error for 'morph.pron'\n" );
				restart(1);
			}
			strcpy( morph->pron, val );
			break;
		case W_POS:
			morph->hinshiID = hinshiID( val );
			break;
		case W_CTYPE:
			morph->katsuyogataID = katsuyogataID( val );
			break;
		case W_CFORM:
			morph->katsuyokeiID = katsuyokeiID( val );
			break;
		case W_INFO:
			strcpy( acc, val );
			break;
/*		default:
/*			ErrMsg( "Unknown option ... %s='%s'\n", attr, val );	*/
		}
/*		if( strcmp("。",kanji)==0 )  break;	*/
	}
	parse_accent_info( acc, morph );

/* ポーズ，無音の処理 */
	if( morph->hinshiID == H_KUTEN ||      /* 。*/
			strcmp(morph->kanji,"？")==0  )  {   /* ？ */
/*		morph->silence = PAU;
		make_sil_morph( SILE );
*/
		morph->silence = SILE;

	} else if( morph->hinshiID == H_TOUTEN ||    /* 、 */
			   morph->hinshiID == H_KAKKO_HIRAKU ||	   /* 「 など */
			   morph->hinshiID == H_KAKKO_TOJIRU )  {  /* 」など */
		morph->silence = PAU;
	}

	return( nbyte );
}

/*-----------------------------------------------------------------------*/
/*  unidic-1.0.x による解析結果の処理                                    */
/*-----------------------------------------------------------------------*/

int make_morph_data( MORPH *morph, char *line, char *acc, char *acon )
{
	int 	i, n, alone, nbyte;
	char	tagname[128], *attr, *val;
	TAGOPTIONS	op[10];

	n = parse_XMLtag( line, tagname, op, &alone );

	nbyte = 0;
	for( i=0; i<n; ++i )  {
		attr = op[i].attr;   val = op[i].val;
/*		TmpMsg( "%s='%s'\n", attr, val );	*/
		switch( attributeID( attr ) )  {
		case W_ORTH:
			morph->kanji = (char *) malloc( sizeof(char) * (strlen(val)+1) );
			if( ! morph->kanji )  {
				ErrMsg( "* malloc error for 'morph.kanji'\n" );
				restart(1);
			}
			strcpy( morph->kanji, val );
			nbyte = morph->nbyte = strlen( val );
			break;
		case W_PRON:
			morph->pron = (char *) malloc( sizeof(char) * (strlen(val)+1) );
			if( ! morph->pron )  {
				ErrMsg( "* malloc error for 'morph.pron'\n" );
				restart(1);
			}
			strcpy( morph->pron, val );
			break;
		case W_POS:
			morph->hinshiID = hinshiID( val );
			break;
		case W_C_TYPE:
			morph->katsuyogataID = katsuyogataID( val );
			break;
		case W_C_FORM:
			morph->katsuyokeiID = katsuyokeiID( val );
			break;
		case W_A_TYPE:
			if( acc[0] != 0 )  break;	/* aType より accent の値を優先 */
			strcpy( acc, val );
			break;
		case W_ACCENT:
			strcpy( acc, val );
			break;
		case W_A_CON_TYPE:
			strcpy( acon, val );
			break;
/*		default:
/*			ErrMsg( "Unknown option ... %s='%s'\n", attr, val );	*/
		}
/*		if( strcmp("。",kanji)==0 )  break;	*/
	}

	return( nbyte );
}

/* 複合語情報を記憶しておく構造体 */
bool in_compound = false;

/* line[] は
	<W2 orth="意識不明" pron="イシキフメイ" accent="1" 
		pos="名詞-普通名詞-形状詞可能" aType="1">
の形式で、複合語データ。
aType は辞書でのアクセント型。accent はこの発話でのアクセント型。
複合語は一つの morph データとして扱う。 */

int open_chasen_compound( char *line )
{
	int 	nbyte;
	char	acc[256], acon[256];
	MORPH	*morph;

	/* 二つ目以降の文の始まり */
	if( mptail->silence == SILE )  make_sil_morph( SILB );

	in_compound = true;
	acc[0] = acon[0] = '\0';
	morph = new_morph(NON);

	nbyte = make_morph_data( morph, line, acc, acon );

	init_accent_info( morph );
	if( acc[0] )  {
		morph->accentType = getAccent( acc, 
			morph->katsuyogataID, morph->katsuyokeiID );
	}
	if( acon[0] )  {
		parse_aConType( acon, morph );
	}
	return( nbyte );
}

/* 複合語の読みがないときに、複合語の構成語の読みを連結する。*/
void make_W2_pron( MORPH* morph )
{
	//MORPH *m, *subm;
	MORPH *m;
	char pron[512];

	pron[0] = '\0';
	for( m=morph->submorph; m; m=m->next )  {
		strcat( pron, m->pron );
/*		printf( "   %s\n", m->pron );	*/
	}
	morph->pron = (char *) realloc( morph->pron, sizeof(char) * (strlen(pron)+1) );
	if( ! morph->pron )  {
		ErrMsg( "* realloc error for 'morph.pron'\n" );
		restart(1);
	}
	strcpy( morph->pron, pron );
}

void close_chasen_compound()
{
	MORPH *morph, *m, *mphead_save, *subm;
	APHRASE *aphrase;
	int i;
	//char	pron[512];

	in_compound = false;
	morph = mptail;
	subm = morph->submorph;

	if( morph->n_accent == 0 )  {
		/* 先頭の submorph のアクセント結合型情報をコピーしておく。 */
		for( i=0; i<subm->n_accent; ++i )  {
			morph->accent[i].prepos = subm->accent[i].prepos;
			morph->accent[i].form   = subm->accent[i].form;
			morph->accent[i].ctype  = subm->accent[i].ctype;
			morph->accent[i].ctype2 = subm->accent[i].ctype2;
		}
		morph->n_accent = subm->n_accent;
	}

	if( morph->accentType > -1 )  return;

/* 複合語のアクセント型を決定 */

	/* submorph 列を morph 列と仮定して、、、 */
	mphead_save = mphead;  mphead = morph->submorph;
	make_mora();	/* add_to_aphrase() でモーラ情報を参照している。 */

	aphrase = new_aphrase();
	for( m=mphead; m; m=m->next )  {
		add_to_aphrase( m, aphrase );
	}

	morph->accentType = aphrase->accentType;

	/* 茶筌の結果で pron="" となっている W2 データに対応するため */
	if( *(morph->pron) == '\0' )  make_W2_pron( morph );

	refresh_mora();
	refresh_aphrase();
	mphead = mphead_save;
}

/* line[] は
	<W1 orth="社会" pron="シャカイ" accent="1" indexForm="シャカイ" 
		indexOrth="社会" pos="名詞-普通名詞-一般" aType="1" aConType="C1"/>
の形式 
aType は辞書でのアクセント型。accent はこの発話でのアクセント型。
複合語の中の形態素の読み込み。 */

int parse_chasen_submorph( char *line )
{
	int 	nbyte;
	char	acc[256], acon[256];
	MORPH	*submorph;

	acc[0] = acon[0] = '\0';
	submorph = new_submorph();
	nbyte = make_morph_data( submorph, line, acc, acon );

	init_accent_info( submorph );
	submorph->accentType = getAccent( acc, 
		submorph->katsuyogataID, submorph->katsuyokeiID );
	parse_aConType( acon, submorph );

	return( nbyte );
}


/* line[] は
	<W1 orth="社会" pron="シャカイ" accent="1" indexForm="シャカイ" 
		indexOrth="社会" pos="名詞-普通名詞-一般" aType="1" aConType="C1"/>
の形式 
aType は辞書でのアクセント型。accent はこの発話でのアクセント型。 */

/* 形態素の文字数(バイト数)を返す */
int parse_chasen_morph1( char *line )
{
	int 	nbyte;
	char	acc[256], acon[256];
	MORPH	*morph;

	if( in_compound )  {
		nbyte = parse_chasen_submorph( line );
		return( nbyte );
	}

/* 以下で、複合語になっていない形態素の処理 */

	/* 二つ目以降の文の始まり */
	if( mptail->silence == SILE )  make_sil_morph( SILB );

	acc[0] = acon[0] = '\0';
	morph = new_morph(NON);

	nbyte = make_morph_data( morph, line, acc, acon );

	init_accent_info( morph );
	morph->accentType = getAccent( acc, 
		morph->katsuyogataID, morph->katsuyokeiID );
	parse_aConType( acon, morph );

/* ポーズ，無音の処理 */
	if( morph->hinshiID == H_KUTEN ||      /* 。*/
			strcmp(morph->kanji,"？")==0  )  {   /* ？ */
		morph->silence = SILE;

	} else if( morph->hinshiID == H_TOUTEN ||    /* 、 */
			   morph->hinshiID == H_KAKKO_HIRAKU ||	   /* 「 など */
			   morph->hinshiID == H_KAKKO_TOJIRU )  {  /* 」など */
		morph->silence = PAU;
	}

	return( nbyte );
}

/* 無音部を１形態素、１モーラとして作成 */
void make_sil_morph( SILENCE sil )
{
	char sil_str[5];
	MORPH *morph;//, *new_morph();

	if( sil == SILB )  {
		strcpy( sil_str, "silB" );
	} else if( sil == SILE )  {
		strcpy( sil_str, "silE" );
	} else if( sil == PAU )  {
		strcpy( sil_str, "pau" );
	} else {
		ErrMsg( "* Unknown silence in make_sil_morph\n" );
		strcpy( sil_str, "pau" );
/*		restart(1);	*/
	}

	morph = new_morph( sil );
	morph->kanji = (char *) malloc( sizeof(char) * (strlen(sil_str)+1) );
	if( ! morph->kanji )  {
		ErrMsg( "* malloc error for 'morph.kanji of SILENCE'\n" );
		restart(1);
	}
	strcpy( morph->kanji, sil_str );
}

/*------------------------------------------------------------*/

void print_hinshi_name( int hid )
{
	int 	i;
	for( i=0; i<NUM_HINSHI; ++i )  {
		if( hinshiTable[i].id == hid )  {
		  LogMsg( "%s", hinshiTable[i].name );
		  return;
		}
	}
}

void print_aConType( MORPH *morph )
{
	int i;

	if( morph->n_accent == 0 )  {
		LogMsg( "\t-" );
		return;
	}
	for( i=0; i<morph->n_accent; ++i )  {
		if( i == 0 )  {
			LogMsg( "\t" );
		} else {
			LogMsg( "," );
		}
		LogMsg( "%c%%%s", morph->accent[i].prepos, 
			aformName( morph->accent[i].form ) );
	}
}


void print_morph()
{
	//int 	n, i;
	int 	n;
	MORPH	*morph;

	LogMsg( "* morph data\n" );
	LogMsg( "(orth\tpron\tPOS\t[accent]\taConType\tmora)\n" );
	n = 0;
	for( morph=mphead; morph; morph=morph->next )  {
		LogMsg( "%s\t", morph->kanji );
		print_morph_yomi( morph );
/*		LogMsg( "%s", morph->pron );	*/
		LogMsg( "\t" );
		print_hinshi_name( morph->hinshiID );
		LogMsg( ":%d/%d/%d\t[%d]", 
			morph->hinshiID, 
			morph->katsuyogataID, morph->katsuyokeiID, 
			morph->accentType );
		print_aConType( morph );
		LogMsg( "\t%d\n", morph->nmora );
		++n;
	}
	LogMsg( "- n_morph: %d\n", n );
}

void print_morph_yomi( MORPH *morph )
{
	MORA	*m;

	if( morph->mrhead == NULL )  {
		LogMsg( "-" );
		return;
	}
	for( m=morph->mrhead; m && m->parent==morph; m=m->next )  {
		LogMsg( "%s", m->yomi );
	}
}

