/*
    KCTRL - Dynamic Link Library of Japanization Toolkit for Windows CE
    Copyright (C) 1997-2001  ITO, Eiichiro

    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.1 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
 */
#define	STRICT
#include	<windows.h>
#include	<tchar.h>
#include	"kctrl.h"

#define		Version			(28)

#ifdef	_WIN32_WCE
BOOL WINAPI
DllMain( HINSTANCE hInstDll, DWORD reason, LPVOID situation )
{
	return TRUE ;
}
#endif	/* _WIN32_WCE */

extern	BOOL	InitLocal( void ) ;
extern	void	ReleaseLocal( void ) ;

TCHAR	MessageBuf[ 128 ] ;
/* フォントメトリック */
DWORD	FontZenVW = 0 ;			/* 全角フォントの表示幅 */
DWORD	FontHanVW = 0 ;			/* 半角フォントの表示幅 */
DWORD	FontVH = 0 ;			/* 全半角フォントの表示高さ */
/* タブサイズ */
DWORD	TabWidth = 8 ;
/* コード変換共通作業領域 */
DWORD	ConvCount = 0 ;			/* 変換後文字数 */
LPTSTR	ConvPtr = 0 ;			/* 変換先領域 */
DWORD	Stat = 0 ;				/* 直前状態 */
DWORD	F_convmode = MODE_NORMAL ;	/* 0=Normal, 1=エスケープ文字'\'使用, 2=BASE64 */
/* JIS->UNICODE変換作業領域 */
TCHAR	PendingBuffer[ 128 ] ;	/* 保留バッファ */
int		PendingSize = 0 ;		/* 保留バッファサイズ */
/* UNICODE->JIS変換作業領域 */
TCHAR	C1 = 0 ;				/* 直前文字 */

/* 半角→全角変換テーブル(0xA0〜0xDF) */
const	WORD	hank_to_zenk[] = {
	0x2020, 0x8142, 0x8175, 0x8176, 0x8141, 0x8145, 0x8392, 0x8340,
	0x8342, 0x8344, 0x8346, 0x8348, 0x8383, 0x8385, 0x8387, 0x8362,
	0x815B, 0x8341, 0x8343, 0x8345, 0x8347, 0x8349, 0x834A, 0x834C,
	0x834E, 0x8350, 0x8352, 0x8354, 0x8356, 0x8358, 0x835A, 0x835C,
	0x835E, 0x8360, 0x8363, 0x8365, 0x8367, 0x8369, 0x836A, 0x836B,
	0x836C, 0x836D, 0x836E, 0x8371, 0x8374, 0x8377, 0x837A, 0x837D,
	0x837E, 0x8380, 0x8381, 0x8382, 0x8384, 0x8386, 0x8388, 0x8389,
	0x838A, 0x838B, 0x838C, 0x838D, 0x838F, 0x8393, 0x814A, 0x814B,
} ;
/* ガ〜ド */
const	WORD	ga_do[] = {
	0x834B, 0x834D, 0x834F, 0x8351, 0x8353,
	0x8355, 0x8357, 0x8359,	0x835B, 0x835D,
	0x835F, 0x8361, 0x8364, 0x8366, 0x8368,
} ;
/* バ〜ボ */
const	WORD	ba_bo[] = {
    0x836F, 0x8372, 0x8375, 0x8378, 0x837B,
} ;
/* パ〜ポ */
const	WORD	pa_po[] = {
    0x8370, 0x8373, 0x8376, 0x8379, 0x837C,
} ;

BOOL
InitKanjiControls( void )
{
	return InitLocal() ;
}

void
ReleaseKanjiControls( void )
{
	ReleaseLocal() ;
}

#include	"han2zen.c"

/*
 * エラーダイアログ表示ルーチン
 */
void
error_dialog( LPCTSTR message, LPCTSTR opt )
{
	TCHAR	buf[ 128 ] ;

	SetCursor( NULL ) ;
	wsprintf( buf, TEXT("%s:%s\n"), message, opt ) ;
	MessageBox( NULL,
				buf,
				TEXT("Error"),
				MB_ICONSTOP|MB_OK ) ;
}

/*
 * デバッグダイアログ表示ルーチン
 */
void
debug_dialog( LPCTSTR message, LPCTSTR opt )
{
	TCHAR	buf[ 128 ] ;

	SetCursor( NULL ) ;
	wsprintf( buf, TEXT("%s:%s\n(ErrorCode=%d)"), message, opt, GetLastError() ) ;
	MessageBox( NULL,
				buf,
				TEXT("Error"),
				MB_ICONSTOP|MB_OK ) ;
}

DWORD
GetFontZW( void )
{
	return FontZenVW ;
}

DWORD
GetFontHW( void )
{
	return FontHanVW ;
}

DWORD
GetFontH( void )
{
	return FontVH ;
}

void
SetTabWidth( DWORD tab )
{
	TabWidth = tab ;
}

DWORD
GetTabWidth( void )
{
	return TabWidth ;
}

DWORD
GetKVersion( void )
{
	return Version ;
}

/*
 * JIS <-> SJIS コード変換関係
 */
/*
 * 　名称：シフトＪＩＳ→ＪＩＳコード変換
 * 　略称：sjis2jis_char
 * 　概要：シフトＪＩＳコードをＪＩＳコードに変換する
 * 引き数：sjis(WORD) = シフトＪＩＳコード
 * 戻り値：WORD = ＪＩＳコード
 */
WORD
sjis2jis_char( WORD sjis )
{
	BYTE	c1 = sjis / 256 ;
	BYTE	c2 = sjis % 256 ;

    c1 -= (c1 <= 0x9F) ? 0x70 : 0xB0 ;
    c1 <<= 1 ;
    if ( c2 < 0x9F ) {
        c2 -= (c2 < 0x7F) ? 0x1F : 0x20 ;
        c1 -- ;
    } else {
        c2 -= 0x7E ;
	}
	return c1 << 8 | c2 ;
}

/*
 * 　名称：ＪＩＳ→シフトＪＩＳコード変換
 * 　略称：jis2sjis_char
 * 　概要：ＪＩＳコードをシフトＪＩＳコードに変換する
 * 引き数：jis(WORD) = ＪＩＳコード
 * 戻り値：WORD = シフトＪＩＳコード
 */
WORD
jis2sjis_char( WORD jis )
{
	BYTE	c1 = jis / 256 ;
	BYTE	c2 = jis % 256 ;

	if ( c1 & 1 ) {
		c1 >>= 1 ;
		c1 += 0x71 ;
		c2 += 0x1F ;
		if ( c2 >= 0x7F ) {
			c2 ++ ;
		}
	} else {
		c1 >>= 1 ;
		c1 += 0x70 ;
		c2 += 0x7E ;
	}
	if ( c1 > 0x9F ) {
		c1 += 0x40 ;
	}
	return (c1 << 8) | c2 ;
}

static	BOOL	F_base64 = FALSE ;
static	TCHAR	Base64Buf[3] ;
static	int		Base64Len = 0 ;
static	TCHAR	Base64Table[] = TEXT("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/") ;

static	void
PUT_base64( TCHAR c )
{
	Base64Buf[ Base64Len ++ ] = c ;
	if ( Base64Len >= 3 ) {
		Base64Len = 0 ;
		c = Base64Buf[0] >> 2 ;
		putchar_codeconv( Base64Table[c] ) ;
		c = ((Base64Buf[0] & 0x03) << 4) | (Base64Buf[1] >> 4) ;
		putchar_codeconv( Base64Table[c] ) ;
		c = ((Base64Buf[1] & 0x0F) << 2) | (Base64Buf[2] >> 6) ;
		putchar_codeconv( Base64Table[c] ) ;
		c = Base64Buf[2] & 0x3F ;
		putchar_codeconv( Base64Table[c] ) ;
	}
}

static	void
PUT_base64_flush( void )
{
	TCHAR	c ;

	if ( !Base64Len ) {
		return ;
	}
	c = Base64Buf[0] >> 2 ;
	putchar_codeconv( Base64Table[c] ) ;
	c = (Base64Buf[0] & 0x03) << 4 ;
	if ( Base64Len == 1 ) {
		putchar_codeconv( Base64Table[c] ) ;
		putchar_codeconv( '=' ) ;
	} else {
		c |= Base64Buf[1] >> 4 ;
		putchar_codeconv( Base64Table[c] ) ;
		c = (Base64Buf[1] & 0x0F) << 2 ;
		putchar_codeconv( Base64Table[c] ) ;
	}
	putchar_codeconv( '=' ) ;
}

static	void
start_base64( BOOL flag )
{
	if ( flag ) {
		puts_codeconv( TEXT("=?ISO-2022-JP?B?") ) ;
	}
	Base64Len = 0 ;
	F_base64 = TRUE ;
}

static	void
end_base64( BOOL flag )
{
	if ( F_base64 ) {
		F_base64 = FALSE ;
		PUT_base64_flush() ;
		if ( flag ) {
			putchar_codeconv( TEXT('?') ) ;
			putchar_codeconv( TEXT('=') ) ;
		}
	}
}

static	void
PUT_jis( TCHAR c )
{
	if ( F_base64 ) {
		PUT_base64( c ) ;
	} else {
		putchar_codeconv( c ) ;
	}
}

/* 
 * SJIS→JISコード変換
 */
static	void
sjis_jis( WORD sjis )
{
	WORD	jis = sjis2jis_char( sjis ) ;

	if ( F_convmode == MODE_QUOTE ) {
		TCHAR	c = jis / 256 ;
		if ( c == '"' ) {
			putchar_codeconv( TEXT('\\') ) ;
		}
		putchar_codeconv( c ) ;
		c = jis % 256 ;
		if ( c == '"' ) {
			putchar_codeconv( TEXT('\\') ) ;
		}
		putchar_codeconv( c ) ;
	} else {
		PUT_jis( (TCHAR) (jis / 256) ) ;
		PUT_jis( (TCHAR) (jis % 256) ) ;
	}
}

static	void
literals( int mode )
{
	switch ( mode ) {
	case 1:
		if ( F_convmode == MODE_BASE64 ) {
			start_base64( TRUE ) ;
		}
		PUT_jis( ESC ) ;	PUT_jis('$') ;	PUT_jis('B') ;	//PUT_jis('@') ;
		break ;
	case 2:
		PUT_jis( ESC ) ;	PUT_jis('(') ;	PUT_jis('B') ;	//PUT_jis('J') ;
		end_base64( TRUE ) ;
		break ;
	case 3:
		PUT_jis( 0x0E ) ;
		break ;
	case 4:
		PUT_jis( 0x0F ) ;
		break ;
	}
}

#define		IS_KANJI(c)		((c)&0xFF00)
#define		IS_HANKANA(c)	((0xA0<=(c))&&((c)<=0xDF))

/*
 * 　名称：UNICODE->JISコード変換メイン
 * 　略称：unicode_jis_char
 * 　概要：UNICODE->JISコード変換を行う。setup_codeconvのパラメータに
 * 　　　：応じて，文字数の計算または実際のコード変換を行う
 * 引き数：c = UNICODE文字(TCHAR)
 * 戻り値：なし
 * 使用法：1.setup_codeconv(0)をcall
 * 　　　：2.UNICODE文字列内の文字全てについてunicode_jis_charをcall
 * 　　　：3.unicode_jis_char(0)でバッファをフラッシュ
 * 　　　：4.get_codeconv()で，変換に必要な文字数を得て，メモリ確保する
 * 　　　：5.setup_codeconv( [4.で確保したメモリ] )をcall
 * 　　　：6.UNICODE文字列内の文字全てについてunicode_jis_charをcall
 * 　　　：7.unicode_jis_char(0)でバッファをフラッシュ
 * 　　　：以上でコード変換が行われる。
 */
void
unicode_jis_char( TCHAR c )
{
	if ( F_convmode == MODE_NOCONV ) {
		putchar_codeconv( c ) ;
		return ;
	}
	if ( !c ) {
		/* flush static buffer */
		if ( Stat == 4 ) {
			sjis_jis( hank_to_zenk[ C1 - C_KANA ] ) ;
		}
		if ( Stat == 2 || Stat == 4 ) {
			literals( 2 ) ;
		}
		Stat = 0 ;
		/* 最後に０を追加する */
		PUT_jis( 0 ) ;
		return ;
	}
	/* 一旦SJISコードになおす */
	c = unicode2sjis_char( c ) ;
TOP:
	switch ( Stat ) {
	case 0:	/* 半角英数字モード */
		if ( IS_KANJI( c ) ) {
			/* 漢字の場合 */
			literals( 1 ) ;
			sjis_jis( c ) ;
			Stat = 2 ;
		} else if ( IS_HANKANA( c ) ) {
			/* 半角カナの場合 */
			C1 = c ;
			literals( 1 ) ;
			Stat = 4 ;
		} else {
			PUT_jis( c ) ;
			// Stat = 0 ;
		}
		break ;
	case 2:	/* 漢字モード */
		if ( IS_KANJI( c ) ) {
			/* 漢字の場合 */
			sjis_jis( c ) ;
			// Stat = 2 ;
		} else if ( IS_HANKANA( c ) ) {
			/* 半角カナの場合 */
			C1 = c ;
			Stat = 4 ;
		} else {
			/* 英数字の場合 */
			literals( 2 ) ;
			Stat = 0 ;
			goto TOP ;
		}
		break ;
	case 4:	/* 半角カナモード */
		if ( c == C_DAKUTEN ) {
			/* 濁点の場合 */
			if ( C1 == C_U ) {
				/* ヴ */
				sjis_jis( 0x8394 ) ;
			} else if ( C1 >= C_KA && C1 <= C_TO ) {
				/* ガ〜ド */
				sjis_jis( ga_do[ C1 - C_KA ] ) ;
			} else if ( C1 >= C_HA && C1 <= C_HO ) {
				/* バ〜ボ */
				sjis_jis( ba_bo[ C1 - C_HA ] ) ;
			} else {
				sjis_jis( hank_to_zenk[ C1 - C_KANA ] ) ;
				sjis_jis( hank_to_zenk[ c  - C_KANA ] ) ;
			}
			Stat = 2 ;
		} else if ( c == C_HANDAKU ) {
			/* 半濁点の場合 */
			if ( C1 >= C_HA && C1 <= C_HO ) {
				/* パ〜ポ */
				sjis_jis( pa_po[ C1 - C_HA ] ) ;
			} else {
				sjis_jis( hank_to_zenk[ C1 - C_KANA ] ) ;
				sjis_jis( hank_to_zenk[ c  - C_KANA ] ) ;
			}
			Stat = 2 ;
		} else if ( IS_HANKANA( c ) ) {
			/* 半角カナの場合 */
			sjis_jis( hank_to_zenk[ C1 - C_KANA ] ) ;
			C1 = c ;
			Stat = 4 ;
		} else if ( IS_KANJI( c ) ) {
			/* 漢字の場合 */
			sjis_jis( hank_to_zenk[ C1 - C_KANA ] ) ;
			Stat = 2 ;
			goto TOP ;
		} else {
			/* 英数字の場合 */
			sjis_jis( hank_to_zenk[ C1 - C_KANA ] ) ;
			literals( 2 ) ;
			Stat = 0 ;
			goto TOP ;
		}
		break ;
	}
}

/*
 * JIS→UNICODEコード変換
 */
static	void
clear_pending( void )
{
	PendingSize = 0 ;
}

static	void
add_pending( TCHAR c )
{
	PendingBuffer[ PendingSize ++ ] = c ;
}

static	void
put_pending( void )
{
	LPTSTR	ptr = PendingBuffer ;

	if ( !PendingSize ) {
		return ;
	}
	do {
		putchar_codeconv( *ptr++ ) ;
	} while ( -- PendingSize ) ;
}

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

enum jis_unicode {
	ANK_NORMAL, ANK_ESC, ANK_ESC1, ANK_ESC2,
	JIS_NORMAL, JIS_ESC, JIS_ESC1, JIS_ESC2, JIS_2BYTE,
	SJIS_2BYTE
} ;

/*
 * 　名称：JIS->UNICODEコード変換メイン
 * 　略称：jis_unicode_char
 * 　概要：JIS->UNICODEコード変換を行う。setup_codeconvのパラメータに
 * 　　　：応じて，文字数の計算または実際のコード変換を行う
 * 引き数：c = JIS文字(TCHAR)
 * 戻り値：なし
 * 使用法：1.setup_codeconv(0)をcall
 * 　　　：2.JIS文字列内の文字全てについてjis_unicode_charをcall
 * 　　　：3.jis_unicode_char(0)でバッファをフラッシュ
 * 　　　：4.get_codeconv()で，変換に必要な文字数を得て，メモリ確保する
 * 　　　：5.setup_codeconv( [4.で確保したメモリ] )をcall
 * 　　　：6.JIS文字列内の文字全てについてjis_unicode_charをcall
 * 　　　：7.jis_unicode_char(0)でバッファをフラッシュ
 * 　　　：以上でコード変換が行われる。
 */
void
jis_unicode_char( TCHAR c )
{
	WORD	sjis ;
	TCHAR	last ;

	if ( !c ) {
		/* c == 0 の場合はバッファをフラッシュする */
		put_pending() ;
		return ;
	}
	add_pending( c ) ;
	switch ( Stat ) {
	case ANK_NORMAL:
		if ( c >= 0x100 ) {
			goto put_ank_normal ;
		} else if ( is_sjis1byte( c ) ) {
			Stat = SJIS_2BYTE ;
			break ;
		} else if ( c != ESC ) {
			if ( c == 0xA0 ) {
				PendingBuffer[ PendingSize - 1 ] = 0x20 ;
			}
			goto put_ank_normal ;
		}
		Stat = ANK_ESC ;
		break ;
	case ANK_ESC:	/* ESC */
		if ( c == '$' ) {
			Stat = ANK_ESC1 ;
			break ;
		} else if ( c == '(' ) {
			Stat = ANK_ESC2 ;
			break ;
		}
		goto put_ank_normal ;
	case ANK_ESC1:	/* ESC $ */
		if ( c != '@' && c != 'B' ) {
			goto put_ank_normal ;
		}
		/* ESC $ [@B] を検出したのでJISモードに移行 */
		clear_pending() ;
		Stat = JIS_NORMAL ;
		break ;
	case ANK_ESC2:	/* ESC ( */
		if ( c != 'J' && c != 'B' ) {
			goto put_ank_normal ;
		}
		/* ESC ( B を検出した */
		clear_pending() ;
		Stat = ANK_NORMAL ;
		break ;
	case JIS_NORMAL:	/* (JIS) */
		if ( c == ESC ) {
			Stat = JIS_ESC ;
			break ;
		} else if ( c < ' ' ) {
			goto put_ank_normal ;
		}
		Stat = JIS_2BYTE ;
		break ;
	case JIS_ESC:	/* (JIS) ESC */
		if ( c == '$' ) {
			Stat = JIS_ESC1 ;
			break ;
		} else if ( c == '(' ) {
			Stat = JIS_ESC2 ;
			break ;
		}
		goto put_jis_normal ;
	case JIS_ESC1:	/* (JIS) ESC $ */
		if ( c != '@' && c != 'B' ) {
			goto put_jis_normal ;
		}
		/* ESC $ [@B] を検出した */
		clear_pending() ;
		Stat = JIS_NORMAL ;
		break ;
	case JIS_ESC2:	/* (JIS) ESC ( */
		if ( c != 'J' && c != 'B' && c != 'I' ) {
			goto put_jis_normal ;
		}
		clear_pending() ;
		Stat = ANK_NORMAL ;
		break ;
	case JIS_2BYTE:	/* (JIS) 2byte */
		last = PendingBuffer[0] ;
		/* JIS→SJIS→UNICODE変換して出力する */
		sjis = jis2sjis_char( (WORD) ((last << 8) | c) ) ;
		c = sjis2unicode_char( sjis ) ;
		putchar_codeconv( c ) ;
		/* JIS_NORMALモードに移行 */
		clear_pending() ;
		Stat = JIS_NORMAL ;
		break ;
	case SJIS_2BYTE:
		/* 2バイト化して出力する */
		last = PendingBuffer[0] ;
		c = (last << 8) | c ;
		c = sjis2unicode_char( c ) ;
		putchar_codeconv( c ) ;
		/* ANK_NORMALモードに移行 */
		clear_pending() ;
		Stat = ANK_NORMAL ;
		break ;
	}
	return ;

put_ank_normal:
	/* PendingBufferを出力してモードをANK_NORMALにする */
	Stat = ANK_NORMAL ;
	put_pending() ;
	return ;

put_jis_normal:
	/* PendingBufferを出力してモードをJIS_NORMALにする */
	Stat = JIS_NORMAL ;
	put_pending() ;
	return ;
}

/*
 * 　名称：コード変換開始
 * 　略称：setup_codeconv
 * 　概要：変換に先立って変換先の領域を指定する
 * 引き数：ptr = 変換先の領域
 * 戻り値：なし
 * 　備考：ptr = NULL とした場合，実際に領域への書き込みは行われず
 * 　　　：文字数のカウントのみ行う。後で，getcount_codeconvで変換
 * 　　　：に必要な文字数を得ることができる。
 */
void
setup_codeconv( LPTSTR ptr )
{
	ConvPtr = ptr ;
	ConvCount = 0 ;
	Stat = 0 ;
	C1 = 0 ;
	F_base64 = FALSE ;
}

/*
 * 　名称：変換先文字数取得
 * 　略称：getcount_codeconv
 * 　概要：コード変換に必要な領域サイズを求める
 * 引き数：なし
 * 戻り値：文字数(DWORD)
 */
DWORD
getcount_codeconv( void )
{
	return ConvCount ;
}

/*
 * 　名称：文字格納
 * 　略称：putchar_codeconv
 * 　概要：1文字変換バッファに格納する
 * 引き数：c = 格納する文字
 * 戻り値：なし
 */
void
putchar_codeconv( TCHAR c )
{
	if ( ConvPtr ) {
		/* 領域に書き込む */
		*ConvPtr++ = c ;
	} else {
		/* 文字数をカウントする */
		ConvCount ++ ;
	}
}

/*
 * 　名称：文字列格納
 * 　略称：puts_codeconv
 * 　概要：文字列を変換バッファに格納する
 * 引き数：str = 格納する文字列
 * 戻り値：なし
 */
void
puts_codeconv( LPCTSTR str )
{
	TCHAR	c ;

	while ( c = *str++ ) {
		putchar_codeconv( c ) ;
	}
}

/*
 * 　名称：変換モード設定
 * 　略称：set_convmode
 * 　概要：コード変換時のモードを設定する
 * 引き数：mode = 0:標準, 1:エスケープ文字使用, 2:Base64
 * 戻り値：なし
 */
void
set_convmode( DWORD mode )
{
	end_base64( TRUE ) ;
	F_convmode = mode ;
}
