#ifndef GAMEZ_GOS_PORT
#include "pch.hpp"
#endif

//
//===========================================================================//
// File:	 RenderIME.cpp													 //
// Project:  GameOS					                                         //
// Contents: Render and manipulate the IME									 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 12/12/99  JTR    Original code: Kazuyuki S. Adapted to GameOS.			 //
// 04/14/00  JTR    Major Modifications.									 //
// 04/18/00  KS     Corrections and Cleanup.							     //
// 05/08/00  KS     Switched to LeadByteTable							     //
// 05/09/00  KS     Corrections and Cleanup.							     //
// 06/16/00 05:22 by yutaka.
//---------------------------------------------------------------------------//
// Copyright (C) 2000 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
//

#ifndef GAMEZ_GOS_PORT

#include "LocalizationManager.hpp"
#include <mbstring.h>
#include <mbctype.h>


extern DWORD gLanguageDLL;
//
// From WinProc.cpp
//
extern float FontSize;
extern HGOSFONT3D FontHandle;
extern DWORD FontColor;

#else


#include "GameOS.HPP"
#include <windows.h>
#include <mbstring.h>
#include <mbctype.h>
#include <math.h>
#include <zConfig.h>
#include "locale.h"

#include "GameZ_GOS.hpp"

void InsertGOSKey(DWORD vkey, DWORD scancode);
DWORD gLanguageDLL = 0;
#define GOS_RENDERIME_BADATTRIBUTES     26
extern float FontSize;
extern HGOSFONT3D FontHandle;
extern DWORD FontColor;
BYTE LeadByteTable[256];
char gApplicationLocale[MAX_PATH];

inline long float2long( float fval )
{
	fval += 3 << 22;
	return ((*(long*)&fval)&0x007fffff) - 0x00400000;
}

#endif
//
// IME Variables
//
#define MAX_CANDLIST 9

#ifndef GAMEZ_GOS_PORT
#define INTERNAL_DEPENDENCY			// Comment this out if your not using the
									// GameOS library.
#endif

#ifndef INTERNAL_DEPENDENCY
struct gos_CaretInfo
{
	DWORD		fontX;
	DWORD		fontY;
	float		fontSize;
	HGOSFONT3D	fontHandle;
	DWORD		fontColor;
	bool		fontBold;
	bool		fontItalic;
	bool		fontProportional;
	RECT		margins ;
} ;
#endif

struct _gSkinIME
{
	DWORD         baseColor;
	DWORD         borderColor;
	DWORD		  symbolColor;
	unsigned char symbolHeight;
	unsigned char symbolTranslucence;
	unsigned char symbolPlacement; 
};

_gSkinIME gSkinIME;

// ...from Win98DDK version of IMM.H
typedef struct tagINPUTCONTEXT {                        
    HWND                hWnd;                           
    BOOL                fOpen;                          
    POINT               ptStatusWndPos;                 
    POINT               ptSoftKbdPos;                   
    DWORD               fdwConversion;                  
    DWORD               fdwSentence;                    
    union   {                                           
        LOGFONTA        A;                              
        LOGFONTW        W;                              
    } lfFont;                                           
    COMPOSITIONFORM     cfCompForm;                     
    CANDIDATEFORM       cfCandForm[4];                  
    HIMCC               hCompStr;                       
    HIMCC               hCandInfo;                      
    HIMCC               hGuideLine;                     
    HIMCC               hPrivate;                       
    DWORD               dwNumMsgBuf;                    
    HIMCC               hMsgBuf;                        
    DWORD               fdwInit;                        
    DWORD               dwReserve[3];                   
} INPUTCONTEXT, *PINPUTCONTEXT, NEAR *NPINPUTCONTEXT, FAR *LPINPUTCONTEXT;  

extern HWND hWindow;
static HIMC g_himcOrg;
static bool g_bImeEnabled = false;
static TCHAR g_szCompositionString[256];
static BYTE  g_szCompAttrString[256];
static DWORD g_IMECursorBytes = 0;
static DWORD g_IMECursorChars = 0;
static TCHAR g_szCandidate[MAX_CANDLIST][256];
static DWORD g_dwSelection, g_dwCount;
#ifndef GAMEZ_GOS_PORT
static DWORD g_bDisableImeCompletely = false;
#else
DWORD g_bDisableImeCompletely = false;
bool g_bCompStrOn = false;
static bool g_bIgnoreKey = false;
#endif
static g_bInsertMode = true;

// Global Variables
gos_CaretInfo g_CaretInfo;
HFONT g_hIMEFont = NULL;
bool g_bComposingIME = false;
DWORD swirl = 0;
double lastSwirl;
unsigned char g_SymbolIME[3];
extern LCID gLangID;
const char* c_szCandListFormat = "%d %s";

extern float FontMinX, FontMaxX;
extern float FontMinY, FontMaxY;
extern bool FontBold;
extern bool FontItalic;
extern bool FontProportional;

extern HIMC (WINAPI* _ImmGetContext)(HWND hW);
extern BOOL (WINAPI* _ImmReleaseContext)( HWND hW, HIMC hImc );
extern HIMC (WINAPI* _ImmAssociateContext)( HWND hW, HIMC himc );
extern LONG (WINAPI* _ImmGetCompositionString)( HIMC himc, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen );
extern BOOL (WINAPI* _ImmSetCompositionString)( HIMC hIMC, DWORD dwIndex, LPVOID lpBuf, DWORD dwBufLen, LPVOID lpRead, DWORD dwReadLen );
extern BOOL (WINAPI* _ImmGetOpenStatus)( HIMC himc );
extern BOOL (WINAPI* _ImmSetOpenStatus)( HIMC himc, BOOL flag );
extern BOOL (WINAPI* _ImmGetConversionStatus)( HIMC himc, LPDWORD lpfdwConversion, LPDWORD lpfdwSentence );
extern DWORD (WINAPI* _ImmGetCandidateList)( HIMC himc, DWORD deIndex, LPCANDIDATELIST lpCandList, DWORD dwBufLen );
extern LPINPUTCONTEXT (WINAPI* _ImmLockIMC)(HIMC hIMC);
extern BOOL (WINAPI* _ImmUnlockIMC)(HIMC hIMC);
extern LPVOID (WINAPI* _ImmLockIMCC)(HIMCC hIMCC);
extern BOOL (WINAPI* _ImmUnlockIMCC)(HIMCC hIMCC);
extern HWND (WINAPI* _ImmGetDefaultIMEWnd)(HWND hW);
extern HWND (WINAPI* _ImmGetIMEFileName)(HKL hKL, LPTSTR lpszFileName, UINT uBufLen);
extern BOOL (WINAPI* _ImmNotifyIME)(HIMC, DWORD dwAction, DWORD dwIndex, DWORD dwValue);

#ifdef INTERNAL_DEPENDENCY
extern void GDIGetChrSize( DWORD * w, DWORD * h, int index);
extern void GDICreateFont();
extern HDC gdiDC;
extern BYTE LeadByteTable[256];
#endif
void ShowIMEActive(bool flag);

DWORD GetIMEInfo();
void IMEDrawRect( int x1, int y1, int x2, int y2, DWORD Color );
void GetReadingString(HWND hWnd);
DWORD isValidTaiwanIME();

// returns number of characters from number of bytes
static int mbGetCharCount(LPCSTR pszString, int iBytes)
{
	int iCount = 0;
	for (int i = 0; pszString[i] && i < iBytes; i++) {
		iCount++;
#ifdef INTERNAL_DEPENDENCY
		if (LeadByteTable[(unsigned char)pszString[i]])
			i++;
#else
		if (isleadbyte((unsigned char)pszString[i]))
			i++;
#endif
	}
	if (i != iBytes)
		iCount = -iCount;	// indicate error - iBytes specifies wrong boundary (i.e. the last byte is leadbyte)
	return iCount;
}

// move caret to the end of composition string
static void IMEMoveCaretToTheEnd(HWND hwnd)
{
	int mblen = _mbstrlen(g_szCompositionString);
	for (int i = g_IMECursorChars; i < mblen ; i++)
#ifndef GAMEZ_GOS_PORT
	{
		SendMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 0);
	}
	SendMessage(hwnd, WM_KEYUP, VK_RIGHT, 0);
#else
	{
		InsertGOSKey(0, VK_RIGHT);
	}
#endif
}

// send key messages to erase composition string.
static void IMECancelCompString(HWND hwnd, bool bUseBackSpace = true, int iNewStrLen = 0)
{
	int mblen = _mbstrlen(g_szCompositionString);
	int i;
	IMEMoveCaretToTheEnd(hwnd);

	if (bUseBackSpace || g_bInsertMode)
		iNewStrLen = 0;

	// The app is in overwriting mode, and it's possible new composition string is sent to 
	// the app after this function call.
	// If new comp string is longer than current one, delete previous comp string 
	// till it's same long as the new one, then move caret to the beginning of comp string.
	// New comp string will overwrite old one.
	if (iNewStrLen < mblen)
	{
		for (i = 0; i < mblen - iNewStrLen; i++)
#ifndef GAMEZ_GOS_PORT
		{
			SendMessage(hwnd, WM_KEYDOWN, VK_BACK, 0);
			SendMessage(hwnd, WM_CHAR, 8, 0);	//Backspace character
		}
		SendMessage(hwnd, WM_KEYUP, VK_BACK, 0);
#else
		{
			InsertGOSKey(8, VK_BACK);
		}
#endif
	}
	else
		iNewStrLen = mblen;

	for (i = 0; i < iNewStrLen; i++)
#ifndef GAMEZ_GOS_PORT
	{
		SendMessage(hwnd, WM_KEYDOWN, VK_LEFT, 0);
	}
	SendMessage(hwnd, WM_KEYUP, VK_LEFT, 0);
#else
	{
		InsertGOSKey(0, VK_LEFT);
	}
#endif
}

// initialize composition string data.
static void IMEInitCompStringData(void)
{
	g_IMECursorBytes = 0;
	g_IMECursorChars = 0;
	memset(&g_szCompositionString, 0, sizeof(g_szCompositionString));
	memset(&g_szCompAttrString, 0, sizeof(g_szCompAttrString));
}

void RenderIME()
{
	if (g_bDisableImeCompletely || !g_CaretInfo.fontHandle)
		return;

	if (g_bImeEnabled)
	{
		ShowIMEActive(g_bComposingIME);
	}

	static int bCand_Index = -1;
	static int bCand_Line = -1;

	if (bCand_Index == -1) {
		DWORD dwSize;
		bCand_Index = 1;
		bCand_Line = 1;
#ifndef GAMEZ_GOS_PORT
		dwSize = sizeof(int);
		gos_LoadDataFromRegistry("Cand_Index", &bCand_Index, &dwSize);
		dwSize = sizeof(int);
		gos_LoadDataFromRegistry("Cand_Line", &bCand_Line, &dwSize);
#else
	zCfgItem* cfgData;
	
	if (cfgData = zcfg_GetItem("Cand_Index"))
		bCand_Index = cfgData->value.ival;
	if (cfgData = zcfg_GetItem("bCand_Line"))
		bCand_Index = cfgData->value.ival;
#endif
	}


	int POS_X = g_CaretInfo.fontX;
	int POS_Y = g_CaretInfo.fontY;
	int left=0;
	int top=0;
	int right=0;
	int bottom=0;	

	gos_TextSetAttributes(g_CaretInfo.fontHandle, g_CaretInfo.fontColor, -g_CaretInfo.fontSize/1.5f,
		false, g_CaretInfo.fontProportional, g_CaretInfo.fontBold, g_CaretInfo.fontItalic);	// hardcoded proportional, non-bold, non-italic

	// don't corrupt current renderstate settings with 
	// out own!
	gos_PushRenderStates();

	DWORD tx, ty;
	SIZE largest = {0,0};

#ifdef INTERNAL_DEPENDENCY
	//
	// replace font settings with caret settings
	//
	gdiDC = CreateCompatibleDC(NULL);
	GDICreateFont();
#endif

	int len = strlen(g_szCompositionString);

	DWORD cType = 0;
	DWORD candX = 0;

	DWORD w,h;
	char temp[512];
	temp[0] = 0;
	strncpy( temp, g_szCompositionString, g_IMECursorBytes );
	temp[g_IMECursorBytes] = 0;

	gos_TextSetRegion(0,0, 10000, 10000);
	gos_TextStringLength(&w, &h, temp);

	POS_X -= w;
	DWORD bgX = POS_X;	
	//
	// Draw the background colors for IME text nuggets
	//
	bool stop = false;
    for (int i = 0; i < len ; i += cType)
	{
		DWORD bgColor = 0x00000000;
		DWORD h,w;
#ifdef INTERNAL_DEPENDENCY
		cType = 1 + LeadByteTable[(unsigned char)g_szCompositionString[i]];
		if (cType == 1)
			GDIGetChrSize(&w, &h, g_szCompositionString[i]);
		else
			GDIGetChrSize(&w, &h, ((((unsigned char *)(g_szCompositionString+i))[0])<<8) + (((unsigned char *)(g_szCompositionString+i))[1]));
#else
		cType = 1 + ((isleadbyte(g_szCompositionString[i])) ? 1 : 0);
		char szChar[3];
		szChar[0] = g_szCompositionString[i];
		szChar[1] = szChar[2] = 0;

		if (cType == 2 && g_szCompositionString[i+1])	// in case we have 0 in trailbyte, we don't count it.
			szChar[1] = g_szCompositionString[i+1];
		gos_TextStringLength(&w, &h, szChar);
#endif

		switch(g_szCompAttrString[i])
		{
		case ATTR_INPUT:
			bgColor = 0x7fffff00;
			if (!stop) 
				candX += w;
			break;
		case ATTR_TARGET_CONVERTED:
			bgColor = 0x7f0000ff;	
			stop = true;
			break;						  
		case ATTR_CONVERTED:
			bgColor = 0x7f00ff00;
			if (!stop) 
				candX += w;
			break;
		case ATTR_TARGET_NOTCONVERTED:
			//
			// This is the one the user is working with currently
			//
			bgColor = 0x7fff0000;
			if (!stop) 
				candX += w;
			break;
		case ATTR_INPUT_ERROR:
			bgColor = 0x7fff0000;
			if (!stop) 
				candX += w;
			break;
		default:
			STOP(( gos_GetResourceString(gLanguageDLL, GOS_RENDERIME_BADATTRIBUTES) ));
			break;
		}

		{
			if (bgX >= g_CaretInfo.margins.left && bgX <= g_CaretInfo.margins.right)
				IMEDrawRect(bgX, g_CaretInfo.margins.top+1, bgX+w, g_CaretInfo.margins.bottom, bgColor);
		}
		bgX += w;
	}

#ifdef INTERNAL_DEPENDENCY
	GDIGetChrSize(&w, &h, ' ');
#else
	gos_TextStringLength(&w, &h, " ");
#endif
	DWORD slotsUsed = g_dwCount;
	if (slotsUsed > MAX_CANDLIST)
		slotsUsed = MAX_CANDLIST;
	if ( POS_Y +(slotsUsed * h) > Environment.screenHeight)
		POS_Y -= (h + (slotsUsed * h));

	if (g_dwCount && g_szCandidate[0][0] != 0)
	{
		largest.cx = largest.cy = 0;
        for (int i = 0; g_szCandidate[i][0] && i < MAX_CANDLIST; i++)
		{
			if (bCand_Index) {
				char szTemp[256];
				sprintf(szTemp, c_szCandListFormat, i+1, g_szCandidate[i]);
				gos_TextStringLength(&tx, &ty, szTemp);
			}
			else
	            gos_TextStringLength(&tx, &ty, g_szCandidate[i]);
			if ((signed)tx > largest.cx)
				largest.cx = tx;
			if ((signed)ty > largest.cy)
				largest.cy = ty;
		}

        //set candidate position
	    if (largest.cx)
		{
			int saveRight;

		    left = POS_X + candX;
			top = POS_Y+2;
		    saveRight = right = POS_X  + candX + largest.cx;
			bottom = POS_Y + 4 +  (((g_dwCount > MAX_CANDLIST) ? MAX_CANDLIST : g_dwCount) * largest.cy);

			IMEDrawRect(left-1,top-1, right+3, bottom+1, gSkinIME.borderColor);

			// assumption : all digits have same width
			if (bCand_Index && bCand_Line) {
				static DWORD uDigitWidth = 0, uSpaceWidth = 0, uDummy;
#ifdef INTERNAL_DEPENDENCY
				GDIGetChrSize(&uDigitWidth, &uDummy, '0');
				GDIGetChrSize(&uSpaceWidth, &uDummy, ' ');
#else
				gos_TextStringLength(&uDigitWidth, &uDummy, "0");
				gos_TextStringLength(&uSpaceWidth, &uDummy, " ");
#endif
				right = left + uDigitWidth + uSpaceWidth - 1;
//				right = left + uDigitWidth - 1;
				IMEDrawRect(left, top, right, bottom, gSkinIME.baseColor);

				left = right + 1;
				right = saveRight;
				IMEDrawRect(left, top, right+2, bottom, gSkinIME.baseColor);
			}
			else
				IMEDrawRect(left,top, right+2, bottom, gSkinIME.baseColor);
		}

        //display candidate
        for (i = 0; i < MAX_CANDLIST && i < g_dwCount; i++)
        {
			if (g_dwSelection == (DWORD)i)
			{
#ifndef GAMEZ_GOS_PORT
				FontColor = 0xff000000 + (~((0x00ffffff) & g_CaretInfo.fontColor));
#else
				gos_TextSetAttributes(g_CaretInfo.fontHandle, 0xff000000 + (~((0x00ffffff) & g_CaretInfo.fontColor)),
					-g_CaretInfo.fontSize/1.5f, false, g_CaretInfo.fontProportional, g_CaretInfo.fontBold, g_CaretInfo.fontItalic);
#endif
				IMEDrawRect(POS_X + candX, 2+POS_Y+ i * largest.cy, POS_X+candX+largest.cx+3, 2+POS_Y+(i+1)*largest.cy, gSkinIME.borderColor);
			}
            else
#ifndef GAMEZ_GOS_PORT
				FontColor = g_CaretInfo.fontColor;
#else
				gos_TextSetAttributes(g_CaretInfo.fontHandle, g_CaretInfo.fontColor, -g_CaretInfo.fontSize/1.5f,
					false, g_CaretInfo.fontProportional, g_CaretInfo.fontBold, g_CaretInfo.fontItalic);	// hardcoded proportional, non-bold, non-italic
#endif
            if (g_szCandidate[i][0] != 0)
			{
				gos_TextSetPosition(POS_X + 2 + candX, 2 + POS_Y + i * largest.cy);
				if (bCand_Index) {
					char szTemp[256];
					sprintf(szTemp, c_szCandListFormat, i+1, g_szCandidate[i]);
					gos_TextDraw(szTemp);
				}
				else
					gos_TextDraw(g_szCandidate[i]);
			}
        }
    }
#ifdef INTERNAL_DEPENDENCY
	DeleteDC( gdiDC );
	gdiDC = NULL;
	if (Environment.DirtyRectangle)
	{
		gosDirtyRectangeRestoreArea( g_CaretInfo.margins.left, g_CaretInfo.margins.top, g_CaretInfo.margins.right, g_CaretInfo.margins.bottom );
		if (largest.cx)
			gosDirtyRectangeRestoreArea( left-1, top-1, right+3, bottom+1 );
	}
#endif
	gos_PopRenderStates();

}

//
// Draw a square region using quads in a given color
//
void IMEDrawRect( int x1, int y1, int x2, int y2, DWORD Color )
{
	gos_SetupViewport( 0,1.0,0,0,0.0,0.0,1.0,1.0 );
	if (x1 < 0) x1 = 0;
	if (x2 < 0) x2 = 0;
	if (y1 < 0) y1 = 0;
	if (y2 < 0) y2 = 0;
	if (x1 > Environment.screenWidth) x1 = Environment.screenWidth;
	if (x2 > Environment.screenWidth) x2 = Environment.screenWidth;
	if (y1 > Environment.screenHeight) y1 = Environment.screenHeight;
	if (y2 > Environment.screenHeight) y2 = Environment.screenHeight;
	if (x1 == x2 || y1 == y2) return;
	
	gos_VERTEX pVertices[4];

	memset( pVertices, 0, sizeof(pVertices) );
	pVertices[0].rhw	= 1.0;
	pVertices[1].rhw	= 1.0;
	pVertices[2].rhw	= 1.0;
	pVertices[3].rhw	= 1.0;

	pVertices[0].x		= (float)x1;
	pVertices[0].y		= (float)y1;
	pVertices[0].argb	= Color;

	pVertices[1].x		= (float)x1;
	pVertices[1].y		= (float)y2;
	pVertices[1].argb	= Color;

	pVertices[2].x		= (float)x2;
	pVertices[2].y		= (float)y2;
	pVertices[2].argb	= Color;

	pVertices[3].x		= (float)x2;
	pVertices[3].y		= (float)y1;
	pVertices[3].argb	= Color;

	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Clipping, 0 );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeFlat );
	gos_SetRenderState( gos_State_MonoEnable, 0 );
	if( (Color&0xff000000)==0xff000000 )
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_OneZero );
	else
		gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );

	gos_DrawQuads( pVertices, 4 );
}

LPARAM ProcessIMEMessages( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam, bool * trapped )
{
	HIMC himc;

	if (g_bDisableImeCompletely) 
	{
		*trapped = false;
		return 0;
	}

	*trapped = true;
	switch( uMsg )
	{
//
//	IME Handling
//
	case WM_INPUTLANGCHANGE:
		if (GetACP() == 950 || GetACP() == 936)
		{
			HKL hkl = GetKeyboardLayout(0);
			g_bComposingIME = (PRIMARYLANGID(LOWORD(hkl)) == LANG_CHINESE);
		}
		*trapped = false;
		break;

    case WM_IME_CONTROL:
		*trapped = false;
        break;

    case WM_IME_SETCONTEXT:
		//
		// We don't want anything to display, so we have to clear this
		//
        lParam = 0;
		*trapped = false;
        break;

    case WM_IME_STARTCOMPOSITION:
#ifdef GAMEZ_GOS_PORT
		g_bCompStrOn = true;
#endif
		IMEInitCompStringData();
		*trapped = false;
		break;

    case WM_IME_COMPOSITIONFULL:
		*trapped = false;
        break;		

    case WM_IME_COMPOSITION:
		{
			LONG lRet;
			char szCompStr[sizeof(g_szCompositionString)/sizeof(g_szCompositionString[0])];

			if (NULL == (himc = _ImmGetContext(hWnd)))
				return 0;

				// ResultStr must be processed before composition string.
		        if ( lParam & GCS_RESULTSTR )
				{
					lRet = _ImmGetCompositionString( himc, GCS_RESULTSTR, szCompStr, 255 );
					szCompStr[lRet] = 0;
					IMECancelCompString(hWindow, false, _mbstrlen(szCompStr));

					strcpy(g_szCompositionString, szCompStr);
	
					for (int i = 0; i < lRet ; i++)
					{
#ifndef GAMEZ_GOS_PORT
						SendMessage(hWindow, WM_CHAR, (unsigned char)g_szCompositionString[i], 0);
#else
						InsertGOSKey((unsigned char)g_szCompositionString[i], 0);
#endif
					}
					IMEInitCompStringData();
				}
		        //
		        // Reads in the composition string.
		        //
		        if ( lParam & GCS_COMPSTR )
		        {
					//////////////////////////////////////////////////////
					// Retrieve the latest user-selected IME candidates
					lRet = _ImmGetCompositionString( himc, GCS_COMPSTR, szCompStr, 255 );
					szCompStr[lRet] = 0;
					//
					// Remove the whole of the string
					//
					IMECancelCompString(hWindow, false, _mbstrlen(szCompStr));

					strcpy(g_szCompositionString, szCompStr);
					lRet = _ImmGetCompositionString( himc, GCS_COMPATTR, g_szCompAttrString, 255 );
					g_szCompAttrString[lRet] = 0;

					if ((GetACP() == 950 && !isValidTaiwanIME()) // for older Taiwan IME
						|| GetACP() == 936)
					{
						int i, chars = strlen(g_szCompositionString) / 2;
						if (chars)
						{
							g_dwCount = 4;
							g_dwSelection = 0;

							for (i = 3; i >= 0; i--)
							{
								if (i > chars - 1)
									g_szCandidate[i][0] = 0;
								else
								{
									g_szCandidate[i][0] = g_szCompositionString[i*2];
									g_szCandidate[i][1] = g_szCompositionString[i*2+1];
									g_szCandidate[i][2] = 0;
								}
							}
							g_szCompositionString[0] = 0;
						}
						else
							g_dwCount = 0;
					}

					// send composition string via WM_CHAR
					int len = strlen(g_szCompositionString);
					for (int i = 0;i < len ; i++) {
#ifndef GAMEZ_GOS_PORT
						SendMessage(hWindow, WM_CHAR, (unsigned char)g_szCompositionString[i], 0);
#else
						InsertGOSKey((unsigned char)g_szCompositionString[i], 0);
#endif
					}

					// move caret to appropreate location
					g_IMECursorBytes = _ImmGetCompositionString(himc, GCS_CURSORPOS, NULL, 0);
					g_IMECursorChars = mbGetCharCount(g_szCompositionString, g_IMECursorBytes);
					len = _mbstrlen(g_szCompositionString + g_IMECursorBytes);
					for (i = 0; i < len; i++)
#ifndef GAMEZ_GOS_PORT
					{
						SendMessage(hWindow, WM_KEYDOWN, VK_LEFT, 0);
					}
					SendMessage(hWindow, WM_KEYUP, VK_LEFT, 0);
#else
					{
						InsertGOSKey(0, VK_LEFT);
					}
#endif
				}
				_ImmReleaseContext(hWnd, himc);
		}
		//
		// DO NOT REMOVE THIS RETURN
		// The GDI disable  available via WM_IME_SETCONTEXT does not work. The only way to 
		// hide the GDI representation is to have this return here.
		return 0;

    case WM_IME_ENDCOMPOSITION:
#ifdef GAMEZ_GOS_PORT
		g_bCompStrOn = false;
		g_bIgnoreKey = true;
#endif
		IMECancelCompString(hWindow);
		IMEInitCompStringData();
		*trapped = false;
        break;

    case WM_IME_NOTIFY:
        switch (wParam)
        {
			case IMN_SETCONVERSIONMODE:
				*trapped = false;
				break;

			case IMN_SETOPENSTATUS:
				{
					if (NULL == (himc = _ImmGetContext(hWnd)))
					{
						g_bComposingIME = false;
						return 0;
					}
					BOOL  fOpen;
					DWORD dwConvMode, dwSentMode;
					fOpen = _ImmGetOpenStatus(himc);
					g_bComposingIME = fOpen ? true : false;
					SPEW(("jreicher", "composing = %d", g_bComposingIME));
					if (fOpen)
					    _ImmGetConversionStatus(himc, &dwConvMode, &dwSentMode);
					else
					    dwConvMode = 0;
					_ImmReleaseContext(hWnd, himc);
#ifdef GAMEZ_GOS_PORT
					g_bIgnoreKey = true;
#endif
				}
					*trapped = false;
			    break;
			
			case IMN_OPENCANDIDATE:
			case IMN_CHANGECANDIDATE:
				{
					if (NULL == (himc = _ImmGetContext(hWnd)))
						return 0;
					
					LPCANDIDATELIST lpCandList;
					DWORD dwIndex, dwBufLen;
				    DWORD temp;
				    LPTSTR lpStr;

					dwIndex = 0;
					dwBufLen = _ImmGetCandidateList( himc, dwIndex, NULL, 0 );
					if ( dwBufLen )
					{
					    lpCandList = (LPCANDIDATELIST)LocalAlloc(LPTR, dwBufLen);
					    _ImmGetCandidateList( himc, dwIndex, lpCandList, dwBufLen );
					    g_dwSelection = lpCandList->dwSelection;
					    g_dwCount = lpCandList->dwCount;

						int what_page = g_dwSelection / lpCandList->dwPageSize;
						g_dwSelection = g_dwSelection - (what_page * lpCandList->dwPageSize);

					    memset(&g_szCandidate, 0, sizeof(g_szCandidate));
					    for (int i = (what_page * lpCandList->dwPageSize), j = 0; i < lpCandList->dwCount && j < MAX_CANDLIST; i++, j++)
					    {
					        temp = (DWORD)lpCandList + lpCandList->dwOffset[i];
					        lpStr = (LPTSTR)temp;
					        _mbscpy(((unsigned char *) g_szCandidate[j]), (const unsigned char *) lpStr);
					    }
					    LocalFree( (HANDLE)lpCandList );
						_ImmReleaseContext(hWnd, himc);
					}
					return 0;
				}
			
			case IMN_CLOSECANDIDATE:
				g_dwCount = 0;
			    memset(&g_szCandidate, 0, sizeof(g_szCandidate));
			    return 0;

			// Jun.16,2000 05:21 by yutaka.
			case IMN_PRIVATE:
				GetReadingString(hWnd);
				*trapped = false;	// This is necessary to on/off Taiwan IME by shift key - 000619 kazuyuks
				break;
		}
		break;

	default:
	{
		*trapped = false;
	}
	}
	return 0;
}

void _stdcall gos_PositionIME(DWORD x, DWORD y, DWORD baseColor, DWORD borderColor, unsigned char symbolTranslucence, unsigned char symbolSize, unsigned char symbolPlacement, DWORD symbolColor)
{
	g_CaretInfo.fontX = x;
	g_CaretInfo.fontY = y;
#ifndef GAMEZ_GOS_PORT
	g_CaretInfo.fontColor = FontColor;
	g_CaretInfo.fontSize = FontSize;
	g_CaretInfo.fontHandle = FontHandle;
	g_CaretInfo.margins.left = float2long(FontMinX);
	g_CaretInfo.margins.right = float2long(FontMaxX);
	g_CaretInfo.margins.top = float2long(FontMinY);
	g_CaretInfo.margins.bottom = float2long(FontMaxY);
	g_CaretInfo.fontBold = FontBold;
	g_CaretInfo.fontItalic = FontItalic;
	g_CaretInfo.fontProportional = FontProportional;

	gSkinIME.baseColor = baseColor;
	gSkinIME.borderColor = borderColor;
	gSkinIME.symbolTranslucence = symbolTranslucence;
	gSkinIME.symbolHeight = symbolSize;
	gSkinIME.symbolPlacement = symbolPlacement;
	gSkinIME.symbolColor = symbolColor;
#else
extern const CZFont* _GetTextFont();
extern const RECT* _GetTextRegion();
extern DWORD _GetTextColor();
	static _FontInfo fi;
	fi.m_ZFont = _GetTextFont();
	g_CaretInfo.fontColor = _GetTextColor();
	g_CaretInfo.fontSize = ((float)fi.m_ZFont->height())*1.5f;
	g_CaretInfo.fontHandle = &fi;
	g_CaretInfo.margins = *_GetTextRegion();
	g_CaretInfo.fontBold = fi.m_ZFont->weight() != FW_NORMAL;
	g_CaretInfo.fontItalic = false;	// BUGBUG: No way to get italic property!
	g_CaretInfo.fontProportional = 0; //
#if 1
	// fix for #3946 - we use hardcoded value to minimize change.
	g_CaretInfo.fontColor = 0xff000000;

	gSkinIME.baseColor = 0xffffffff;
	gSkinIME.borderColor = 0xc00020c0;
	gSkinIME.symbolTranslucence = 0xd0;
	gSkinIME.symbolHeight = 24;
	gSkinIME.symbolPlacement = 0;
	gSkinIME.symbolColor = 0x000020b0;
#endif
#endif
}

void _stdcall gos_ToggleIME(bool onOff)
{
	HIMC himc;
	if (onOff)
		gos_EnableIME(onOff);
	if (NULL != (himc = _ImmGetContext(hWindow))) 
	{
		SPEW(("jreicher", "imeon = %d", onOff));
		if (g_bDisableImeCompletely)
			onOff = false;
		_ImmSetOpenStatus(himc, onOff);
		g_bComposingIME = onOff;

		_ImmReleaseContext(hWindow, himc);
	}
}

DWORD GetIMEInfo()
{
	HIMC himc;
	if (NULL != (himc = _ImmGetContext(hWindow))) 
	{
		DWORD val = _ImmGetOpenStatus(himc) ? 0x1: 0x0;
		_ImmReleaseContext(hWindow, himc);
		return val;
	}
	else
		return 0x0;
}

void _stdcall gos_EnableIME( bool onOff )
{
	if (!hWindow)
		return;
	if (g_bDisableImeCompletely)
		onOff = false;

	SPEW(("jreicher", "IME enabled: %d", onOff));

	HIMC himcDbg;
	himcDbg = _ImmAssociateContext( hWindow, onOff ? g_himcOrg : NULL );
	g_bImeEnabled = onOff;
}

void ShowIMEActive(bool flag)
{
	// draw a fan in greyscale if not active
	// draw it in swirling rainbow if active
	
	gos_PushRenderStates();
	gos_SetRenderState( gos_State_Texture, 0 );
	gos_SetRenderState( gos_State_Filter, gos_FilterNone );
	gos_SetRenderState( gos_State_ZCompare, 0 );
	gos_SetRenderState( gos_State_ZWrite, 0 );
	gos_SetRenderState( gos_State_AlphaTest, 0 );
	gos_SetRenderState( gos_State_Perspective, 0 );
	gos_SetRenderState( gos_State_Specular, 0 );
	gos_SetRenderState( gos_State_Dither, 0 );
	gos_SetRenderState( gos_State_Clipping, 2 );
	gos_SetRenderState( gos_State_TextureMapBlend, gos_BlendModulate );
	gos_SetRenderState( gos_State_ShadeMode, gos_ShadeGouraud );
	gos_SetRenderState( gos_State_MonoEnable, 0 );

	// if we want translucence
	gos_SetRenderState( gos_State_AlphaMode, gos_Alpha_AlphaInvAlpha );

	gos_VERTEX PieData[17];	
	float SizeOfPie = (float) gSkinIME.symbolHeight;

	memset( &PieData[0], 0, sizeof(PieData) );

	switch(gSkinIME.symbolPlacement)
	{
	case 0:
		{
			if (SizeOfPie + g_CaretInfo.margins.right+4 > Environment.screenWidth)
			{
				PieData[0].x=(-SizeOfPie/2) + g_CaretInfo.margins.left-4;
				PieData[0].y= (float) g_CaretInfo.margins.bottom - (gSkinIME.symbolHeight/2);
			}
			else
			{
				PieData[0].x=-(SizeOfPie/2) + g_CaretInfo.margins.right+gSkinIME.symbolHeight+4;
				PieData[0].y= (float) g_CaretInfo.margins.bottom - (gSkinIME.symbolHeight/2);
			}
			break;
		}
	case 1: // upperleft
		PieData[0].x = 4+ (SizeOfPie/2);
		PieData[0].y = 4+ (SizeOfPie/2);
		break;
	case 2: // upperright
		PieData[0].x = Environment.screenWidth - (4+ (SizeOfPie/2));
		PieData[0].y = 4+ (SizeOfPie/2);
		break;
	case 3: // lowerright
		PieData[0].x = Environment.screenWidth - (4+ (SizeOfPie/2));
		PieData[0].y = Environment.screenHeight - (4+ (SizeOfPie/2));
		break;
	case 4: // lowerleft
		PieData[0].x = 4+ (SizeOfPie/2);
		PieData[0].y = Environment.screenHeight - (4+ (SizeOfPie/2));
		break;
	}
	PieData[0].rhw=1.0f;
	if (flag)
	{
		if (gos_GetElapsedTime() - lastSwirl > .25) 
		{
			swirl++;
			lastSwirl = gos_GetElapsedTime();
			if (swirl > 13)
				swirl = 0;
		}
	}
	else
		swirl = 0;
	for( int t1=1; t1<16; t1++ )
	{
		PieData[t1].x=(float)(PieData[0].x+SizeOfPie/2*sin( (2.0f*3.1415926f*(t1-1+(flag*swirl)))/14.0f ));
		PieData[t1].y=(float)(PieData[0].y-SizeOfPie/2*cos( (2.0f*3.1415926f*(t1-1+(flag*swirl)))/14.0f ));
		PieData[t1].rhw=1.0f;
	}
	PieData[t1].x=(float)(PieData[0].x+SizeOfPie/2*sin( 0 ));
	PieData[t1].y=(float)(PieData[0].y-SizeOfPie/2*cos( 0 ));
	PieData[t1].rhw=1.0f;

	if (!gSkinIME.symbolColor)
	{
		if (!flag)
		{
			PieData[0].argb=0xffffff +  (((DWORD)gSkinIME.symbolTranslucence)<<24);
			for( int t1=1; t1<16; t1++ )
			{
				PieData[t1].argb=0x404040 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
			}
		}
		else
		{
		 	PieData[0].argb=0xffffff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[1].argb=0xff0000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[2].argb=0xff3000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[3].argb=0xff6000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[4].argb=0xff9000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[5].argb=0xffC000 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[6].argb=0xffff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[7].argb=0xC0ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[8].argb=0x90ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[9].argb=0x60ff00 + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[10].argb=0x30c0ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[11].argb=0x00a0ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[12].argb=0x3090ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
	 		PieData[13].argb=0x6060ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[14].argb=0x9030ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		 	PieData[15].argb=0xc000ff + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		}
	}
	else
	{
		PieData[0].argb=0xffffff +  (((DWORD)gSkinIME.symbolTranslucence)<<24);
		for( int t1=1; t1<16; t1++ )
		{
			PieData[t1].argb=gSkinIME.symbolColor + (((DWORD)gSkinIME.symbolTranslucence)<<24);
		}
	}
#ifndef GAMEZ_GOS_PORT
	gos_DrawFans( PieData, 16 );
#else
	gos_VERTEX temp_quad[4];
	temp_quad[0] = temp_quad[3] = PieData[0];
	for (int i = 1; i < 15; i++) {
		temp_quad[1] = PieData[i];
		temp_quad[2] = PieData[i+1];
		gos_DrawQuads(temp_quad, 4);
	}
#endif

	gos_PopRenderStates();

	gos_TextSetAttributes(g_CaretInfo.fontHandle, (((DWORD)gSkinIME.symbolTranslucence)<<24), -gSkinIME.symbolHeight*0.625f, false, true, flag, false); 

	gos_TextSetRegion( 0, 0, Environment.screenWidth, Environment.screenHeight );

	//
	// draw the proper symbol over the fan
	//
	DWORD  w, h;
	if (flag)
	{
		gos_TextStringLength(&w, &h, (const char *) g_SymbolIME);
		gos_TextSetPosition(float2long(PieData[0].x)-w/2, float2long(PieData[0].y)-h/2);
		gos_TextDraw( (const char *) g_SymbolIME );
	}
	else
	{
		gos_TextStringLength(&w, &h, (const char *) "A");
		gos_TextSetPosition(float2long(PieData[0].x)-w/2, float2long(PieData[0].y)-h/2);
		gos_TextDraw( (const char *) "A" );
	}

#ifdef INTERNAL_DEPENDENCY
	if (Environment.DirtyRectangle)
		gosDirtyRectangeRestoreArea( float2long(PieData[0].x)-(gSkinIME.symbolHeight/2), float2long(PieData[0].y)-(gSkinIME.symbolHeight/2), float2long(PieData[0].x)+(gSkinIME.symbolHeight/2), float2long(PieData[0].y)+(gSkinIME.symbolHeight/2) );
#endif
}

void InitializeIME(HWND hWindow)
{
	static bool bInitialized = false;
	if (!bInitialized) {
		bInitialized = true;
		g_himcOrg = _ImmGetContext(hWindow);
		_ImmReleaseContext(hWindow, g_himcOrg);
	}

	// Turn off IME toolbar
	HWND hwndImeDef = _ImmGetDefaultIMEWnd(hWindow);
	if (hwndImeDef)
		SendMessage(hwndImeDef, WM_IME_CONTROL, IMC_CLOSESTATUSWINDOW, 0);
 
#ifndef GAMEZ_GOS_PORT
	DWORD allowIME = false;
	DWORD dwSize = sizeof(allowIME);

	g_bDisableImeCompletely = true;
	gos_LoadDataFromRegistry("AllowIme", &allowIME, &dwSize);
	if (allowIME)
		g_bDisableImeCompletely = false;
#else
	zCfgItem* allowIME;
	
	if (allowIME = zcfg_GetItem("AllowIME"))
		g_bDisableImeCompletely = (allowIME->value.ival == FALSE);

	//
	//
	// Open the GameOS Resource.dll and check the GOS_LOCALE string in the string table
	// If Language.dll isn't found, set the locale the English (United States)
	//
	if (gLanguageDLL = gos_OpenResourceDLL("Assets\\Binaries\\Language.dll")) {

		strncpy(gApplicationLocale, gos_GetResourceString( gLanguageDLL, 0), MAX_PATH-1 );

		//
		// Set all the the language "formatting"
		// to be in the resource's locale (LC_ALL).
		//
		setlocale(LC_ALL, gApplicationLocale);
		if (strcmp(gApplicationLocale, "English") == 0 && GetACP() != 1252)
		{
			char szCodePage[16];
			sprintf(szCodePage, ".%d", GetACP());
			setlocale(LC_CTYPE, szCodePage);
		}
	}

	for( int t0=0; t0<256; t0++ )
		LeadByteTable[t0]=(BYTE)(isleadbyte(t0) ? 1 : 0);

#endif

	gSkinIME.baseColor = 0xffffffff;
	gSkinIME.borderColor = 0xff000000;
	gSkinIME.symbolTranslucence = 0xA0;
	gSkinIME.symbolHeight = 24;
	gSkinIME.symbolPlacement = 0;

	switch (GetACP())
	{
	// Simplified Chinese (TBD)
	case 936:
		g_SymbolIME[0] = 0xa4;
		g_SymbolIME[1] = 0xa4;
		break;
	// Trad Chinese (Taiwan)
	case 950:
		g_SymbolIME[0] = 0xa4;
		g_SymbolIME[1] = 0xa4;
		break;
	// Korean (TBD)
	case 949:
		g_SymbolIME[0] = 0xb0;
		g_SymbolIME[1] = 0xa1;
		break;
 	// Japanese	& other	(0x411 = Japanese)
	case 932:
		g_SymbolIME[0] = 0x82;
		g_SymbolIME[1] = 0xa0;
		break;							   
	default:
		g_SymbolIME[0] = 'A';
		g_SymbolIME[1] = 0;
	}
	g_SymbolIME[2] = 0x00;
}

#ifdef GAMEZ_GOS_PORT
void DestroyIME()
{
	if (gLanguageDLL)
	{
		gos_CloseResourceDLL( gLanguageDLL );
		gLanguageDLL = 0;
	}
	gos_EnableIME(false);	// make sure that IME is disabled in game
}

bool IMECheckKey(char c)
{
	bool bRc = g_bCompStrOn || (g_bComposingIME && isprint(c)) || g_bIgnoreKey;
	g_bIgnoreKey = false;
	return bRc;
}
#endif

///////////////////////////////////
// Check Taiwan New Phonetic IME //
///////////////////////////////////

#define _TAIWAN_HKL			((HKL)0xE0080404)
#define _TAIWAN_IMEFILENAME	"TINTLGNT.IME"
#define _TAIWAN_IMEVERSION43 (0x00040003) // 4.3.x.x // Win98SE or WebRelease
#define _TAIWAN_IMEVERSION50 (0x00050000) // 5.0.x.x // WinMe

DWORD isValidTaiwanIME()
{
#ifdef GAMEZ_GOS_PORT
	return 0;
#else
	DWORD dwRet = 0;
	
	DWORD 	dwVerSize;
	DWORD 	dwVerHandle;
	LPVOID	lpVerBuffer;
	LPVOID	lpVerData;
	UINT	cbVerData;
	
	char szTmp[1024];
	HKL kl = GetKeyboardLayout(0);
	SPEW(("jreicher", "GetKeyboardLayout(0)=%08lX", kl ));
	if ( kl != _TAIWAN_HKL ){ // check non-Taiwan env
		return dwRet;
	}
	
	if ( _ImmGetIMEFileName( kl, szTmp, sizeof(szTmp)-1 ) <= 0 ){
		return dwRet;
	}
	SPEW(("jreicher", "ImmGetIMEFileName=[%s]", szTmp));

	if ( strcmp( szTmp, _TAIWAN_IMEFILENAME) != 0 )
		return dwRet;
	
	dwVerSize = GetFileVersionInfoSize( szTmp, &dwVerHandle );
	if( dwVerSize )
	{
		lpVerBuffer = (LPVOID) gos_Malloc(dwVerSize);
		if( lpVerBuffer )
		{
			if( GetFileVersionInfo( szTmp, dwVerHandle, dwVerSize, lpVerBuffer ) )
			{
				if( VerQueryValue( lpVerBuffer, "\\", &lpVerData, &cbVerData ) )
				{
					#define pVerFixedInfo ((VS_FIXEDFILEINFO FAR*)lpVerData)
					SPEW(("jreicher", "FileVersion=[%d.%d.%d.%d]", HIWORD(pVerFixedInfo->dwFileVersionMS), LOWORD(pVerFixedInfo->dwFileVersionMS), HIWORD(pVerFixedInfo->dwFileVersionLS), LOWORD(pVerFixedInfo->dwFileVersionLS) ));
					if ( pVerFixedInfo->dwFileVersionMS == _TAIWAN_IMEVERSION43 || 
					     pVerFixedInfo->dwFileVersionMS == _TAIWAN_IMEVERSION50 ){
						dwRet = pVerFixedInfo->dwFileVersionMS;
					}
					#undef pVerFixedInfo
				}
			}
		}
		gos_Free(lpVerBuffer);
	}
	
	return dwRet;
#endif
}

void GetReadingString(HWND hWnd)
{
	DWORD dwTaiwanIMEVersion = isValidTaiwanIME();
	if ( ! dwTaiwanIMEVersion )
		return;
	
	HIMC himc;
	
	himc = _ImmGetContext(hWnd);
	
	if (NULL != himc )
	{
		LPINPUTCONTEXT lpIMC = _ImmLockIMC(himc);
		
		// *** hacking code from Michael Yang ***
		
		LPBYTE p = 0;
		DWORD dwlen = 0;
		DWORD dwerr = 0;
		WCHAR *wstr = 0;
		bool unicode = FALSE;
		
		switch ( dwTaiwanIMEVersion ){
		
		case _TAIWAN_IMEVERSION43: // New Phonetic IME 4.3 (Win98SE/WebRelease)
			
			p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 24);
			dwlen = *(DWORD *)(p + 7*4 + 32*4);	//m_dwInputReadStrLen
			dwerr = *(DWORD *)(p + 8*4 + 32*4);	//m_dwErrorReadStrStart
			wstr = (WCHAR *)(p + 56);
			unicode = TRUE;
			break;
		
		case _TAIWAN_IMEVERSION50: // New Phonetic IME 5.0 (WinMe)
			
			p = *(LPBYTE *)((LPBYTE)_ImmLockIMCC(lpIMC->hPrivate) + 3*4); // PCKeyCtrlManager
			p = *(LPBYTE *)((LPBYTE)p + 1*4 + 5*4 + 4*2 ); // = PCReading = &STypingInfo
			dwlen = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16);		//m_dwDisplayStringLength;
			dwerr = *(DWORD *)(p + 1*4 + (16*2+2*4) + 5*4 + 16 + 1*4);	//m_dwDisplayErrorStart;
			wstr = (WCHAR *)(p + 1*4 + (16*2+2*4) + 5*4);
			unicode = FALSE;
			break;
		}
		
		SPEW(("jreicher", "READING Len=%d", dwlen));

		g_dwCount = dwlen;


		g_szCandidate[0][0] = 0;
		g_szCandidate[1][0] = 0;
		g_szCandidate[2][0] = 0;
		g_szCandidate[3][0] = 0;
		
		if ( unicode ){
			g_dwSelection = -1; // do not select any char
			for ( int i=0; i < dwlen ; i++ ) // dwlen > 0, if known IME : yutakah
			{
				if ( dwerr<=i && g_dwSelection==-1 ){ // select error char
					g_dwSelection = i;
				}

				char mbc[3];
				mbc[1] = 0;
				mbc[2] = 0;
				wctomb(mbc, wstr[i]);

				g_szCandidate[i][0] = mbc[0];
				g_szCandidate[i][1] = mbc[1];
				g_szCandidate[i][2] = 0;


				SPEW(( "jreicher", "  [%d]: UNICODE %04x MBCS %s", i, wstr[i], mbc ));
			}
			g_szCandidate[i][0] = 0;
		} else {
			TCHAR *p = (TCHAR *)wstr;
			g_dwSelection = -1;
			for ( int i=0, j=0; i < dwlen ; i++,j++ ) // dwlen > 0, if known IME : yutakah
			{
				if ( dwerr<=i && g_dwSelection==-1 ){
					g_dwSelection = j;
				}
				g_szCandidate[j][0] = p[i];
				g_szCandidate[j][1] = 0;
				g_szCandidate[j][2] = 0;
#ifdef INTERNAL_DEPENDENCY
				if (LeadByteTable[(unsigned char)p[i]])
#else
				if ( isleadbyte((unsigned char)p[i]) )
#endif
				{
					i++;
					g_szCandidate[j][1] = p[i];
				}

				SPEW(( "jreicher", "  [%d]: MBCS %s", i, g_szCandidate[j] ));

			}
			g_szCandidate[j][0] = 0;
			g_dwCount = j;
		}
		
		_ImmUnlockIMCC( lpIMC->hPrivate );
		_ImmUnlockIMC( himc );
		_ImmReleaseContext(hWnd, himc);
	}
}

void gos_FinalizeStringIME(void)
{
	if (g_szCompositionString[0] == 0)
		return;
	// This is to leave composition string in app's edit box
	IMEInitCompStringData();
	// clear composition string in IME
	HIMC himc;
	if (NULL == (himc = _ImmGetContext(hWindow)))
		return ;
	_ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL, 0);
	_ImmReleaseContext(hWindow, himc);
	return;
}

void gos_SetIMEInsertMode(int bInsert)
{
	g_bInsertMode = bInsert ? true : false;
}
