#define		STRICT
#include	<windows.h>
#include	"jpconv.h"

#ifdef	_WIN32_WCE
#define		islower(c)		((c)>='a'&&(c)<='z')
#define		toupper(c)		(islower(c)?(c)-'a'+'A':(c))
int
strnicmp( LPCSTR src, LPCSTR dst, int len )
{
	CHAR	c = 0, c1 ;

	while ( len && (c = *src++) ) {
		c = toupper( c ) ;
		c1 = *dst++ ;
		c1 = toupper( c1 ) ;
		if ( c != c1 ) {
			return c - c1 ;
		}
		len -- ;
	}
	c = toupper( c ) ;
	c1 = *dst++ ;
	c1 = toupper( c1 ) ;
	return len ? c - c1 : 0 ;
}
#endif	/* _WIN32_WCE */

#define     isvowel(c)      (toupper(c)=='A'||toupper(c)=='I'||toupper(c)=='U'||toupper(c)=='E'||toupper(c)=='O')

typedef	LPCSTR	PConvTable ;

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

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

	while ( *pct ) {
		keyptr = key ;
		while ( ( c = *pct++ ) != '\t' ) {
			*keyptr++ = c ;
		}
		*keyptr = 0 ;
		len = strlen( key ) ;
		ret = fIgnoreCase ? strnicmp( key, str, len ) : strncmp( key, str, len ) ;
		if ( !ret ) {
			*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, BOOL fIgnoreCase )
{
	CHAR	c ;
	DWORD	len ;
	LPCSTR	value ;

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

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

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

const PConvTable	UppLowBuf = {
#include	"upplow.txt"
} ;
ConvTableQuery	UppLowQuery = { UppLowBuf } ;

void
upper2lower( LPCSTR src, LPSTR dst )
{
	conv_all( &UppLowQuery, src, dst, FALSE ) ;
}

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

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

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

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

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


LPCSTR	Itaiji[] = {
#include	"itai.txt"
} ;
int		NumItaiji = sizeof Itaiji / sizeof *Itaiji ;

void
itaiconv( LPCSTR src, LPSTR dst )
{
	int		i ;
	LPCSTR	ptr ;
	BYTE	c, c1, c2 ;

	while ( c = *src ++ ) {
		if ( iskanji( c ) ) {
			c1 = c, c2 = *src++ ;
			for ( i = 0 ; i < NumItaiji ; i ++ ) {
				ptr = Itaiji[ i ] ;
				while ( c = *ptr++ ) {
					if ( (BYTE) (*ptr++) == c2 && c == c1 ) {
						if ( !*ptr ) {
							ptr = Itaiji[ i ] ;
						}
						c1 = *ptr++, c2 = *ptr++ ;
						break ;
					}
				}
				if ( c ) {
					break ;
				}
			}
			*dst++ = c1 ;
			*dst++ = c2 ;
		} else {
			*dst++ = c ;
		}
	}
	*dst = 0 ;
}

DWORD
itaiIndex( LPCSTR buf )
{
	int		i ;
	LPCSTR	ptr ;
	BYTE	c, c1, c2 ;

	c1 = *buf++ ;
	if ( iskanji( c1 ) ) {
		c2 = *buf ;
		for ( i = 0 ; i < NumItaiji ; i ++ ) {
			ptr = Itaiji[ i ] ;
			while ( c = *ptr++ ) {
				if ( (BYTE) (*ptr++) == c2 && c == c1 ) {
					return i + 1 ;
				}
			}
		}
	}
	return 0 ;
}

/*
 * 指定されたひらがな／かたかなを異体字リストの先頭の文字にする
 */
void
hiraNormalize( LPCSTR src, LPSTR dst, BOOL fNoAscii )
{
	int		i ;
	LPCSTR	ptr ;
	BYTE	c, c1, c2 ;

	while ( c1 = *src++ ) {
		if ( iskanji( c1 ) ) {
			c2 = *src++ ;
			for ( i = 0 ; i < NumItaiji ; i ++ ) {
				ptr = Itaiji[ i ] ;
				while ( c = *ptr++ ) {
					if ( (BYTE) (*ptr++) == c2 && c == c1 ) {
						ptr = Itaiji[ i ] ;
						c1 = *ptr++ ;
						c2 = *ptr++ ;
						break ;
					}
				}
				if ( c ) {
					break ;
				}
			}
			*dst++ = c1 ;
			*dst++ = c2 ;
		} else if ( !fNoAscii ) {
			*dst++ = c1 ;
		}
	}
	*dst = 0 ;
}

void
RomaText::clear()
{
	*m_szRoma = *m_szKana = 0 ;
	m_dwNumToken = 0 ;
	m_bRomaPos[ m_dwNumToken ] = m_bKanaPos[ m_dwNumToken ] = 0 ;
}

void
RomaText::addchar( CHAR c )
{
	DWORD	len ;

	len = strlen( m_szRoma ) ;
	m_szRoma[ len++ ] = c ;
	m_szRoma[ len ] = 0 ;
}

void
RomaText::addromachar( CHAR c )
{
	LPCSTR	kana ;
	CHAR	input0, input1 ;
	DWORD	romaLen, kanaLen, len ;
	PConvTableQuery	query = &RomaKanaQuery ;

	/* 入力中文字列に文字を加える */
	addchar( c ) ;
	romaLen = m_bRomaPos[ m_dwNumToken ] ;
	kanaLen = m_bKanaPos[ m_dwNumToken ] ;
	/* 'N'や撥音の処理を行う */
	input0 = toupper( m_szRoma[ romaLen ] ) ;
	input1 = toupper( m_szRoma[ romaLen+1 ] ) ;
	if ( input0 == 'N' ) {
		if ( input1 && !isvowel( input1 ) && input1 != 'Y' ) {
			/* 'N'で始まり次が子音の場合 */
			m_dwNumToken ++ ;
			strcat( m_szKana, "ん" ) ;
			m_bKanaPos[ m_dwNumToken ] = strlen( m_szKana ) ;
			m_bRomaPos[ m_dwNumToken ] = m_bRomaPos[ m_dwNumToken - 1 ] + (input1 == 'N' ? 2 : 1) ;
		}
	} else if ( input0 == input1 ) {
		/* 最初と次の文字が同じだった場合 */
		m_dwNumToken ++ ;
		strcat( m_szKana, "っ" ) ;
		m_bKanaPos[ m_dwNumToken ] = strlen( m_szKana ) ;
		m_bRomaPos[ m_dwNumToken ] = m_bRomaPos[ m_dwNumToken - 1 ] + 1 ;
	}
	romaLen = m_bRomaPos[ m_dwNumToken ] ;
	/* ローマ字かな変換を行う */
	query->str = &m_szRoma[ romaLen ] ;
	query->plen = &len ;
	query->value = &kana ;
	if ( find_ConvTable( query, TRUE ) ) {
		/* ローマ字かな変換テーブルにあった場合 */
		m_dwNumToken ++ ;
		strcat( m_szKana, kana ) ;
		m_bKanaPos[ m_dwNumToken ] = strlen( m_szKana ) ;
		m_bRomaPos[ m_dwNumToken ] = m_bRomaPos[ m_dwNumToken - 1 ] + (BYTE) len ;
	}
}

void
RomaText::addstr( LPCSTR str )
{
	DWORD	len = strlen( m_szRoma ) ;
	DWORD	romaLen = m_bRomaPos[ m_dwNumToken ] ;

	if ( len > romaLen ) {
		m_dwNumToken ++ ;
		strcat( m_szKana, &m_szRoma[ romaLen ] ) ;
		m_bKanaPos[ m_dwNumToken ] = strlen( m_szKana ) ;
		m_bRomaPos[ m_dwNumToken ] = (BYTE) len ;
	}
	m_dwNumToken ++ ;
	strcat( m_szKana, str ) ;
	strcat( m_szRoma, str ) ;
	m_bKanaPos[ m_dwNumToken ] = strlen( m_szKana ) ;
	m_bRomaPos[ m_dwNumToken ] = strlen( m_szRoma ) ;
}

void
RomaText::backspace()
{
	DWORD	len = strlen( m_szRoma ) ;

	if ( len > m_bRomaPos[ m_dwNumToken ] ) {
		m_szRoma[ len - 1 ] = 0 ;
	} else if ( m_dwNumToken ) {
		m_dwNumToken -- ;
		m_szRoma[ m_bRomaPos[ m_dwNumToken ] ] = 0 ;
		m_szKana[ m_bKanaPos[ m_dwNumToken ] ] = 0 ;
	}
}

void
RomaText::getroma( LPSTR roma ) const
{
	strcpy( roma, m_szRoma ) ;
}

void
RomaText::getkana( LPSTR kana ) const
{
	DWORD	len = strlen( m_szRoma ) ;

	strcpy( kana, m_szKana ) ;
	strcat( kana, &m_szRoma[ m_bRomaPos[ m_dwNumToken ] ] ) ;
}

void
RomaText::setkana( LPCSTR kana )
{
	strcpy( m_szKana, kana ) ;
}

/*
 * 無変換モードの文字列を求める
 */
void
RomaText::getmuhenkan( DWORD muhenkan, LPSTR buf ) const
{
	CHAR	buf2[ 256 ] ;

	switch ( muhenkan ) {
	case ROMA_NORMAL:
	case ROMA_ZENHIRA:
		getkana( buf ) ;
		break ;
	case ROMA_ZENKATA:
		getkana( buf2 ) ;
		hira2kata( buf2, buf ) ;
		break ;
	case ROMA_HANKATA:
		getkana( buf2 ) ;
		zen2han( buf2, buf ) ;
		break ;
	case ROMA_ZENROMA:
		getroma( buf2 ) ;
		han2zen( buf2, buf ) ;
		break ;
	case ROMA_HANROMA:
		getroma( buf ) ;
		break ;
	default:
		*buf = 0 ;
		break ;
	}
}
