#define	STRICT
#include	<windows.h>
#include	<stdlib.h>
#include	<string.h>
#include	<tchar.h>
#include	"list.h"
#include	"fep.h"

static	BYTE	FepKey[ 256 ] ;
static	CHAR	FepValue[ 256 ] ;

BYTE	FileBuffer[ 512 ] ;

#define		to_lower(c)		(((c)>='A'&&(c)<='Z')?(c)-'A'+'a':(c))
#define		isvowel(c)		(to_lower(c)=='a'||to_lower(c)=='i'||to_lower(c)=='u'||to_lower(c)=='e'||to_lower(c)=='o')
#define		cmp_char(a,b)	(to_lower(a)==to_lower(b))

LPSTR
strupr( LPSTR src )
{
	CHAR	c ;
	LPSTR	last = src ;

	while ( c = *src ) {
		if ( c >= 'a' && c <= 'z' ) {
			*src = c - 'a' + 'A' ;
		}
		src ++ ;
	}
	return last ;
}

typedef	LPCSTR	PConvTable ;

struct ConvTableQuery {
	PConvTable	pct ;
	LPCSTR		str ;
	LPDWORD		plen ;
	LPCSTR		*value ;
} ;
typedef	ConvTableQuery	*PConvTableQuery ;

BOOL
find_ConvTable( PConvTableQuery query )
{
	PConvTable	pct = query->pct ;
	LPCSTR		str = query->str ;
	LPDWORD		plen = query->plen ;
	LPCSTR		*value = query->value ;
	DWORD		len ;
	CHAR		c, key[ 128 ], *keyptr ;

	while ( *pct ) {
		keyptr = key ;
		while ( ( c = *pct++ ) != '\t' ) {
			*keyptr++ = c ;
		}
		*keyptr = 0 ;
		len = strlen( key ) ;
		if ( !strncmp( key, str, len ) ) {
			*plen = len ;
			*value = pct ;
			return TRUE ;
		}
		/* '\0' までスキップする */
		while ( *pct++ ) {
		}
	}
	return FALSE ;
}

#define		iskanji(x)	(((x)>=0x81 && (x)<=0x9F)||((x)>=0xE0 && (x)<=0xFC))

static	void
conv_all( PConvTableQuery query, LPCSTR src, LPSTR dst )
{
	CHAR	c ;
	DWORD	len ;
	LPCSTR	value ;

	while ( *src ) {
		query->str = src ;
		query->plen = &len ;
		query->value = &value ;
		if ( find_ConvTable( query ) ) {
			/* 変換テーブルにあった場合 */
			strcpy( dst, value ) ;
			dst += strlen( value ) ;
			src += len ;
		} else {
			c = *src++ ;
			*dst++ = c ;
			if ( iskanji( c ) ) {
				*dst++ = *src++ ;
			}
		}
	}
	*dst = 0 ;
}

const PConvTable	RomaKanaBuf = {
#include	"romakana.txt"
} ;
ConvTableQuery	RomaKanaQuery = { RomaKanaBuf } ;

const PConvTable	HiraKataBuf = { 
#include	"hirakata.txt"
} ;
ConvTableQuery	HiraKataQuery = { HiraKataBuf } ;

const PConvTable	ZenHanBuf = {
#include	"zenhan.txt"
} ;
ConvTableQuery	ZenHanQuery = { ZenHanBuf } ;

const PConvTable	HanZenBuf = {
#include	"hanzen.txt"
} ;
ConvTableQuery	HanZenQuery = { HanZenBuf } ;

void
hira2kata( LPCSTR src, LPSTR dst )
{
	conv_all( &HiraKataQuery, src, dst ) ;
}

void
zen2han( LPCSTR src, LPSTR dst )
{
	conv_all( &ZenHanQuery, src, dst ) ;
}

void
han2zen( LPCSTR src, LPSTR dst )
{
	conv_all( &HanZenQuery, src, dst ) ;
}

BOOL
insert_filebuffer( HANDLE hFile, DWORD offset, DWORD len )
{
	DWORD	readsize, writesize ;
	DWORD	size = sizeof FileBuffer - len ;
	LPBYTE	buf = &FileBuffer[ len ] ;

	if ( !len ) {
		return TRUE ;
	}
	while ( 1 ) {
		if ( offset != SetFilePointer( hFile, offset, NULL, FILE_BEGIN ) ) {
			return FALSE ;
		}
		if ( !ReadFile( hFile, buf, size, &readsize, NULL ) ) {
			break ;
		}
		if ( offset != SetFilePointer( hFile, offset, NULL, FILE_BEGIN ) ) {
			return FALSE ;
		}
		if ( readsize != size ) {
			WriteFile( hFile, FileBuffer, len + readsize, &writesize, NULL ) ;
			break ;
		}
		WriteFile( hFile, FileBuffer, size, &writesize, NULL ) ;
		memcpy( FileBuffer, &FileBuffer[ size ], len ) ;
		offset += size ;
	}
	return FlushFileBuffers( hFile ) ;
}

/*-------------------------------------------------------------------------
 * DictItem クラス実現部
 *-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------
 * DictLine クラス実現部
 *-------------------------------------------------------------------------*/
BOOL
DictLine::write( HANDLE hFile )
{
	LPCSTR		str ;
	LPBYTE		ptr ;
	PDictItem	item ;
	Iterator	l( items ) ;
	DWORD		rest, writesize, len ;

	if ( offset != SetFilePointer( hFile, offset, NULL, FILE_BEGIN ) ) {
		return FALSE ;
	}
	ptr = FileBuffer ;
	rest = sizeof FileBuffer ;
	*ptr++ = '/' ;
	rest -- ;
	while ( item = (PDictItem) l() ) {
		str = item->get_str() ;
		len = strlen( str ) ;
		if ( len + 1 < rest ) {
			WriteFile( hFile, FileBuffer, sizeof FileBuffer - rest, &writesize, NULL ) ;
			ptr = FileBuffer ;
			rest = sizeof FileBuffer ;
		}
		memcpy( ptr, str, len ), ptr += len ;
		*ptr++ = '/' ;
		rest -= len + 1 ;
	}
	if ( rest != sizeof FileBuffer ) {
		WriteFile( hFile, FileBuffer, sizeof FileBuffer - rest, &writesize, NULL ) ;
	}
	return FlushFileBuffers( hFile ) ;
}

PDictItem
DictLine::find( LPCSTR i )
{
	Iterator	l( items ) ;
	PDictItem	di ;

	while ( di = (PDictItem) l() ) {
		if ( !strcmp( i, di->get_str() ) ) {
			return di ;
		}
	}
	return NULL ;
}

/*-------------------------------------------------------------------------
 * FepDict クラス実現部
 *-------------------------------------------------------------------------*/
void
FepDict::set( LPCTSTR fn )
{
	_tcscpy( filename, fn ) ;
}

BOOL
FepDict::open()
{
	if ( hFile != INVALID_HANDLE_VALUE ) {
		return SetFilePointer( hFile, 0, NULL, FILE_BEGIN ) == 0 ;
	}
	hFile = CreateFile( filename, GENERIC_READ|GENERIC_WRITE,
				0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
				0 ) ;
	return hFile != INVALID_HANDLE_VALUE ;
}

void
FepDict::close()
{
	if ( hFile != INVALID_HANDLE_VALUE ) {
		CloseHandle( hFile ) ;
		hFile = INVALID_HANDLE_VALUE ;
	}
}

void
FepDict::clear_offset()
{
	DWORD		count = KEY_SIZE ;
	LPDWORD		ptr = offset ;

	do {
		*ptr++ = IDX_NONE ;
	} while ( -- count ) ;
}

void
FepDict::clear_index()
{
	if ( index ) {
		delete[] index ;
		index = 0 ;
	}
}

BOOL
FepDict::read_offset()
{
	LPBYTE	keyptr = 0 ;
	BYTE	*src, c ;
	BOOL	f_topline = TRUE, f_read = FALSE ;
	DWORD	ret, readsize, total = 0, keyofs = 0 ;

	clear_offset() ;
	if ( !open() ) {
		return FALSE ;
	}
	total = 0 ;
	while ( 1 ) {
		ret = ReadFile( hFile, FileBuffer, sizeof FileBuffer, &readsize, NULL ) ;
		if ( !ret || !readsize ) {
			break ;
		}
		src = FileBuffer ;
		do {
			c = *src++, total ++ ;
			if ( c == '\n' ) {
				/* LF が来た場合 */
				f_topline = TRUE ;
				continue ;
			} else if ( c < ' ' ) {
				/* 制御コードが来た場合 */
				continue ;
			} else if ( f_topline ) {
				/* 行の先頭からキーの取り込みを開始する */
				f_topline = FALSE ;
				keyptr = FepKey ;
				keyofs = total - 1 ;
			} else if ( keyptr && c == ' ' ) {
				*keyptr = 0 ;
				/* キーが確定した場合 */
				if ( *FepKey != 0x82 ) {
					/* ひらがなでない場合 */
					continue ;
				}
				if ( FepKey[1] < KEY_START || FepKey[1] > KEY_END ) {
					/* 妥当なキーでない場合 */
					continue ;
				}
				/* キーの記録をやめる */
				keyptr = 0 ;
				/* 該当するキーの位置を記録する */
				DWORD	idx = FepKey[1] - KEY_START ;
				if ( offset[ idx ] == IDX_NONE ) {
					/* 最初のものだけ記録 */
					offset[ idx ] = keyofs ;
				}
				f_read = TRUE ;
			}
			if ( keyptr ) {
				*keyptr++ = c ;
			}
		} while ( -- readsize ) ;
	}
	return f_read ;
}

BOOL
FepDict::read( LPCSTR str )
{
	DWORD	ofs_start, ofs ;
	BYTE	k1 = str[0], k2 = str[1] ;
	BYTE	*keyptr = 0  ;
	LPSTR	valptr = FepValue ;
	BYTE	*src, c, last ;
	BOOL	f_topline = TRUE, f_quit = FALSE ;
	DWORD	ret, readsize ;
	PDictLine	dl = 0 ;

	/* 有効なトップキー(全角ひらがな)か調べる */
	if ( k1 != 0x82 ) {
		return FALSE ;
	} else if ( k2 < KEY_START || k2 > KEY_END ) {
		return FALSE ;
	}
	/* 辞書行のトップキーインデックスを求める */
	ret = (DWORD) (k2 - KEY_START) ;
	if ( topkey == ret ) {
		/* 既に読み込み済み */
		return TRUE ;
	}
	topkey = ret ;
	/* トップキーインデックスからファイル中の位置を得る */
	ofs_start = offset[ topkey ] ;
	if ( ofs_start == IDX_NONE ) {
		return FALSE ;
	}
	if ( ofs_start != SetFilePointer( hFile, ofs_start, NULL, FILE_BEGIN ) ) {
		return FALSE ;
	}
	ofs = ofs_start ;
	/* 以前のエントリのクリア */
	dict.clear() ;
	while ( !f_quit ) {
		ret = ReadFile( hFile, FileBuffer, sizeof FileBuffer, &readsize, NULL ) ;
		if ( !ret || !readsize ) {
			break ;
		}
		src = FileBuffer ;
		do {
			c = *src++, ofs ++ ;
			if ( c == '\n' ) {
				/* LF が来た場合 */
				f_topline = TRUE ;
				continue ;
			} else if ( c < ' ' ) {
				/* 制御コードが来た場合 */
				continue ;
			} else if ( f_topline ) {
				f_topline = FALSE ;
				keyptr = FepKey ;
			} else if ( keyptr && c == ' ' ) {
				*keyptr = 0 ;
				/* キーが確定した場合 */
				if ( *FepKey != 0x82 ) {
					/* ひらがなでない場合 */
					continue ;
				}
				if ( FepKey[1] < KEY_START || FepKey[1] > KEY_END ) {
					/* 妥当なキーでない場合 */
					continue ;
				}
				if ( FepKey[1] != k2 ) {
					/* キー先頭の値が異なる場合 */
					f_quit = TRUE ;
					break ;
				}
				/* キーの記録をやめる */
				keyptr = 0 ;
				/* 新しい辞書エントリを作成する */
				dl = new DictLine( (LPSTR) FepKey, ofs ) ;
				dict.append( dl ) ;
				/* 項目内容を初期化する */
				valptr = FepValue ;
				/* 漢字2バイト目バッファを初期化 */
				last = 0 ;
				continue ;
			}
			if ( keyptr ) {
				/* キー文字列を記録する */
				*keyptr++ = c ;
			} else if ( dl ) {
				/* 辞書エントリに登録する必要がある場合 */
				if ( last ) {
					/* 漢字2バイト目の場合 */
					*valptr++ = (CHAR) last ;
					*valptr++ = (CHAR) c ;
					last = 0 ;
				} else if ( c == '/' ) {
					if ( valptr == FepValue ) {
						/* 空の項目はスキップ */
						continue ;
					}
					*valptr = 0 ;
					valptr = FepValue ;
					/* 項目を追加する */
					dl->append( FepValue ) ;
				} else {
					*valptr++ = c ;
				}
			}
		} while ( -- readsize ) ;
	}
	next_offset = ofs ;
	make_index() ;
	return TRUE ;
}

void
FepDict::make_index()
{
	Iterator	l1( dict ) ;
	PDictLine	dl, *ptr ;

	num_index = 0 ;
	while ( dl = (PDictLine) l1() ) {
		num_index ++ ;
	}
	clear_index() ;
	index = ptr = new DictLine*[ num_index ] ;
	Iterator	l2( dict ) ;
	while ( dl = (PDictLine) l2() ) {
		*ptr = dl ;
		ptr ++ ;
	}
}

LPCSTR
FepDict::get_key( DWORD i )
{
	if ( i < num_index ) {
		return (index[ num_index - i - 1 ])->get_key() ;
	}
	return 0 ;
}

PDictLine
FepDict::get_line( DWORD i )
{
	if ( i < num_index ) {
		return index[ num_index - i - 1 ] ;
	}
	return 0 ;
}

BOOL
FepDict::write( PDictLine dl )
{
	return dl->write( hFile ) ;
}

/*
 * 指定されたキーを持つ辞書行とファイルオフセットを得る
 */
PDictLine
FepDict::find( LPCSTR k, LPDWORD ofs )
{
	PDictLine	dl ;
	int			ret ;
	Iterator	l( dict ) ;

	while ( dl = (PDictLine) l() ) {
		ret = strcmp( k, dl->get_key() ) ;
		if ( !ret ) {
			*ofs = dl->get_offset() ;
			return dl ;
		} else if ( ret < 0 ) {
			/* 該当する辞書行が存在しない場合 */
			*ofs = dl->get_offset() - strlen( dl->get_key() ) - 1 ;
			return NULL ;
		}
	}
	/* 該当する辞書行が存在しない場合 */
	*ofs = next_offset ;
	return NULL ;
}

/*
 * 指定された単語を指定されたキーで辞書登録する
 */
BOOL
FepDict::add_item( LPCSTR k, LPCSTR it )
{
	PDictLine	dl ;
	DWORD		ofs, len, i ;
	LPSTR		p = (LPSTR) FileBuffer ;
	DWORD		keylen = strlen( k ), itemlen = strlen( it ) ;

	if ( !keylen || !itemlen || !read( k ) ) {
		return FALSE ;
	}
	dl = find( k, &ofs ) ;
	if ( dl ) {
		/* 辞書行が見つかったので既に項目がないか調べる */
		if ( dl->find( it ) ) {
			/* 既に項目が存在した場合 */
			return TRUE ;
		}
		*p++ = '/' ;
		strcpy( p, it ), p += itemlen ;
		*p = 0 ;
	} else {
		/* 辞書行が見つからなかったので新しい辞書行を作成する */
		strcpy( p, k ), p += keylen ;
		*p++ = ' ' ;
		*p++ = '/' ;
		strcpy( p, it ), p += itemlen ;
		*p++ = '/' ;
		*p++ = '\r' ;
		*p++ = '\n' ;
		*p = 0 ;
	}
	len = strlen( (LPSTR) FileBuffer ) ;
	if ( !insert_filebuffer( hFile, ofs, len ) ) {
		return FALSE ;
	}
	/* 項目を追加することによるオフセット増加を行う */
	for ( i = topkey ; i < KEY_SIZE ; i ++ ) {
		if ( offset[ i ] >= ofs ) {
			offset[ i ] += len ;
		}
	}
	topkey = IDX_NONE ;
	return TRUE ;
}

/*-------------------------------------------------------------------------
 * FepCtrl クラス実現部
 *-------------------------------------------------------------------------*/
void
FepCtrl::addchar( CHAR c )
{
	DWORD	len ;
	LPCSTR	kana ;
	CHAR	roma[ MAX_TOKEN ], input0, input1 ;

	/* 入力中文字列に文字を加える */
	len = strlen( input ) ;
	input[ len++ ] = c ;
	input[ len ] = 0 ;
	/* 'n'や撥音の処理を行う */
	input0 = to_lower(input[0]) ;
	input1 = to_lower(input[1]) ;
	if ( cmp_char( input0, 'n' ) ) {
		if ( input1 && !isvowel( input1 ) && input1 != 'y' ) {
			/* 'n'で始まり次が子音の場合 */
			append( new Token( input[0], "ん" ) ) ;
			if ( input1 == 'n' ) {
				strcpy( input, &input[2] ) ;
			} else {
				strcpy( input, &input[1] ) ;
			}
		}
	} else if ( cmp_char( input0, input1 ) ) {
		/* 最初と次の文字が同じだった場合 */
		append( new Token( input[0], "っ" ) ) ;
		strcpy( input, &input[1] ) ;
	}
	/* ローマ字かな変換を行う */
	CHAR	input2[ 256 ] ;
	strcpy( input2, input ) ;
	strupr( input2 ) ;
	PConvTableQuery	query = &RomaKanaQuery ;
	query->str = input2 ;
	query->plen = &len ;
	query->value = &kana ;
	if ( find_ConvTable( query ) ) {
		/* ローマ字かな変換テーブルにあった場合 */
		strncpy( roma, input, len ) ;
		roma[ len ] = 0 ;
		append( new Token( roma, kana ) ) ;
		strcpy( input, &input[ len ] ) ;
	}
}

void
FepCtrl::get_zenhira( DWORD n_token, LPSTR buf )
{
	LPSTR		ptr ;
	DWORD		len ;
	PToken		token ;
	Iterator	l( *this ) ;

	while ( token = (PToken) l() ) {
		if ( n_token ) {
			n_token -- ;
			continue ;
		}
		ptr = token->get_kana() ;
		len = strlen( ptr ) ;
		strcpy( buf, ptr ) ;
		buf += len ;
	}
	strcpy( buf, input ) ;
}

DWORD
FepCtrl::len_zenhira()
{
	PToken		token ;
	DWORD		len = 0 ;
	Iterator	l( *this ) ;

	while ( token = (PToken) l() ) {
		len += strlen( token->get_kana() ) ;
	}
	return len ;
}

void
FepCtrl::get_roma( LPSTR buf )
{
	LPSTR		ptr ;
	DWORD		len ;
	PToken		token ;
	Iterator	l( *this ) ;

	while ( token = (PToken) l() ) {
		ptr = token->get_roma() ;
		len = strlen( ptr ) ;
		strcpy( buf, ptr ) ;
		buf += len ;
	}
	strcpy( buf, input ) ;
}

BOOL
FepCtrl::chop()
{
	DWORD	len = strlen( input ) ;

	clear_proposed() ;
	if ( len ) {
		/* 入力中の文字がある場合 */
		input[ len - 1 ] = 0 ;
		/* Tokenリストには影響なし */
		return FALSE ;
	}
	/* Tokenリストから値を削除する */
	delete_last() ;
	return TRUE ;
}

void
FepCtrl::clear()
{
	/* 入力中文字をクリアする */
	input[ 0 ] = 0 ;
	/* 入力トークンをクリアする */
	List::clear() ;
	/* 変換候補をクリアする */
	clear_proposed() ;
}

void
FepCtrl::clear_proposed()
{
	/* 変換候補リストをクリアする */
	proposed.clear() ;
	/* 辞書項目をクリアする */
	dict_item = 0 ;
}

BOOL
FepCtrl::read_dict()
{
	PToken		token ;
	Iterator	l( *this ) ;

	token = (PToken) l() ;
	if ( !token ) {
		return FALSE ;
	}
	return dict->read( token->get_kana() ) ;
}

DWORD
FepCtrl::find( DWORD start )
{
	DWORD		i ;
	int			ret ;
	PPropLink	prop ;	/* 変換候補 */
	LPCSTR		dictkey, propstr ;
	Iterator	l( proposed, proposed.get_pos() ) ;

	while ( prop = (PPropLink) l() ) {
		num_token = prop->get_num_token() ;
		propstr = prop->get_str() ;
		proposed.inc_pos() ;
		for ( i = start ; ; i ++ ) {
			dictkey = dict->get_key( i ) ;
			if ( !dictkey ) {
				/* 辞書エントリの最後まで到達した場合 */
				start = i ;
				break ;
			}
			ret = strcmp( propstr, dictkey ) ;
			if ( !ret ) {
				/* 対応するキーがみつかった場合 */
				return i ;
			} else if ( ret > 0 ) {
				start = i ;
				break ;
			}
		}
	}
	/* 変換候補がない場合 */
	return IDX_NONE ;
}

BOOL
FepCtrl::conv_first()
{
	if ( input[0] ) {
		/* 入力文字列がある場合は変換できない */
		return FALSE ;
	} else if ( !read_dict() ) {
		line_idx = IDX_NONE ;
		return FALSE ;
	}
	line_idx = 0 ;
	dict_line = 0 ;
	item_idx = 0 ;
	dict_item = 0 ;
	/* 変換候補を作成する */
	proposed.make( *this ) ;
	return TRUE ;
}

BOOL
FepCtrl::conv_next()
{
	while ( line_idx != IDX_NONE ) {
		if ( dict_line ) {
			/* 辞書行が確定している場合 */
			dict_item = (PDictItem) (*dict_line->get_items())[ item_idx ] ;
			if ( dict_item ) {
				/* 辞書項目がみつかった場合 */
				item_idx ++ ;
				return TRUE ;
			}
		}
		/* 次の辞書行を検索する */
		line_idx = find( line_idx ) ;
		if ( line_idx == IDX_NONE ) {
			/* 辞書最後まで到達した場合 */
			break ;
		}
		/* 辞書行オブジェクトを得る */
		dict_line = dict->get_line( line_idx ) ;
		/* 辞書項目番号をクリアする */
		item_idx = 0 ;
	}
	/* 辞書最後まで到達している場合 */
	dict_item = 0 ;
	dict_line = 0 ;
	return FALSE ;
}

BOOL
FepCtrl::conv_prev()
{
	if ( !dict_line || !dict_item || !item_idx ) {
		dict_item = 0 ;
		return TRUE ;
	} else if  ( item_idx < 2 ) {
		return TRUE ;
	}
	/* 辞書行が確定している場合 */
	item_idx -- ;
	dict_item = (PDictItem) (*dict_line->get_items())[ item_idx - 1 ] ;
	return TRUE ;
}

void
FepCtrl::get_top( LPSTR buf )
{
	PToken		token ;
	Iterator	l( *this ) ;

	token = (PToken) l() ;
	if ( token ) {
		strcpy( buf, token->get_kana() ) ;
	} else {
		buf[0] = 0 ;
	}
}

void
FepCtrl::get_conv( LPSTR buf )
{
	if ( dict_item ) {
		LPCSTR	ptr = dict_item->get_str() ;
		strcpy( buf, ptr ) ;
	} else {
		*buf = 0 ;
	}
}

void
FepCtrl::get_all( LPSTR buf )
{
	DWORD	n_token = 0 ;

	if ( dict_item ) {
		LPCSTR	ptr = dict_item->get_str() ;
		strcpy( buf, ptr ) ;
		buf += strlen( ptr ) ;
		n_token = num_token ;
	}
	get_zenhira( n_token, buf ) ;
}

void
FepCtrl::drop_top( LPSTR buf )
{
	/* 先頭内容を取得する */
	if ( buf ) {
		get_top( buf ) ;
	}
	/* 先頭を削除する */
	delete_first() ;
}

void
FepCtrl::drop_conv( LPSTR buf )
{
	/* 変換内容を取得する */
	if ( buf ) {
		get_conv( buf ) ;
	}
	/* 変換対象をクリアする */
	clear_proposed() ;
	if ( num_token ) {
		/* 変換したトークンを削除する */
		do {
			delete_first() ;
		} while ( -- num_token ) ;
	}
}

void
FepCtrl::drop_all( LPSTR buf )
{
	/* 全体を取得する */
	if ( buf ) {
		get_all( buf ) ;
	}
	/* 全体をクリアする */
	clear() ;
}

void
FepCtrl::shrink()
{
	line_idx = 0 ;
	dict_line = 0 ;
	item_idx = 0 ;
}

void
FepCtrl::grow()
{
	PPropLink	prop ;
	Iterator	l( proposed ) ;
	BOOL		f_found = FALSE ;
	DWORD		pos = 0, n_token ;

	while ( prop = (PPropLink) l() ) {
		n_token = prop->get_num_token() ;
		if ( n_token == num_token + 1 ) {
			f_found = TRUE ;
			break ;
		}
		pos ++ ;
	}
	if ( !f_found ) {
		/* 指定数のトークンを持つ変換対象が見つからなかった場合 */
		pos = 0 ;
	}
	proposed.set_pos( pos ) ;
	line_idx = 0 ;
	dict_line = 0 ;
	item_idx = 0 ;
}

void
FepCtrl::set_dict( PFepDict d )
{
	dict = d ;
	line_idx = 0 ;
	dict_line = 0 ;
	item_idx = 0 ;
	dict_item = 0 ;
}

BOOL
FepCtrl::space()
{
	if ( !is_converting() ) {
		/* 変換中でない場合 */
		if ( !conv_first() ) {
			return FALSE ;
		}
	}
	return conv_next() ;
}

BOOL
FepCtrl::is_converting() const
{
	/* 変換中かどうか調べる */
	return dict_item ? TRUE : FALSE ;
}

BOOL
FepCtrl::is_input() const
{
	return count() || input[0] ? TRUE : FALSE ;
}

void
FepCtrl::confirm()
{
	if ( !dict_item || !dict_line ) {
		return ;
	}
	/* 変換中の単語を辞書行先頭に移動させる */
	dict_line->get_items()->move_top( dict_item ) ;
	/* 辞書を更新する */
	dict->write( dict_line ) ;
}

/*
 * 辞書に項目を登録する
 */
BOOL
FepCtrl::add_item( LPCSTR item )
{
	CHAR	key[ 256 ] ;

	get_zenhira( 0, key ) ;
	return dict->add_item( key, item ) ;
}

void
FepCtrl::get_fep_line( LPSTR buf, DWORD size )
{
	CHAR	c ;
	LPCSTR	str ;
	LPSTR	ptr ;
	BOOL	f_found ;
	DWORD	rest, idx, len ;

	if ( get_muhenkan_str( buf ) ) {
		/* 無変換操作中の場合 */
		strcat( buf, "_" ) ;
		return ;
	} else if ( !dict_item || !dict_line ) {
		/* 変換中ではない場合 */
		get_zenhira( 0, buf ) ;
		strcat( buf, "_" ) ;
		return ;
	} else if ( item_idx < 3 ) {
		/* 変換中だが2候補目までの場合 */
		str = dict_item->get_str() ;
		strcpy( buf, str ) ;
		buf += strlen( str ) ;
		get_zenhira( num_token, buf ) ;
		strcat( buf, "_" ) ;
		return ;
	}
	/* 3候補目以降の場合 */
	ptr = buf ;
	rest = size ;
	f_found = FALSE ;
	idx = 0 ;
	c = ' ' ;
	while ( 1 ) {
		PDictItem	di = (PDictItem) (*dict_line->get_items())[ idx ] ;
		if ( !di ) {
			/* 変換候補がなくなった場合 */
			break ;
		}
		str = di->get_str() ;
		len = strlen( str ) ;
		if ( rest < len + 3 ) {
			if ( f_found ) {
				break ;
			}
			ptr = buf ;
			rest = size ;
		}
		if ( ++ idx == item_idx ) {
			/* 変換中の項目の場合 */
			f_found = TRUE ;
			*ptr++ = '(' ;
			c = ')' ;
		} else {
			*ptr++ = c ;
			c = ' ' ;
		}
		strcpy( ptr, str ) ;
		ptr += len ;
		rest -= len + 1 ;
	}
	*ptr++ = c ;
	*ptr = 0 ;
}

/*
 * 無変換モードの文字列を求める
 */
BOOL
FepCtrl::get_muhenkan_str( LPSTR buf )
{
	CHAR	buf2[ 256 ] ;

	if ( !buf ) {
		return FALSE ;
	}
	switch ( muhenkan ) {
	case MODE_ZENHIRA:
		get_zenhira( 0, buf ) ;
		break ;
	case MODE_ZENKATA:
		get_zenhira( 0, buf2 ) ;
		hira2kata( buf2, buf ) ;
		break ;
	case MODE_HANKATA:
		get_zenhira( 0, buf2 ) ;
		zen2han( buf2, buf ) ;
		break ;
	case MODE_ZENROMA:
		get_roma( buf2 ) ;
		han2zen( buf2, buf ) ;
		break ;
	case MODE_HANROMA:
		get_roma( buf ) ;
		break ;
	default:
		return FALSE ;
	}
	return TRUE ;
}

/*
 * 無変換モードに切り替える
 */
void
FepCtrl::set_muhenkan( CHAR c )
{
	switch ( c ) {
	case 'U' - '@':
		muhenkan = MODE_ZENHIRA ;
		break ;
	case 'I' - '@':
		muhenkan = MODE_ZENKATA ;
		break ;
	case 'O' - '@':
		if ( muhenkan == MODE_ZENROMA || muhenkan == MODE_HANROMA ) {
			muhenkan = MODE_HANROMA ;
		} else {
			muhenkan = MODE_HANKATA ;
		}
		break ;
	case 'P' - '@':
		muhenkan = MODE_ZENROMA ;
		break ;
	default:
		muhenkan = MODE_NORMAL ;
		line_idx = 0 ;
		dict_line = 0 ;
		item_idx = 0 ;
		dict_item = 0 ;
		return ;
	}
	/* 入力がなかったら無変換モードを解除する */
	if ( !is_input() ) {
		muhenkan = MODE_NORMAL ;
	}
}

/*
 * 無変換モードの文字列を取得し，モードをクリアする
 */
void
FepCtrl::drop_muhenkan( LPSTR buf )
{
	get_muhenkan_str( buf ) ;
	set_muhenkan( 0 ) ;
	clear() ;
}

/*-------------------------------------------------------------------------
 * PropLink クラス実現部
 *-------------------------------------------------------------------------*/

/*-------------------------------------------------------------------------
 * FepProposed クラス実現部
 *-------------------------------------------------------------------------*/
/*
 * 入力文字列から変換候補を作成する。
 */
void
FepProposed::make( FepCtrl& fc )
{
	CHAR		last = 0 ;
	DWORD		i, j, len = fc.len_zenhira() ;
	LPSTR		str = new CHAR[ len + 10 ] ;

	/* 候補をすべてクリアする */
	clear() ;
	/* 参照位置を初期化する */
	pos = 0 ;
	/* 入力トークンの数だけループ */
	for ( j = fc.count() ; j ; j -- ) {
		LPSTR		ptr = str, hira, last_roma = 0 ;
		Iterator	l( fc ) ;

		for ( i = 0 ; i < j ; i ++ ) {
			PToken	token = (PToken) l() ;
			hira = token->get_kana() ;
			last_roma = token->get_roma() ;
			strcpy( ptr, hira ) ;
			ptr += strlen( hira ) ;
		}
		if ( last ) {
			/* 次のローマ字の先頭アルファベット */
			ptr[0] = last ;
			ptr[1] = 0 ;
			/* かな文字＋ローマ字先頭を追加 */
			append( new PropLink( str, j ) ) ;
		}
		*ptr = 0 ;
		/* かな文字を追加 */
		append( new PropLink( str, j ) ) ;
		/* 次の候補のためにローマ字先頭アルファベットを求める */
		last = *last_roma ;
	}
	delete[] str ;
	return ;
}
