/**********************************************************************/
/* Copyright (C) 1994-1995 Microsoft Corporation.                     */
/*      J. J. Lee                                                     */
/**********************************************************************/
#include <windows.h>
#include <imm.h>
#include "imeattr.h"
#include "imedefs.h"
#if defined(UNIIME)
#include "uniime.h"
#endif

/**********************************************************************/
/* ChkIMEHotKey()                                                     */
/* Return Value:                                                      */
/*      the ID of hot key                                             */
/**********************************************************************/
UINT PASCAL ChkIMEHotKey(
    UINT           uVirtKey,
    CONST LPBYTE   lpbKeyState)
{
    UINT uModifyKeys;
    UINT uRL;
    UINT i;

    // find IME hot keys
    uModifyKeys = 0;
    uRL = 0;

    if (lpbKeyState[VK_MENU] & 0x80) {
        uModifyKeys |= MOD_ALT;

        if (lpbKeyState[VK_LMENU] & 0x80) {
            uRL |= MOD_LEFT;
        } else if (lpbKeyState[VK_RMENU] & 0x80) {
            uRL |= MOD_RIGHT;
        } else {
            // above can not work in Win95, so fall into this
            uRL = (MOD_LEFT|MOD_RIGHT);
        }
    }

    if (lpbKeyState[VK_CONTROL] & 0x80) {
        uModifyKeys |= MOD_CONTROL;

        if (lpbKeyState[VK_LCONTROL] & 0x80) {
            uRL |= MOD_LEFT;
        } else if (lpbKeyState[VK_RCONTROL] & 0x80) {
            uRL |= MOD_RIGHT;
        } else {
            // above can not work in Win95, so fall into this
            uRL = (MOD_LEFT|MOD_RIGHT);
        }
    }

    if (lpbKeyState[VK_SHIFT] & 0x80) {
        uModifyKeys |= MOD_SHIFT;

        if (lpbKeyState[VK_LSHIFT] & 0x80) {
            uRL |= MOD_LEFT;
        } else if (lpbKeyState[VK_RSHIFT] & 0x80) {
            uRL |= MOD_RIGHT;
        } else {
            // above can not work in Win95, so fall into this
            uRL = (MOD_LEFT|MOD_RIGHT);
        }
    }

    if (lpbKeyState[VK_LWIN] & 0x80) {
        uModifyKeys |= MOD_WIN;
        uRL |= MOD_LEFT;
    } else if (lpbKeyState[VK_RWIN] & 0x80) {
        uModifyKeys |= MOD_WIN;
        uRL |= MOD_RIGHT;
    } else {
    }

    if (!uRL) {
        uRL = (MOD_LEFT|MOD_RIGHT);
    }

    for (i = 0; i < NUM_OF_IME_HOTKEYS; i++) {
        // virtual key
        if (sImeG.uVKey[i] != uVirtKey) {
            // virtual key unmatched!
            continue;
        }

        if (sImeG.uModifiers[i] & MOD_IGNORE_ALL_MODIFIER) {
        } else if ((sImeG.uModifiers[i] &
            (MOD_ALT|MOD_CONTROL|MOD_SHIFT|MOD_WIN)) != uModifyKeys) {
            // modifiers unmatched!
            continue;
        } else {
        }

        if ((sImeG.uModifiers[i] & (MOD_LEFT|MOD_RIGHT)) ==
            (MOD_LEFT|MOD_RIGHT)) {
            return (CST_IME_HOTKEYS + i);
        }

        // we don't have way to distinguish left & right yet
        if ((sImeG.uModifiers[i] & (MOD_LEFT|MOD_RIGHT)) == uRL) {
            return (CST_IME_HOTKEYS + i);
        }
    }

    // not a hot key
    return (0);
}

/**********************************************************************/
/* ProcessKey()                                                       */
/* Return Value:                                                      */
/*      different state which input key will change IME to            */
/**********************************************************************/
UINT PASCAL ProcessKey(     // this key will cause the IME go to what state
#if defined(UNIIME)
    LPINSTDATAL    lpInstL,
    LPIMEL         lpImeL,
#endif
    WORD           wCharCode,
    UINT           uVirtKey,
    UINT           uScanCode,
    CONST LPBYTE   lpbKeyState,
    LPINPUTCONTEXT lpIMC,
    LPPRIVCONTEXT  lpImcP)
{
    if (!lpIMC) {
        return (CST_INVALID);
    }

    if (!lpImcP) {
        return (CST_INVALID);
    }

    if ((lpIMC->fdwConversion & (IME_CMODE_NATIVE|IME_CMODE_EUDC|
        IME_CMODE_NOCONVERSION|IME_CMODE_CHARCODE)) == IME_CMODE_NATIVE) {
        UINT uHotKeyID;

        uHotKeyID = ChkIMEHotKey(uVirtKey, lpbKeyState);

        if (uHotKeyID) {
            return (uHotKeyID);
        }
    }

    if (uVirtKey == VK_MENU) {       // no ALT key
        return (CST_INVALID);
    } else if (uScanCode & KF_ALTDOWN) {    // no ALT-xx key
        return (CST_INVALID);
    } else if (uVirtKey == VK_CONTROL) {    // no CTRL key
        return (CST_INVALID);
    } else if (lpbKeyState[VK_CONTROL] & 0x80) {    // no CTRL-xx key
        return (CST_INVALID);
    } else if (uVirtKey == VK_SHIFT) {      // no SHIFT key
        return (CST_INVALID);
    } else if (!lpIMC->fOpen) {             // don't compose in close status
        return (CST_INVALID);
    } else if (lpIMC->fdwConversion & IME_CMODE_NOCONVERSION) {
        // don't compose in no coversion status
        return (CST_INVALID);
    } else if (lpIMC->fdwConversion & IME_CMODE_CHARCODE) {
        // not support
        return (CST_INVALID);
    } else {
        // need more check
    }

#if !defined(ROMANIME)
    if (lpIMC->fdwConversion & IME_CMODE_SYMBOL) {
        if (uVirtKey == VK_ESCAPE) {
            return (CST_SYMBOL);
        }
    }
#endif

#if defined(DAYI)
    if (lpIMC->fdwConversion & IME_CMODE_NATIVE) {
        if (wCharCode == '=') {
            return (CST_SYMBOL);
        }
    }
#endif

#if !defined(ROMANIME)
    if (lpImcP->fdwImeMsg & (MSG_ALREADY_OPEN|MSG_ALREADY_OPEN2)) {
        if (uVirtKey == VK_PRIOR) {
            return (CST_CHOOSE);
        } else if (uVirtKey == VK_NEXT) {
            return (CST_CHOOSE);
        } else if (uVirtKey == VK_ESCAPE) {
            return (CST_CHOOSE);
#if defined(DAYI)
        } else if (uVirtKey == VK_LEFT) {
            return (CST_CHOOSE);
        } else if (uVirtKey == VK_UP) {
            return (CST_CHOOSE);
        } else if (uVirtKey == VK_RIGHT) {
            return (CST_CHOOSE);
#elif defined(WINAR30)
#else
        } else if (wCharCode == '<') {
            return (CST_CHOOSE);
        } else if (wCharCode == '>') {
            return (CST_CHOOSE);
        } else if (wCharCode == '?') {
            return (CST_CHOOSE);
#endif
        } else {
            // need more check
        }
    }

    if (lpImcP->iImeState == CST_CHOOSE) {
        if (wCharCode > 'z') {
            return (CST_INVALID);
        } else if (wCharCode < ' ') {
            return (CST_INVALID);
        } else if (wCharCode >= '0' && wCharCode <= '9') {
#if defined(WINAR30)
        } else if (wCharCode == '<') {
            return (CST_CHOOSE);
        } else if (wCharCode == '>') {
            return (CST_CHOOSE);
        } else if (wCharCode == '?') {
            return (CST_CHOOSE);
#endif
        } else {
            wCharCode = bUpper[wCharCode - ' '];

#if defined(PHON)
            // convert different phonetic keyboard layout to ACER
            wCharCode = bStandardLayout[lpImeL->nReadLayout]
                [wCharCode - ' '];
#endif
        }

        if (wCharCode > '_') {
        } else if (uVirtKey >= VK_NUMPAD0 && uVirtKey <= VK_NUMPAD9) {
            if (uVirtKey >= (VK_NUMPAD0 + (UINT)lpImeL->wCandRangeStart)) {
                return (CST_CHOOSE);
            } else {
                return (CST_ALPHANUMERIC);
            }
        } else if (lpImeL->fChooseChar[(wCharCode - ' ') >> 4] &
            fMask[wCharCode & 0x000F]) {        // convert to upper case
            return (CST_CHOOSE);
        } else {
        }

        if (!(lpIMC->fdwConversion & IME_CMODE_NATIVE)) {
            // alphanumeric mode
            if (wCharCode >= ' ' && wCharCode <= '~') {
                return (CST_ALPHANUMERIC);
            } else {
                return (CST_INVALID);
            }
        } else if (uVirtKey >= 'A' && uVirtKey <= 'Z') {
            return (CST_ALPHABET);
        } else if (wCharCode >= ' ' && wCharCode <= '~') {
            return (CST_ALPHANUMERIC);
        } else {
            return (CST_INVALID);
        }
    }

    // not in choose mode but candidate alaredy open,
    // we must under quick view or phrase prediction
    if (lpImcP->fdwImeMsg & (MSG_ALREADY_OPEN|MSG_ALREADY_OPEN2)) {
        if (lpImcP->iImeState == CST_INIT) {
            if (lpbKeyState[VK_SHIFT] & 0x80) {
                if ((uVirtKey >= '0' + (UINT)lpImeL->wCandRangeStart) &&
                    uVirtKey <= '9') {
                    return (CST_CHOOSE);
                }
            }
#if defined(WINAR30)
        } else {
            if (wCharCode >= '0' && wCharCode <= '9') {
                if (*(LPDWORD)lpImcP->bSeq != 0x17) {
                    return (CST_CHOOSE);
                }
            }
#endif
        }
    }

    if (uVirtKey >= VK_NUMPAD0 && uVirtKey <= VK_DIVIDE) {
        // as PM decide all numpad should be past to app
        return (CST_ALPHANUMERIC);
    }
#endif

#if defined(ROMANIME)
    if (wCharCode >= ' ' && wCharCode <= '~') {
        if (lpIMC->fdwConversion & IME_CMODE_FULLSHAPE) {
            return (CST_ALPHANUMERIC);
        } else {
            return (CST_INVALID);
        }
    }
#else
    if (!(lpIMC->fdwConversion & IME_CMODE_NATIVE)) {
        // alphanumeric mode
        if (wCharCode >= ' ' && wCharCode <= '~') {
            if (lpIMC->fdwConversion & IME_CMODE_FULLSHAPE) {
                return (CST_ALPHANUMERIC);
            } else {
                return (CST_INVALID);
            }
        } else {
            return (CST_INVALID);
        }
    } else if (!(lpbKeyState[VK_SHIFT] & 0x80)) {
        // need more check for IME_CMODE_NATIVE
#if defined(DAYI)
    } else if (lpIMC->fdwConversion & IME_CMODE_SYMBOL) {
#endif
    } else if (uVirtKey >= 'A' && uVirtKey <= 'Z') {
        return (CST_ALPHABET);
    } else if (wCharCode >= ' ' && wCharCode <= '~') {
        // need more check for IME_CMODE_NATIVE
    } else {
        return (CST_INVALID);
    }

    // IME_CMODE _EUDC will use the same state with IME_CMODE_NATIVE

    if (wCharCode >= ' ' && wCharCode <= 'z') {
        wCharCode = bUpper[wCharCode - ' '];
#if defined(PHON)
        {
            // convert different phonetic keyboard layout to ACER
            wCharCode = bStandardLayout[lpImeL->nReadLayout][wCharCode - ' '];
        }
#endif
    }

    if (uVirtKey == VK_ESCAPE) {
        register LPGUIDELINE lpGuideLine;
        register UINT        iImeState;

        if (lpImcP->fdwImeMsg & MSG_ALREADY_START) {
            return (CST_INPUT);
        }

        lpGuideLine = ImmLockIMCC(lpIMC->hGuideLine);

        if (!lpGuideLine) {
            return (CST_INVALID);
        } else if (lpGuideLine->dwLevel == GL_LEVEL_NOGUIDELINE) {
            iImeState = CST_INVALID;
        } else {
            // need this key to clean information string or guideline state
            iImeState = CST_INPUT;
        }

        ImmUnlockIMCC(lpIMC->hGuideLine);

        return (iImeState);
    } else if (uVirtKey == VK_BACK) {
        if (lpImcP->fdwImeMsg & MSG_ALREADY_START) {
            return (CST_INPUT);
        } else {
            return (CST_INVALID);
        }
#if 0
    } else if (uVirtKey >= VK_NUMPAD0 && uVirtKey <= VK_DIVIDE) {
        // as PM decide all numpad should be past to app
        return (CST_ALPHANUMERIC);
#endif
    } else if (wCharCode > '~') {
        return (CST_INVALID);
    } else if (wCharCode < ' ') {
        return (CST_INVALID);
#if !defined(ROMANIME)
    } else if (lpIMC->fdwConversion & IME_CMODE_EUDC) {
    } else if (lpIMC->fdwConversion & IME_CMODE_SYMBOL) {
        return (CST_SYMBOL);
    } else if (lpInstL->fdwTblLoad == TBL_LOADERR) {
        return (CST_INVALID);
    } else if (lpInstL->fdwTblLoad == TBL_NOTLOADED) {
#ifdef DEBUG
        _asm int 3;
#endif
        if (++lpInstL->cRefCount <= 0) {
            lpInstL->cRefCount = 1;
        }

        LoadTable(lpInstL, lpImeL);
#endif
    } else {
    }

    // check finalize char
    if (wCharCode == ' ' && lpImcP->iImeState == CST_INIT) {
        if (lpIMC->fdwConversion & IME_CMODE_FULLSHAPE) {
            return (CST_ALPHANUMERIC);
        } else {
            return (CST_INVALID);
        }
    } else if (lpImeL->fCompChar[(wCharCode - ' ') >> 4] &
        fMask[wCharCode & 0x000F]) {
        return (CST_INPUT);
    } else if (lpIMC->fdwConversion & IME_CMODE_EUDC) {
        return (CST_INVALID);
#if defined(WINAR30)
    } else if (*(LPDWORD)lpImcP->bSeq == 0x17 && wCharCode >= '0' &&
        wCharCode <= '9') {
        return (CST_SYMBOL);
#elif defined(DAYI)
    } else if (lpImeL->wChar2SeqTbl[wCharCode - ' '] >= 0x30 &&
        lpImeL->wChar2SeqTbl[wCharCode - ' '] <= 0x35) {
        return (CST_ROAD);
#endif
    } else if (lpIMC->fdwConversion & IME_CMODE_FULLSHAPE) {
        return (CST_ALPHANUMERIC);
    } else {
        return (CST_INVALID);
    }
#endif // !ROMANIME

    return (CST_INVALID);
}

/**********************************************************************/
/* ImeProcessKey() / UniImeProcessKey()                               */
/* Return Value:                                                      */
/*      TRUE - successful, FALSE - failure                            */
/**********************************************************************/
// if this key is need by IME?
#if defined(UNIIME)
BOOL WINAPI UniImeProcessKey(
    LPINSTDATAL  lpInstL,
    LPIMEL       lpImeL,
#else
BOOL WINAPI ImeProcessKey(
#endif
    HIMC         hIMC,
    UINT         uVirtKey,
    LPARAM       lParam,
    CONST LPBYTE lpbKeyState)
{
    LPINPUTCONTEXT lpIMC;
    LPPRIVCONTEXT  lpImcP;
    BYTE           szAscii[4];
    int            nChars;
    BOOL           fRet;

    // can't compose in NULL hIMC
    if (!hIMC) {
        return (FALSE);
    }

    lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
    if (!lpIMC) {
        return (FALSE);
    }

    lpImcP = (LPPRIVCONTEXT)ImmLockIMCC(lpIMC->hPrivate);
    if (!lpImcP) {
        ImmUnlockIMC(hIMC);
        return (FALSE);
    }

    nChars = ToAscii(uVirtKey, HIWORD(lParam), lpbKeyState,
                (LPVOID)szAscii, 0);

    if (!nChars) {
        szAscii[0] = 0;
    }

    if (ProcessKey(
#if defined(UNIIME)
        lpInstL, lpImeL,
#endif
        (WORD)szAscii[0], uVirtKey, HIWORD(lParam), lpbKeyState,
        lpIMC, lpImcP) == CST_INVALID) {
        fRet = FALSE;
    } else {
        fRet = TRUE;
    }

    ImmUnlockIMCC(lpIMC->hPrivate);
    ImmUnlockIMC(hIMC);

    return (fRet);
}

/**********************************************************************/
/* TranslateToAscii()                                                 */
/* Return Value:                                                      */
/*      the number of translated chars                                */
/**********************************************************************/
UINT PASCAL TranslateToAscii(       // translate the key to WM_CHAR
                                    // as keyboard driver
    UINT    uVirtKey,
    UINT    uScanCode,
    LPDWORD lpdwTransBuf,
    UINT    uNumMsg,
    WORD    wCharCode)
{
    if (wCharCode) {                    // one char code
        // 3 DWORD (message, wParam, lParam)
        lpdwTransBuf = lpdwTransBuf + 1 + uNumMsg * 3;

        *lpdwTransBuf++ = WM_CHAR;
        *lpdwTransBuf++ = wCharCode;
        *lpdwTransBuf = (uScanCode << 16) | 1UL;
        return (1);
    }

    // no char code case
    return (0);
}

/**********************************************************************/
/* TranslateImeMessage()                                              */
/* Return Value:                                                      */
/*      the number of translated messages                             */
/**********************************************************************/
UINT PASCAL TranslateImeMessage(
    LPDWORD        lpdwTransBuf,
    LPINPUTCONTEXT lpIMC,
    LPPRIVCONTEXT  lpImcP)
{
    UINT uNumMsg;
    UINT i;
    BOOL bLockMsgBuf;

    uNumMsg = 0;
    bLockMsgBuf = FALSE;

    for (i = 0; i < 2; i++) {
#if !defined(ROMANIME)
        if (lpImcP->fdwImeMsg & MSG_IMN_COMPOSITIONSIZE) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_PRIVATE;
                *lpdwTransBuf++ = IMN_PRIVATE_COMPOSITION_SIZE;
            }
        }

        if (lpImcP->fdwImeMsg & MSG_START_COMPOSITION) {
            if (!(lpImcP->fdwImeMsg & MSG_ALREADY_START)) {
                if (!i) {
                    uNumMsg++;
                } else {
                    *lpdwTransBuf++ = WM_IME_STARTCOMPOSITION;
                    *lpdwTransBuf++ = 0;
                    *lpdwTransBuf++ = 0;
                    lpImcP->fdwImeMsg |= MSG_ALREADY_START;
                }
            }
        }

        if (lpImcP->fdwImeMsg & MSG_IMN_COMPOSITIONPOS) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_SETCOMPOSITIONWINDOW;
                *lpdwTransBuf++ = 0;
            }
        }
#endif

        if (lpImcP->fdwImeMsg & MSG_COMPOSITION) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_COMPOSITION;
                *lpdwTransBuf++ = (DWORD)lpImcP->dwCompChar;
                *lpdwTransBuf++ = (DWORD)lpImcP->fdwGcsFlag;
            }
        }

#if !defined(ROMANIME)
        if (lpImcP->fdwImeMsg & MSG_GUIDELINE) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_GUIDELINE;
                *lpdwTransBuf++ = 0;
            }
        }

        if (lpImcP->fdwImeMsg & MSG_IMN_PAGEUP) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_PRIVATE;
                *lpdwTransBuf++ = IMN_PRIVATE_PAGEUP;
            }
        }

        if (lpImcP->fdwImeMsg & MSG_OPEN_CANDIDATE) {
            if (!(lpImcP->fdwImeMsg & MSG_ALREADY_OPEN)) {
                if (!i) {
                    uNumMsg++;
                } else {
                    *lpdwTransBuf++ = WM_IME_NOTIFY;
                    *lpdwTransBuf++ = IMN_OPENCANDIDATE;
                    *lpdwTransBuf++ = 0x0001;
                    lpImcP->fdwImeMsg |= MSG_ALREADY_OPEN;
                }
            }
        }

        if (lpImcP->fdwImeMsg & MSG_CHANGE_CANDIDATE) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_CHANGECANDIDATE;
                *lpdwTransBuf++ = 0x0001;
            }
        }

        if (lpImcP->fdwImeMsg & MSG_IMN_UPDATE_PREDICT) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_PRIVATE;
                *lpdwTransBuf++ = IMN_PRIVATE_UPDATE_PREDICT;
            }
        }

#if defined(WINAR30)
        if (lpImcP->fdwImeMsg & MSG_IMN_UPDATE_QUICK_KEY) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_PRIVATE;
                *lpdwTransBuf++ = IMN_PRIVATE_UPDATE_QUICK_KEY;
            }
        }
#else
        if (lpImcP->fdwImeMsg & MSG_IMN_UPDATE_SOFTKBD) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_PRIVATE;
                *lpdwTransBuf++ = IMN_PRIVATE_UPDATE_SOFTKBD;
            }
        }
#endif

        if (lpImcP->fdwImeMsg & MSG_CLOSE_CANDIDATE) {
            if (lpImcP->fdwImeMsg & MSG_ALREADY_OPEN) {
                if (!i) {
                    uNumMsg++;
                } else {
                    *lpdwTransBuf++ = WM_IME_NOTIFY;
                    *lpdwTransBuf++ = IMN_CLOSECANDIDATE;
                    *lpdwTransBuf++ = 0x0001;
                    lpImcP->fdwImeMsg &= ~(MSG_ALREADY_OPEN);
                }
            }
        }

        if (lpImcP->fdwImeMsg & MSG_END_COMPOSITION) {
            if (lpImcP->fdwImeMsg & MSG_ALREADY_START) {
                if (!i) {
                    uNumMsg++;
                } else {
                    *lpdwTransBuf++ = WM_IME_ENDCOMPOSITION;
                    *lpdwTransBuf++ = 0;
                    *lpdwTransBuf++ = 0;
                    lpImcP->fdwImeMsg &= ~(MSG_ALREADY_START);
                }
            }
        }
#endif

        if (lpImcP->fdwImeMsg & MSG_IMN_TOGGLE_UI) {
            if (!i) {
                uNumMsg++;
            } else {
                *lpdwTransBuf++ = WM_IME_NOTIFY;
                *lpdwTransBuf++ = IMN_PRIVATE;
                *lpdwTransBuf++ = IMN_PRIVATE_TOGGLE_UI;
            }
        }

        if (!i) {
            HIMCC hMem;

            if (!uNumMsg) {
                return (uNumMsg);
            }

            if (lpImcP->fdwImeMsg & MSG_IN_IMETOASCIIEX) {
                UINT uNumMsgLimit;

                // ++ for the start position of buffer to strore the messages
                uNumMsgLimit = *lpdwTransBuf++;

                if (uNumMsg <= uNumMsgLimit) {
                    continue;
                }
            }

            // we need to use message buffer
            if (!lpIMC->hMsgBuf) {
#if defined(DEBUG)
                _asm int 3;             // no message buffer! for debug only
#endif
                lpIMC->hMsgBuf = ImmCreateIMCC(uNumMsg * sizeof(DWORD) * 3);
                lpIMC->dwNumMsgBuf = 0;
            } else if (hMem = ImmReSizeIMCC(lpIMC->hMsgBuf,
                (lpIMC->dwNumMsgBuf + uNumMsg) * sizeof(DWORD) * 3)) {
                if (hMem != lpIMC->hMsgBuf) {
                    ImmDestroyIMCC(lpIMC->hMsgBuf);
                    lpIMC->hMsgBuf = hMem;
                }
            } else {
#if defined(DEBUG)
                _asm int 3;             // big problem
#endif
                return (0);
            }

            lpdwTransBuf = (LPDWORD)ImmLockIMCC(lpIMC->hMsgBuf);
            if (!lpdwTransBuf) {
                return (0);
            }

            lpdwTransBuf += lpIMC->dwNumMsgBuf * 3;

            bLockMsgBuf = TRUE;
        } else {
            if (bLockMsgBuf) {
                ImmUnlockIMCC(lpIMC->hMsgBuf);
            }
        }
    }

    return (uNumMsg);
}

/**********************************************************************/
/* TranslateFullChar()                                                */
/* Return Value:                                                      */
/*      the number of translated chars                                */
/**********************************************************************/
UINT PASCAL TranslateFullChar(          // convert to Double Byte Char
    LPDWORD        lpdwTransBuf,
    LPINPUTCONTEXT lpIMC,
    LPPRIVCONTEXT  lpImcP,
    WCHAR          wCharCode)
{
    LPCOMPOSITIONSTRING lpCompStr;

    lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);

    if (!lpCompStr) {
        return TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
    }

    lpCompStr->dwResultReadClauseLen = 0;
    lpCompStr->dwResultReadStrLen = 0;

    lpCompStr->dwResultStrLen = sizeof(WCHAR) / sizeof(TCHAR);
#if defined(UNICODE)
    *(LPWSTR)((LPBYTE)lpCompStr + lpCompStr->dwResultStrOffset) =
        wCharCode;
#else
    *((LPBYTE)lpCompStr + lpCompStr->dwResultStrOffset) =
        HIBYTE(wCharCode);
    *((LPBYTE)lpCompStr + lpCompStr->dwResultStrOffset + sizeof(BYTE)) =
        LOBYTE(wCharCode);
#endif
    // add a null terminator
    *(LPTSTR)((LPBYTE)lpCompStr + lpCompStr->dwResultStrOffset +
        sizeof(WCHAR)) = '\0';

    lpCompStr->dwResultClauseLen = 2 * sizeof(DWORD);
    *(LPDWORD)((LPBYTE)lpCompStr + lpCompStr->dwResultClauseOffset +
        sizeof(DWORD)) = lpCompStr->dwResultStrLen;

    ImmUnlockIMCC(lpIMC->hCompStr);

    lpImcP->fdwImeMsg |=  MSG_COMPOSITION;
    lpImcP->dwCompChar = 0;
    lpImcP->fdwGcsFlag |= GCS_RESULTREAD|GCS_RESULT;

    return TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
}

/**********************************************************************/
/* ImeToAsciiEx() / UniImeToAsciiex()                                 */
/* Return Value:                                                      */
/*      the number of translated message                              */
/**********************************************************************/
#if defined(UNIIME)
UINT WINAPI UniImeToAsciiEx(
    LPINSTDATAL  lpInstL,
    LPIMEL       lpImeL,
#else
UINT WINAPI ImeToAsciiEx(
#endif
    UINT         uVirtKey,
    UINT         uScanCode,
    CONST LPBYTE lpbKeyState,
    LPDWORD      lpdwTransBuf,
    UINT         fuState,
    HIMC         hIMC)
{
    WORD                wCharCode;
    LPINPUTCONTEXT      lpIMC;
    LPCOMPOSITIONSTRING lpCompStr;
#if !defined(ROMANIME)
    LPGUIDELINE         lpGuideLine;
#endif
    LPPRIVCONTEXT       lpImcP;
    UINT                uNumMsg;
    int                 iRet;

#ifdef UNICODE
    wCharCode = HIWORD(uVirtKey);
#else
    wCharCode = HIBYTE(uVirtKey);
#endif
    uVirtKey = LOBYTE(uVirtKey);

    if (!hIMC) {
        uNumMsg = TranslateToAscii(uVirtKey, uScanCode, lpdwTransBuf,
            0, wCharCode);
        return (uNumMsg);
    }

    lpIMC = (LPINPUTCONTEXT)ImmLockIMC(hIMC);
    if (!lpIMC) {
        uNumMsg = TranslateToAscii(uVirtKey, uScanCode, lpdwTransBuf,
            0, wCharCode);
        return (uNumMsg);
    }

    lpImcP = (LPPRIVCONTEXT)ImmLockIMCC(lpIMC->hPrivate);
    if (!lpImcP) {
        ImmUnlockIMC(hIMC);
        uNumMsg = TranslateToAscii(uVirtKey, uScanCode, lpdwTransBuf,
            0, wCharCode);
        return (uNumMsg);
    }

    // Now all composition realated information already pass to app
    // a brand new start
#if defined(ROMANIME)
    lpImcP->fdwImeMsg = MSG_IN_IMETOASCIIEX;
#else
    lpImcP->fdwImeMsg = lpImcP->fdwImeMsg & (MSG_STATIC_STATE) |
        MSG_IN_IMETOASCIIEX;
#endif

    iRet = ProcessKey(
#if defined(UNIIME)
        lpInstL, lpImeL,
#endif
        wCharCode, uVirtKey, uScanCode, lpbKeyState, lpIMC, lpImcP);

#if !defined(ROMANIME)
    if (iRet == CST_ALPHABET) {
        // A-Z convert to a-z, a-z convert to A-Z
        wCharCode ^= 0x20;

        iRet = CST_ALPHANUMERIC;
    }

    if (iRet == CST_CHOOSE) {
    } else if (iRet == CST_TOGGLE_UI) {
    } else if (lpImcP->iImeState == CST_INPUT) {
    } else if (lpImcP->iImeState == CST_CHOOSE) {
    } else {
        ClearCand(lpIMC);

        if (lpImcP->fdwImeMsg & MSG_ALREADY_OPEN) {
            lpImcP->fdwImeMsg = (lpImcP->fdwImeMsg |
                MSG_CLOSE_CANDIDATE) & ~(MSG_OPEN_CANDIDATE);
        } else {
            lpImcP->fdwImeMsg &= ~(MSG_OPEN_CANDIDATE);
        }
    }
#endif

    if (iRet == CST_ALPHANUMERIC) {
        if (lpIMC->fdwConversion & IME_CMODE_FULLSHAPE) {   // convert to DBCS
            uNumMsg = TranslateFullChar(lpdwTransBuf, lpIMC, lpImcP,
                sImeG.wFullABC[wCharCode - ' ']);
        } else {
            uNumMsg = TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);

            uNumMsg += TranslateToAscii(uVirtKey, uScanCode, lpdwTransBuf,
                uNumMsg, wCharCode);
        }
#if !defined(ROMANIME)
    } else if (iRet == CST_SYMBOL) {
        if (uVirtKey == VK_ESCAPE) {
            CandEscapeKey(lpIMC, lpImcP);

            ImmSetConversionStatus(hIMC,
                lpIMC->fdwConversion & ~(IME_CMODE_SYMBOL),
                lpIMC->fdwSentence);
        } else if (lpIMC->fdwConversion & IME_CMODE_SYMBOL) {
#if defined(WINAR30) || defined(DAYI)
#if defined(DAYI)
            if (wCharCode == '=') {
                CandEscapeKey(lpIMC, lpImcP);

                ImmSetConversionStatus(hIMC,
                    lpIMC->fdwConversion ^ (IME_CMODE_SYMBOL),
                    lpIMC->fdwSentence);
#endif
#if defined(WINAR30)
            if (wCharCode >= '0' && wCharCode <= '9') {
#endif
#if defined(DAYI)
            } else if (wCharCode >= ' ' && wCharCode <= '~') {
#endif
                CandEscapeKey(lpIMC, lpImcP);

                ImmSetConversionStatus(hIMC,
                    lpIMC->fdwConversion | IME_CMODE_SYMBOL,
                    lpIMC->fdwSentence);

                SearchSymbol(wCharCode, hIMC, lpIMC, lpImcP);
            } else {
                MessageBeep((UINT)-1);

                CandEscapeKey(lpIMC, lpImcP);

                ImmSetConversionStatus(hIMC,
                    lpIMC->fdwConversion & ~(IME_CMODE_SYMBOL),
                    lpIMC->fdwSentence);
            }
#else
            ImmSetConversionStatus(hIMC,
                lpIMC->fdwConversion & ~(IME_CMODE_SYMBOL),
                lpIMC->fdwSentence);

            if (wCharCode >= ' ' && wCharCode <= '}') {
                wCharCode = bUpper[wCharCode - ' '];

                uNumMsg = TranslateFullChar(lpdwTransBuf, lpIMC, lpImcP,
                    sImeG.wSymbol[wCharCode - ' ']);

                goto ImToAsExExit;
            }
#endif
        } else {
#if defined(WINAR30) || defined(DAYI)
            CandEscapeKey(lpIMC, lpImcP);

            ImmSetConversionStatus(hIMC,
                lpIMC->fdwConversion | IME_CMODE_SYMBOL,
                lpIMC->fdwSentence);

#if defined(WINAR30)
            SearchSymbol(wCharCode, hIMC, lpIMC, lpImcP);
#endif
#endif
        }

        uNumMsg = TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
    } else if (iRet == CST_CHOOSE) {
        LPCANDIDATEINFO lpCandInfo;

        lpCandInfo = (LPCANDIDATEINFO)ImmLockIMCC(lpIMC->hCandInfo);

        if (uVirtKey == VK_PRIOR) {
            wCharCode = CHOOSE_PREVPAGE;
        } else if (uVirtKey == VK_NEXT) {
            wCharCode = CHOOSE_NEXTPAGE;
        } else if (uVirtKey >= (VK_NUMPAD0 + (UINT)lpImeL->wCandRangeStart) &&
            uVirtKey <= VK_NUMPAD9) {
            wCharCode = uVirtKey - VK_NUMPAD0;
#if defined(DAYI)
        } else if (lpImcP->iImeState != CST_CHOOSE && uVirtKey >= '0' &&
            uVirtKey <= '9') {
            // convert shift-0 ... shift-9 to 0 ... 9
            wCharCode = uVirtKey - '0';
        } else if (uVirtKey == VK_LEFT) {
            wCharCode = CHOOSE_PREVPAGE;
        } else if (uVirtKey == VK_UP) {
            wCharCode = CHOOSE_HOME;
        } else if (uVirtKey == VK_RIGHT) {
            wCharCode = CHOOSE_NEXTPAGE;
        } else if (wCharCode < ' ') {
        } else if (wCharCode > '~') {
        } else {
            wCharCode = lpImeL->cChooseTrans[wCharCode - ' '];
#else
        } else if (uVirtKey >= ('0' + (UINT)lpImeL->wCandRangeStart) &&
            uVirtKey <= '9') {
            // convert shift-0 ... shift-9 to 0 ... 9
            wCharCode = uVirtKey - '0';
        } else if (wCharCode == '<') {
            wCharCode = CHOOSE_PREVPAGE;
        } else if (wCharCode == '?') {
            wCharCode = CHOOSE_HOME;
        } else if (wCharCode == '>') {
            wCharCode = CHOOSE_NEXTPAGE;
        } else if (wCharCode == ' ') {
            wCharCode = CHOOSE_CIRCLE;
        } else {
#endif
        }

        ChooseCand(
#if defined(UNIIME)
            lpInstL, lpImeL,
#endif
            wCharCode, hIMC, lpIMC, lpCandInfo, lpImcP);

        ImmUnlockIMCC(lpIMC->hCandInfo);

        uNumMsg = TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
    } else if (iRet == CST_INPUT) {
        lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);
        lpGuideLine = (LPGUIDELINE)ImmLockIMCC(lpIMC->hGuideLine);

        CompWord(
#if defined(UNIIME)
            lpInstL, lpImeL,
#endif
            wCharCode, hIMC, lpIMC, lpCompStr, lpGuideLine, lpImcP);

        if (lpGuideLine) {
            ImmUnlockIMCC(lpIMC->hGuideLine);
        }

        if (lpCompStr) {
            ImmUnlockIMCC(lpIMC->hCompStr);
        }

        uNumMsg = TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
#endif
#if defined(DAYI)
    } else if (iRet == CST_ROAD) {
        wCharCode = lpImeL->wSeq2CompTbl[lpImeL->wChar2SeqTbl[wCharCode - ' ']];

#ifndef UNICODE
        wCharCode = HIBYTE(wCharCode)|(LOBYTE(wCharCode) << 8);
#endif

        uNumMsg = TranslateFullChar(lpdwTransBuf, lpIMC, lpImcP, wCharCode);
#endif
    } else if (iRet == CST_RESEND_RESULT) {
        DWORD dwResultStrLen;

        lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);

        if (lpCompStr) {
            if (lpCompStr->dwResultStrLen) {
                dwResultStrLen = lpCompStr->dwResultStrLen;
            } else {
                dwResultStrLen = 0;
            }

            ImmUnlockIMCC(lpIMC->hCompStr);
        } else {
            dwResultStrLen = 0;
        }

        if (dwResultStrLen) {
            lpImcP->fdwImeMsg |=  MSG_COMPOSITION;
            lpImcP->dwCompChar = 0;
            lpImcP->fdwGcsFlag |= GCS_RESULTREAD|GCS_RESULT;
        } else {
            MessageBeep((UINT)-1);
        }

        uNumMsg = TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
    } else if (iRet == CST_PREVIOUS_COMP) {
        DWORD dwResultReadStrLen;
        TCHAR szReading[16];

        lpCompStr = (LPCOMPOSITIONSTRING)ImmLockIMCC(lpIMC->hCompStr);

        if (lpCompStr) {
            if (lpCompStr->dwResultReadStrLen) {
                dwResultReadStrLen = lpCompStr->dwResultReadStrLen;

                if (dwResultReadStrLen > lpImeL->nMaxKey * sizeof(WCHAR)
                    / sizeof(TCHAR)) {
                    dwResultReadStrLen = lpImeL->nMaxKey * sizeof(WCHAR)
                        / sizeof(TCHAR);
                }

                CopyMemory(szReading, (LPBYTE)lpCompStr +
                    lpCompStr->dwResultReadStrOffset,
                    dwResultReadStrLen * sizeof(TCHAR));

                // NULL termainator
                szReading[dwResultReadStrLen] = '\0';
            } else {
                dwResultReadStrLen = 0;
            }

            ImmUnlockIMCC(lpIMC->hCompStr);
        } else {
            dwResultReadStrLen = 0;
        }

        if (dwResultReadStrLen) {
#if defined(UNIIME)
            UniImeSetCompositionString(lpInstL, lpImeL, hIMC, SCS_SETSTR,
                NULL, 0, szReading, dwResultReadStrLen * sizeof(TCHAR));
#else
            ImeSetCompositionString(hIMC, SCS_SETSTR, NULL, 0, szReading,
                dwResultReadStrLen * sizeof(TCHAR));
#endif
        } else {
            MessageBeep((UINT)-1);
        }

        uNumMsg = TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
    } else if (iRet == CST_TOGGLE_UI) {
        lpImeL->fdwModeConfig ^= MODE_CONFIG_OFF_CARET_UI;

        SetUserSetting(
#if defined(UNIIME)
            lpImeL,
#endif
            szRegModeConfig, REG_DWORD, (LPBYTE)&lpImeL->fdwModeConfig,
            sizeof(lpImeL->fdwModeConfig));

        InitImeUIData(lpImeL);

        lpImcP->fdwImeMsg |= MSG_IMN_TOGGLE_UI;

        uNumMsg = TranslateImeMessage(lpdwTransBuf, lpIMC, lpImcP);
    } else {
        uNumMsg = TranslateToAscii(uVirtKey, uScanCode, lpdwTransBuf,
            0, wCharCode);
    }

#if !defined(ROMANIME)
#if !defined(DAYI) && !defined(WINAR30)
ImToAsExExit:
#endif
    lpImcP->fdwImeMsg &= (MSG_STATIC_STATE);
    lpImcP->fdwGcsFlag = 0;
#endif

    ImmUnlockIMCC(lpIMC->hPrivate);
    ImmUnlockIMC(hIMC);

    return (uNumMsg);
}
