#define	STRICT
#include	<windows.h>
#include	<tchar.h>
#include	"kctrl.h"

extern	DWORD	FontZenVW ;
extern	DWORD	FontHanVW ;
extern	DWORD	FontVH ;
extern	DWORD	TabWidth ;

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

#define		FontHanW		(0xFF & FontX2HanBuf0[0])
#define		FontHanH		(0xFF & FontX2HanBuf0[1])
#define		FontHanSize		(0xFF & FontX2HanBuf0[2])
#define		FontHanName		(&FontX2HanBuf0[8])
#define		FontX2HanBuf	(&FontX2HanBuf0[16])
#define		FontX2TableNum	(0xFF & FontX2Table0[0])
#define		FontX2Table		(&FontX2Table0[2])
#define		FontZenW		(0xFF & FontX2ZenBuf0[0])
#define		FontZenH		(0xFF & FontX2ZenBuf0[1])
#define		FontZenSize		(0xFF & FontX2ZenBuf0[2])
#define		FontZenName		(&FontX2ZenBuf0[8])
#define		FontX2ZenBuf	(&FontX2ZenBuf0[16])

/* フォントバッファ */
BYTE	*FontX2HanBuf0 = 0 ;
WORD	*FontX2Table0 = 0 ;
BYTE	*FontX2ZenBuf0 = 0 ;
LONG	dwScreenWidth = 480 ;	/* 画面の幅 */

static	BOOL	drawText_BmpBuffer( LPCTSTR str, int len, LPDWORD pw, LPDWORD ph ) ;
static	BOOL	DrawBmpBuffer( HDC hDC, DWORD x, DWORD y, DWORD w, DWORD h ) ;
static	BOOL	DrawBmpBuffer2( HDC hDC, DWORD x, DWORD y, DWORD w, DWORD h ) ;
static	void	InvertBmpBuffer( void ) ;

HINSTANCE	hFontDll = 0 ;

BOOL
InitLocal( void )
{
	DWORD	tmp ;
	HRSRC	hRes ;

	/* フォントDLLを読み込む */
	hFontDll = LoadLibrary( TEXT("KCTRLFNT") ) ;
	if ( !hFontDll ) {
		return FALSE ;
	}

	/* 半角フォント */
	hRes = FindResource( hFontDll, TEXT("#1"), TEXT("FONTDATA") ) ;
	if ( hRes == NULL ) {
		return FALSE ;
	}
	FontX2HanBuf0 = (BYTE*) LoadResource( hFontDll, hRes ) ;
	if ( !FontX2HanBuf0 ) {
		return FALSE ;
	}
	FontX2HanBuf0 += 8 ;
	/* 全角フォントテーブル */
	hRes = FindResource( hFontDll, TEXT("#2"), TEXT("FONTDATA") ) ;
	if ( hRes == NULL ) {
		return FALSE ;
	}
	FontX2Table0 = (WORD*) LoadResource( hFontDll, hRes ) ;
	if ( !FontX2Table0 ) {
		return FALSE ;
	}
	FontX2Table0 += 4 ;
	/* 全角フォント */
	hRes = FindResource( hFontDll, TEXT("#3"), TEXT("FONTDATA") ) ;
	if ( hRes == NULL ) {
		return FALSE ;
	}
	FontX2ZenBuf0 = (BYTE*) LoadResource( hFontDll, hRes ) ;
	if ( !FontX2ZenBuf0 ) {
		return FALSE ;
	}
	FontX2ZenBuf0 += 8 ;

	/* フォントが組み込まれているかチェックする */
	if ( !FontZenW || !FontHanW || !FontZenH || !FontHanH ) {
		SetLastError( ERROR_FONT ) ;
		return FALSE ;
	}

	/* 全角／半角フォント幅の比が２：１になるように調整する */
	FontZenVW = FontZenW ;
	FontHanVW = FontHanW ;
	tmp = FontHanVW * 2 ;
	if ( tmp > FontZenVW ) {
		FontZenVW = tmp ;
	} else if ( tmp < FontZenVW ) {
		FontHanVW = FontZenVW / 2 ;
	}
	/* 全角／半角フォント高さが同じになるように調整する */
	FontVH = FontZenH > FontHanH ? FontZenH : FontHanH ;
	/* 画面の幅を得る */
	dwScreenWidth = GetSystemMetrics( SM_CXSCREEN ) ;
	SetLastError( 0 ) ;
	return TRUE ;
}

void
ReleaseLocal( void )
{
	if ( hFontDll ) {
		FreeLibrary( hFontDll ) ;
	}
}

void
KDrawText( HDC hDC, LPCTSTR str, int len, LPRECT pos, UINT format )
{
	DWORD	w, h, w1, h1 ;

#if 0
	HANDLE	hFile ;
	TCHAR	buf[ 30 ] ;
	DWORD	nWrite ;
	hFile = CreateFile( TEXT("\\KCTRL.OUT"), GENERIC_WRITE, 0, NULL,
						OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0 ) ;
	SetFilePointer( hFile, 0, NULL, FILE_END ) ;
	wsprintf( buf, TEXT("%d,%d,%d,%d\r\n"),
			  pos->left, pos->top, pos->right, pos->bottom ) ;
	WriteFile( hFile, buf, (_tcslen( buf ) + 1) * sizeof (TCHAR), &nWrite, 0 ) ;
	CloseHandle( hFile ) ;
#endif
	if ( pos->top > pos->bottom && pos->right == dwScreenWidth && pos->bottom == (LONG) (FontVH + 2) ) {
		/* MyMail対応 */
		if ( drawText_BmpBuffer( str, len, &w, &h ) ) {
			DrawBmpBuffer( hDC, pos->left, pos->top, w, h ) ;
		}
	} else {
		RECT	rect ;

		if ( drawText_BmpBuffer( str, len, &w, &h ) ) {
			w1 = pos->right - pos->left + 1 ;
			w = w1 < w ? w1 : w ;
			h1 = pos->bottom - pos->top + 1 ;
			h = h1 < h ? h1 : h ;
			DrawBmpBuffer( hDC, pos->left, pos->top, w, h ) ;
			if ( w1 > w ) {
				rect.left = pos->left + w, rect.top = pos->top ;
				rect.right = pos->right, rect.bottom = pos->bottom ;
				FillRect( hDC, &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
			}
			if ( h1 > h ) {
				rect.left = pos->left, rect.top = pos->top + h ;
				rect.right = pos->right, rect.bottom = rect.top + h1 - h ;
				FillRect( hDC, &rect, (HBRUSH) GetStockObject( WHITE_BRUSH ) ) ;
			}
		}
	}
}

void
KDrawTextW( HDC hDC, LPCTSTR str, int len, LPRECT pos, UINT format )
{
	DWORD	w, h ;

	if ( drawText_BmpBuffer( str, len, &w, &h ) ) {
		DrawBmpBuffer2( hDC, pos->left, pos->top, w, h ) ;
	}
}

void
KDrawTextI( HDC hDC, LPCTSTR str, int len, LPRECT pos, UINT format )
{
	DWORD	w, h ;

	if ( drawText_BmpBuffer( str, len, &w, &h ) ) {
		InvertBmpBuffer() ;
		DrawBmpBuffer( hDC, pos->left, pos->top, w, h ) ;
	}
}

/*---------------------------------------------------------------------
 * コード変換
 *---------------------------------------------------------------------*/
/* SJIS <-> Unicode変換テーブル */
typedef struct {
	WORD	startCode, endCode ;
} SEGTABLE ;

const	SEGTABLE	UniSegTable[] = {
#include	"uniseg.h"
} ;
#define		Num_UniSegTable		(sizeof (UniSegTable)/sizeof (*UniSegTable))

const	SEGTABLE	SjisSegTable[] = {
#include	"sjisseg.h"
} ;
#define		Num_SjisSegTable	(sizeof (SjisSegTable)/sizeof (*SjisSegTable))

const	WORD		Uni2Sjis[] = {
#include	"uni2sjis.h"
} ;

const	WORD		Sjis2Uni[] = {
#include	"sjis2uni.h"
} ;

/*
 * 　名称：Unicode→SJISコード変換
 * 　略称：unicode2sjis
 * 　概要：Unicode文字列をSJIS文字列に変換する
 * 引き数：src = Unicode文字列
 * 　　　：dst = SJIS文字列を格納する領域へのポインタ
 * 　　　：max = SJIS文字列を格納する領域の最大バイト数
 * 戻り値：DWORD = 変換結果のバイト数(dst=0の時は必要バイト数)\0も含む
 */
DWORD
unicode2sjis( LPCTSTR src, BYTE *dst, DWORD max )
{
	WORD		sjis ;
	DWORD		count ;
	TCHAR		unicode ;

	count = 0 ;
	if ( dst ) {
		if ( max <= 1 ) {
			return count ;
		}
		max -- ;
	}
	while ( unicode = *src++ ) {
		sjis = unicode2sjis_char( unicode ) ;
		if ( sjis == 0xFFFF ) {
			continue ;
		}
		if ( sjis >= 0x0100 ) {
			count ++ ;
			if ( dst ) {
				*dst++ = (BYTE) (sjis / 0x0100) ;
				if ( !(-- max) ) {
					break ;
				}
			}
		}
		count ++ ;
		if ( dst ) {
			*dst++ = (BYTE) (sjis % 0x0100) ;
			if ( !(-- max) ) {
				break ;
			}
		}
	}
	count ++ ;
	if ( dst ) {
		*dst = 0 ;
	}
	return count ;
}

/*
 * 　名称：SJIS→Unicodeコード変換
 * 　略称：sjis2unicode
 * 　概要：SJIS文字列をUnicode文字列に変換する
 * 引き数：src = SJIS文字列
 * 　　　：dst = Unicode文字列を格納する領域へのポインタ
 * 　　　：max = Unicode文字列を格納する領域の最大バイト数
 * 戻り値：DWORD = 変換結果のバイト数(dst=0の時は必要バイト数)\0も含む
 */
DWORD
sjis2unicode( const BYTE *src, LPTSTR dst, DWORD max )
{
	BYTE		c1 ;
	WORD		sjis ;
	DWORD		count ;

	count = 0 ;
	if ( dst ) {
		max /= sizeof (TCHAR) ;
		if ( max <= 1 ) {
			return count * sizeof (TCHAR) ;
		}
		max -- ;
	}
	while ( ( sjis = (WORD) *src++ ) != 0 ) {
		if ( iskanji( sjis ) ) {
			c1 = *src++ ;
			if ( !c1 ) {
				break ;
			}
			sjis <<= 8 ;
			sjis |= (WORD) c1 ;
		}
		count ++ ;
		if ( dst ) {
			*dst++ = sjis2unicode_char( sjis ) ;
			if ( !(-- max) ) {
				break ;
			}
		}
	}
	count ++ ;
	if ( dst ) {
		*dst = 0 ;
	}
	return count * sizeof (TCHAR) ;
}

static	WORD
find_code( const SEGTABLE *seg, int count, const WORD *codes, WORD code )
{
	int			total ;

	if ( code < 0x20 ) {
		return code ;
	}
	total = 0 ;
	do {
		WORD	startCode = seg->startCode, endCode = seg->endCode ;

		if ( code <= endCode ) {
			if ( code < startCode ) {
				break ;
			}
			return codes[ total + code - startCode ] ;
		}
		total += endCode - startCode + 1 ;
		seg ++ ;
	} while ( -- count ) ;
	return 0xFFFF ;
}

/*
 * 　名称：シフトＪＩＳコード→UNICODE変換
 * 　略称：sjis2unicode_char
 * 　概要：シフトＪＩＳコード文字をUNICODEに変換する
 * 引き数：sjis(WORD) = シフトＪＩＳコード
 * 戻り値：TCHAR = UNICODE
 */
TCHAR
sjis2unicode_char( WORD code )
{
	return (TCHAR) find_code( SjisSegTable, Num_SjisSegTable, Sjis2Uni, code );
}

/*
 * 　名称：UNICODE→シフトＪＩＳコード変換
 * 　略称：unicode2sjis_char
 * 　概要：UNICODE文字をシフトＪＩＳコードに変換する
 * 引き数：unicode(TCHAR) = UNICODE
 * 戻り値：WORD = シフトＪＩＳコード
 */
WORD
unicode2sjis_char( TCHAR code )
{
	return find_code( UniSegTable, Num_UniSegTable, Uni2Sjis, (WORD) code ) ;
}

/*
 * 　名称：半角文字チェック
 * 　略称：is_hankaku
 * 　概要：Unicode文字が半角かどうか調べる
 * 引き数：c = Unicode文字
 * 戻り値：BOOL = TRUE:半角, FALSE:全角
 */
BOOL
is_hankaku( TCHAR c )
{
	return (c >= 0xFF60 && c <= 0xFF9F) || (c < 0x0080) ;
}

/*
 * 　名称：KCTRLロケール取得
 * 　略称：GetKLocale
 * 　概要：KCTRL(US)やKCTRL(JP)などの区別を返す
 * 引き数：なし
 * 戻り値：DWORD = 0:US, 1:JP, ...
 */
DWORD
GetKLocale( void )
{
	return LOCALE_US ;
}

/*---------------------------------------------------------------------
 * フォント関係
 *---------------------------------------------------------------------*/
/*
 * 　名称：フォント数取得
 * 　略称：GetNumFont
 * 　概要：表示に使用できるフォント数を調べる
 * 引き数：なし
 * 戻り値：DWORD = フォント数
 */
DWORD
GetNumFont( void )
{
	return 1 ;
}

/*
 * 　名称：利用フォント設定
 * 　略称：SetCurFont
 * 　概要：表示に使用するフォントを決める
 * 引き数：index(DWORD) = 番号(0..1)
 * 戻り値：なし
 */
void
SetCurFont( DWORD index )
{
}

/*
 * 　名称：現在フォント取得
 * 　略称：GetCurFont
 * 　概要：表示に使用しているフォントを調べる
 * 引き数：なし
 * 戻り値：DWORD = 0..1
 */
DWORD
GetCurFont( void )
{
	return 0 ;
}

#define		BmpBuffer_W		(MAX_WIDTH+32)
#define		BmpBuffer_H		MAX_FONTH
#define		BmpBuffer_BW	(BmpBuffer_W / 8)
BYTE	BmpBuffer[ BmpBuffer_BW * BmpBuffer_H ] ;

const BYTE ConvTable[] = {
	0,128,64,192,32,160,96,224,16,144,80,208,48,176,112,240,
	8,136,72,200,40,168,104,232,24,152,88,216,56,184,120,248,
	4,132,68,196,36,164,100,228,20,148,84,212,52,180,116,244,
	12,140,76,204,44,172,108,236,28,156,92,220,60,188,124,252,
	2,130,66,194,34,162,98,226,18,146,82,210,50,178,114,242,
	10,138,74,202,42,170,106,234,26,154,90,218,58,186,122,250,
	6,134,70,198,38,166,102,230,22,150,86,214,54,182,118,246,
	14,142,78,206,46,174,110,238,30,158,94,222,62,190,126,254,
	1,129,65,193,33,161,97,225,17,145,81,209,49,177,113,241,
	9,137,73,201,41,169,105,233,25,153,89,217,57,185,121,249,
	5,133,69,197,37,165,101,229,21,149,85,213,53,181,117,245,
	13,141,77,205,45,173,109,237,29,157,93,221,61,189,125,253,
	3,131,67,195,35,163,99,227,19,147,83,211,51,179,115,243,
	11,139,75,203,43,171,107,235,27,155,91,219,59,187,123,251,
	7,135,71,199,39,167,103,231,23,151,87,215,55,183,119,247,
	15,143,79,207,47,175,111,239,31,159,95,223,63,191,127,255,
} ;

static	void
BitBlt_BmpBuffer( DWORD x, DWORD y, DWORD w, DWORD h, const BYTE *srcPtr )
{
	DWORD	j ;
	DWORD	bx = x / 8 ;
	DWORD	bw = (w + 7) / 8 ;
	DWORD	dx = x - bx * 8 ;
	LPBYTE	dstPtr = &BmpBuffer[ bx ], p1 ;
	const BYTE	*p2 ;
	union {
		DWORD	d ;
		BYTE	b[4] ;
	} dt ;
	static	DWORD	mask[] = { 0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF } ;

	do {
		/* ソース領域からビットマップデータを１ライン分得る */
#if 1
		dt.d = 0 ;
		p1 = dt.b ;
		p2 = srcPtr ;
		j = bw - 1 ;
		do {
			*p1++ = *p2++ ;
		} while ( j -- ) ;
#else
		dt = (DWORD) (0xFF & srcPtr[0]) ;
		for ( j = 1 ; j < bw ; j ++ ) {
			dt |= ((DWORD) (0xFF & srcPtr[j])) << (j * 8) ;
		}
#endif
		srcPtr += bw ;
		/* ビット位置が０でなければシフトする */
		if ( dx ) {
			dt.d <<= dx ;
		}
		dt.d = ~dt.d ;
/* MSB ... LSB  MSB ... LSB */
/* Low-Byte     High-Byte   */
		/* BMPBuffer に１ライン分描く */
#if 1
		p1 = dt.b ;
		j = bw ;
		do {
			*dstPtr++ &= ConvTable[ *p1++ ] ;
		} while ( j -- ) ;
#else
		*dstPtr++ &= ConvTable[ 0xFF & dt.d ] ;		dt.d >>= 8 ;
		for ( j = 0 ; j < bw ; j ++ ) {
			*dstPtr++ &= ConvTable[ 0xFF & dt.d ] ;	dt.d >>= 8 ;
		}
#endif
		dstPtr += BmpBuffer_BW - bw - 1 ;
	} while ( -- h ) ;
}

/* ------------------------------------------------------------------------
 * FontX2関連ルーチン
 * ------------------------------------------------------------------------ */
static	BOOL
find_zen_table( WORD sjis, LPDWORD offset )
{
	const WORD	*ptr, *from, *to, *ofs ;
	WORD		num = FontX2TableNum ;

	if ( !num ) {
		return FALSE ;
	}
	ptr = FontX2Table ;		/* フォント領域へのポインタ */
	from = ptr ++ ;			/* 開始コードへのポインタ */
	to = ptr ++ ;			/* 終了コードへのポインタ */
	ofs = ptr ++ ;			/* オフセット(文字単位)へのポインタ */
	do {
		if ( *from <= sjis && sjis <= *to ) {
			*offset = (DWORD) FontZenSize * (DWORD) (*ofs + sjis - *from) ;
		    return TRUE ;
		}
		from += 3, to += 3, ofs += 3 ;
	} while ( -- num ) ;
    return FALSE ;
}

static	void
drawFontX2Zen_BmpBuffer( DWORD x, WORD sjis )
{
	DWORD	ofs ;

	if ( !find_zen_table( sjis, &ofs ) ) {
		return ;
	}
	BitBlt_BmpBuffer( x, 0, FontZenW, FontZenH, &FontX2ZenBuf[ ofs ] ) ;
}

static	void
drawFontX2Han_BmpBuffer( DWORD x, BYTE c )
{
	DWORD	idx = (DWORD) c * FontHanH ;
	BitBlt_BmpBuffer( x, 0, FontHanW, FontHanH, &FontX2HanBuf[ idx ] ) ;
}

static	BOOL
drawText_BmpBuffer( LPCTSTR str, int len, LPDWORD pw, LPDWORD ph )
{
	DWORD	x = 0 ;
	WORD	sjis ;
	DWORD	FontTabW = FontHanVW * TabWidth ;		/* タブの表示幅 */

	if ( len == -1 ) {
		len = _tcslen( str ) ;
	}
	if ( !len ) {
		return FALSE ;
	}
	memset( BmpBuffer, 255, sizeof BmpBuffer ) ;
	do {
		sjis = unicode2sjis_char( *str ++ ) ;
		if ( sjis == 0xFFFF ) {
		} else if ( sjis & 0xFF00 ) {
			if ( sjis != 0x8140 ) {
				drawFontX2Zen_BmpBuffer( x, sjis ) ;
			}
			x += FontZenVW ;
		} else if ( sjis == '\t' ) {
			x += FontTabW - (x % FontTabW) ;
		} else if ( sjis == ' ' ) {
			x += FontHanVW ;
		} else {
			/* 半角フォントがある場合 */
			drawFontX2Han_BmpBuffer( x, (BYTE) sjis ) ;
			x += FontHanVW ;
		}
		if ( x >= MAX_WIDTH ) {
			break ;
		}
	} while ( -- len ) ;
	*pw = x ;
	*ph = FontVH ;
	return TRUE ;
}

static	BOOL
DrawBmpBuffer( HDC hDC, DWORD x, DWORD y, DWORD w, DWORD h )
{
	HDC			hdcBits ;
	HBITMAP		hBmp, oldBmp ;

	hBmp = CreateBitmap( BmpBuffer_W, BmpBuffer_H, 1, 1, BmpBuffer ) ;
	if ( hBmp == NULL ) {
		return FALSE ;
	}
	hdcBits = CreateCompatibleDC( hDC ) ;
	oldBmp = SelectObject( hdcBits, hBmp ) ;
	BitBlt( hDC, x, y, w, h, hdcBits, 0, 0, SRCCOPY ) ;
	SelectObject( hdcBits, oldBmp ) ;
	DeleteObject( hBmp ) ;
	DeleteDC( hdcBits ) ;
	return TRUE ;
}

static	BOOL
DrawBmpBuffer2( HDC hDC, DWORD x, DWORD y, DWORD w, DWORD h )
{
	HDC			hdcBits ;
	HBITMAP		hBmp, oldBmp ;

	hBmp = CreateBitmap( BmpBuffer_W, BmpBuffer_H, 1, 1, BmpBuffer ) ;
	if ( hBmp == NULL ) {
		return FALSE ;
	}
	hdcBits = CreateCompatibleDC( hDC ) ;
	oldBmp = SelectObject( hdcBits, hBmp ) ;
	StretchBlt( hDC, x, y, w * 2, h, hdcBits, 0, 0, w, h, SRCCOPY ) ;
	SelectObject( hdcBits, oldBmp ) ;
	DeleteObject( hBmp ) ;
	DeleteDC( hdcBits ) ;
	return TRUE ;
}

static	void
InvertBmpBuffer( void )
{
	LPDWORD	ptr = (LPDWORD) BmpBuffer ;
	DWORD	size = sizeof BmpBuffer / sizeof (DWORD) ;

	do {
		*ptr = ~*ptr ;
		ptr ++ ;
	} while ( -- size ) ;
}
