/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                    COPYRIGHT 2008-2011 NUANCE COMMUNICATIONS              **
;**                                                                           **
;**               NUANCE COMMUNICATIONS PROPRIETARY INFORMATION               **
;**                                                                           **
;**     This software is supplied under the terms of a license agreement      **
;**     or non-disclosure agreement with Nuance Communications and may not    **
;**     be copied or disclosed except in accordance with the terms of that    **
;**     agreement.                                                            **
;**                                                                           **
;**                                                                           **
;*******************************************************************************
;**                                                                           **
;**     FileName: et9kjamo.c                                                  **
;**                                                                           **
;**  Description: Korean XT9 Jamo-Hangul module.                              **
;**               Conforming to the development version of Korean XT9.        **
;**                                                                           **
;*******************************************************************************
;******************************************************************************/

#include "et9kapi.h"
#include "et9ksys.h"
#include "et9kjamo.h"
#include "et9imu.h"
#include "et9misc.h"


static ET9U16 ET9LOCALCALL ToInternalJamo(ET9U16 wJamo);

static const JamoMap MEDIAL_COMBO_MAP[] = {
    {0x1169, 0x1161, 0x116a}, {0x1169, 0x1162, 0x116b}, {0x1169, 0x1175, 0x116c},
    {0x116e, 0x1165, 0x116f}, {0x116e, 0x1166, 0x1170}, {0x116e, 0x1175, 0x1171},
    {0x1173, 0x1175, 0x1174}
};
static const ET9UINT MEDIAL_COMBO_MAP_COUNT = sizeof(MEDIAL_COMBO_MAP) / sizeof(MEDIAL_COMBO_MAP[0]);

static const JamoMap CHUNJIIN_COMBO_MAP[] = {
    {0x1175, 0x119e, 0x1161}, {0x1161, 0x1175, 0x1162}, {0x1161, 0x119e, 0x1163},
    {0x1175, 0x11a2, 0x1163}, {0x1163, 0x119e, 0x1161}, {0x1163, 0x1175, 0x1164},
    {0x119e, 0x1175, 0x1165}, {0x1165, 0x1175, 0x1166}, {0x11a2, 0x1175, 0x1167},
    {0x1167, 0x1175, 0x1168}, {0x119e, 0x1173, 0x1169}, {0x116c, 0x119e, 0x116a},
    {0x116a, 0x1175, 0x116b}, {0x1169, 0x1175, 0x116c}, {0x11a2, 0x1173, 0x116d},
    {0x1173, 0x119e, 0x116e}, {0x116e, 0x1175, 0x1171}, {0x1172, 0x1175, 0x116f},
    {0x116f, 0x1175, 0x1170}, {0x116e, 0x119e, 0x1172}, {0x1173, 0x11a2, 0x1172},
    {0x1172, 0x119e, 0x116e}, {0x1173, 0x1175, 0x1174}, {0x119e, 0x119e, 0x11a2},
    {0x11a2, 0x119e, 0x119e}
};
static const ET9UINT CHUNJIIN_COMBO_MAP_COUNT = sizeof(CHUNJIIN_COMBO_MAP) / sizeof(CHUNJIIN_COMBO_MAP[0]);

static const JamoMap DOT_COMBO_MAP[] = {
    {0x119e, 0x119e, 0x11a2}, {0x11a2, 0x119e, 0x119e}
};
static const ET9UINT DOT_COMBO_MAP_COUNT = sizeof(DOT_COMBO_MAP) / sizeof(DOT_COMBO_MAP[0]);

static const JamoMap FINAL_COMBO_MAP[] = {
    {0x1100, 0x1109, 0x11aa}, {0x1102, 0x110c, 0x11ac}, {0x1102, 0x1112, 0x11ad},
    {0x1105, 0x1100, 0x11b0}, {0x1105, 0x1106, 0x11b1}, {0x1105, 0x1107, 0x11b2},
    {0x1105, 0x1109, 0x11b3}, {0x1105, 0x1110, 0x11b4}, {0x1105, 0x1111, 0x11b5},
    {0x1105, 0x1112, 0x11b6}, {0x1107, 0x1109, 0x11b9}
};
static const ET9UINT FINAL_COMBO_MAP_COUNT = sizeof(FINAL_COMBO_MAP) / sizeof(FINAL_COMBO_MAP[0]);

static const ET9SYMB FINAL_JAMO[] = {
    0x0000, 0x1100, 0x1101, 0x11aa, 0x1102, 0x11ac, 0x11ad, 0x1103, 0x1105, 0x11b0,
    0x11b1, 0x11b2, 0x11b3, 0x11b4, 0x11b5, 0x11b6, 0x1106, 0x1107, 0x11b9, 0x1109,
    0x110a, 0x110b, 0x110c, 0x110e, 0x110f, 0x1110, 0x1111, 0x1112,
};
static const ET9UINT FINAL_JAMO_COUNT = sizeof(FINAL_JAMO)/sizeof(FINAL_JAMO[0]);

static const ET9SYMB NON_FINAL_CONSONANTS[] = {
    0x1104, 0x1108, 0x110d
};
static const ET9UINT NON_FINAL_CONSONANTS_COUNT = sizeof(NON_FINAL_CONSONANTS)/sizeof(NON_FINAL_CONSONANTS[0]);

/* optimization: some decompose into themselves, thus a second jamo of 0x0000 */
static const JamoDemap MEDIAL_DECOMBO_MAP[] = { /* decompose medial index 9..20[0] (0x116A-0x1175) */
    {0x1169, 0x1161}, {0x1169, 0x1162}, {0x1169, 0x1175}, {0x116D, 0x0000},
    {0x116E, 0x0000}, {0x116E, 0x1165}, {0x116E, 0x1166}, {0x116E, 0x1175},
    {0x1172, 0x0000}, {0x1173, 0x0000}, {0x1173, 0x1175}, {0x1175, 0x0000}
};
static const ET9INT MEDIAL_DECOMBO_MAP_OFFSET = 9; /* the medial decombo table skips the first 9 medials */

/* optimization: some decompose into themselves, thus a second jamo of 0x0000 */
static const JamoDemap FINAL_DECOMBO_MAP[] = { /* decompose final index 1..27[0] (0x11A8-0x11C2) */
    {0x1100, 0x0000}, {0x1101, 0x0000}, {0x1100, 0x1109}, {0x1102, 0x0000},
    {0x1102, 0x110C}, {0x1102, 0x1112}, {0x1103, 0x0000}, {0x1105, 0x0000},
    {0x1105, 0x1100}, {0x1105, 0x1106}, {0x1105, 0x1107}, {0x1105, 0x1109},
    {0x1105, 0x1110}, {0x1105, 0x1111}, {0x1105, 0x1112}, {0x1106, 0x0000},
    {0x1107, 0x0000}, {0x1107, 0x1109}, {0x1109, 0x0000}, {0x110A, 0x0000},
    {0x110B, 0x0000}, {0x110C, 0x0000}, {0x110E, 0x0000}, {0x110F, 0x0000},
    {0x1110, 0x0000}, {0x1111, 0x0000}, {0x1112, 0x0000}
};
static const ET9INT FINAL_DECOMBO_MAP_OFFSET = 1; /* the final decombo table skips the <no final> index */

static ET9BOOL ET9LOCALCALL IsInitialJamo(ET9SYMB symb)
{
    if (ET9K_INITIAL_UNI_MIN <= symb && symb <= ET9K_INITIAL_UNI_MAX) {
        return 1;
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL IsMedialJamo(ET9SYMB symb)
{
    /* The medialMap contains one bit for each medial jamo.
     * The MSB represents the first medial jamo.
     * If the bit is (1), then the jamo is an acceptable medial jamo.
     * if the bit is (0), then it is an unacceptable medial jamo.
     */
    static const ET9U32 medialMap = 0xFF8C6800;
    if (symb >= ET9K_MEDIAL_UNI_MIN && symb <= ET9K_MEDIAL_UNI_MAX &&
        (medialMap & (0x80000000 >> (symb - ET9K_MEDIAL_UNI_MIN)))) {
        return 1;
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL IsDot(ET9SYMB symb)
{
    if (ET9K_CHUNJIIN_UNI_DOT == symb || ET9K_CHUNJIIN_UNI_DOTDOT == symb) {
        return 1;
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL IsVorH(ET9SYMB symb)
{
    if (0x1173 == symb || 0x1175 == symb) {
        return 1;
    }
    return 0;
}

ET9BOOL ET9FARCALL IsKoreanJamo(ET9SYMB symb)
{
    if (IsInitialJamo(symb) || IsMedialJamo(symb) || IsDot(symb)) {
        return 1;
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL HangulHasFinalConsonant(ET9SYMB symb)
{
    if ((ET9K_HANGUL_UNI_MIN <= symb && symb <= ET9K_HANGUL_UNI_MAX) && (symb - ET9K_HANGUL_UNI_MIN) % ET9K_FINAL_COUNT) {
        return 1;
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL IsNonFinalConsonant(ET9SYMB symb)
{
    ET9UINT i;
    for (i = 0; i < NON_FINAL_CONSONANTS_COUNT; i++) {
        if (NON_FINAL_CONSONANTS[i] == symb) {
            return 1;
        }
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL IsEqual(const ET9SimpleWord * const pWord1,
                                    const ET9SimpleWord * const pWord2)
{
    ET9UINT i;

    ET9Assert(pWord1 && pWord2);
    if (pWord1->wLen != pWord2->wLen) {
        return 0;
    }
    for (i = 0; i < pWord1->wLen; ++i) {
        if (pWord1->sString[i] != pWord2->sString[i]) {
            return 0;
        }
    }
    return 1;
}

static ET9BOOL ET9LOCALCALL IsCombo(const JamoMap * const pMap,
                                    ET9UINT               nMapSize,
                                    ET9SYMB               symb,
                                    ET9SYMB       * const pJamo1,
                                    ET9SYMB       * const pJamo2)
{
    ET9UINT i;
    for (i = 0; i < nMapSize; i++) {
        if (pMap[i].sJamoCombo == symb) {
            *pJamo1 = pMap[i].sJamo1;
            *pJamo2 = pMap[i].sJamo2;
            return 1;
        }
    }
    return 0;
}

/* Combine 2 Jamos into a JamoCombo.  Order of sJamo1 and sJamo2 need to match the table content */
static ET9SYMB ET9LOCALCALL CombineJamos(const JamoMap * const pMap,
                                         ET9UINT               nMapSize,
                                         ET9SYMB               sJamo1,
                                         ET9SYMB               sJamo2)
{
    ET9UINT i;
    for (i = 0; i < nMapSize; i++) {
        if (pMap[i].sJamo1 == sJamo1 && pMap[i].sJamo2 == sJamo2) {
            return pMap[i].sJamoCombo;
        }
    }
    return 0;
}

static void ET9LOCALCALL ClrJamoIMF(JamoIMF * const pJamoIMF)
{
    pJamoIMF->bIsMultiSymbMedial = 0;
    pJamoIMF->initialSymbCount = 0;
    pJamoIMF->medialSymbCount = 0;
    pJamoIMF->finalSymbCount = 0;
}

static ET9BOOL ET9LOCALCALL HasInitial(const JamoIMF * const pJamoIMF)
{
    if (pJamoIMF->initialSymbCount > 0) {
        return 1;
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL HasMedial(const JamoIMF * const pJamoIMF)
{
    if (pJamoIMF->medialSymbCount > 0) {
        return 1;
    }
    return 0;
}

static ET9BOOL ET9LOCALCALL HasFinal(const JamoIMF * const pJamoIMF)
{
    if (pJamoIMF->finalSymbCount > 0) {
        return 1;
    }
    return 0;
}

static void ET9LOCALCALL SetInitial(JamoIMF * const pJamoIMF,
                                    ET9SYMB         sUni,
                                    ET9U8           bSymbCount)
{
    pJamoIMF->initial = sUni;
    pJamoIMF->initialSymbCount = bSymbCount;
}

static void ET9LOCALCALL SetMedial(JamoIMF * const pJamoIMF,
                                   ET9SYMB         symb,
                                   ET9U8           bSymbType,
                                   ET9U8           bSymbCount)
{
    pJamoIMF->medial = symb;
    pJamoIMF->medialSymbCount = bSymbCount;
    if ((ET9U8)ET9MULTISYMBEXPLICIT == bSymbType) {
        pJamoIMF->bIsMultiSymbMedial = 1;
    }
}

static void ET9LOCALCALL SetFinal(JamoIMF * const pJamoIMF,
                                  ET9SYMB         sUni,
                                  ET9U8           bSymbCount)
{
    pJamoIMF->final = sUni;
    pJamoIMF->finalSymbCount = bSymbCount;
}

/* get index into final-jamo table of a given consonant Unicode */
static ET9U16 ET9LOCALCALL GetFinalIndex(ET9U16 jamoUni)
{
    ET9U16 i;
    for (i = 1; i < FINAL_JAMO_COUNT; i++) {
        if (FINAL_JAMO[i] == jamoUni) {
            return i;
        }
    }
    return 0;
}

/* return (1) if appended, else (0) */
static ET9BOOL ET9LOCALCALL AppendJamo(ET9SimpleWord  * const pJamo,
                                       ET9U8          * const pbJamoType,
                                       ET9SYMB                symb,
                                       ET9U8                  bSymbType)
{
    if (pJamo->wLen >= ET9MAXWORDSIZE) {
        return 0;
    }
    pJamo->sString[pJamo->wLen] = symb;
    pbJamoType[pJamo->wLen] = bSymbType;
    pJamo->wLen++;
    return 1;
}

/* return (1) if appended, else (0) */
static ET9BOOL ET9LOCALCALL AppendHangul(ET9KHangulWord * const pHangulWord,
                                         ET9SYMB                sHangulUni,
                                         ET9U8                  bSymbCount)
{
    if (pHangulWord->wLen >= ET9MAXWORDSIZE) {
        return 0;
    }
    pHangulWord->sString[pHangulWord->wLen] = sHangulUni;
    pHangulWord->pSymbCounts[pHangulWord->wLen] = bSymbCount;
    pHangulWord->wLen++;
    return 1;
}

/* Return (1) if the compact operation succeeded, else (0).
   Returns symbol count @pbSymbolCount */
static ET9BOOL ET9LOCALCALL DecomposeToCJI(ET9SYMB                symb,
                                           ET9SimpleWord  * const pCompactJamo,
                                           ET9U8          * const pbCompactJamoType,
                                           ET9U8                  *pbSymbolCount)
{
    ET9SYMB pReversedCJI[5], sJamo1, sJamo2;
    ET9UINT bufLen = 0;
    ET9BOOL bResult;
    /* since the decompose process is in reverse order, need to store the CJI parts in a buffer first */
    while (bufLen < 5 && IsCombo(CHUNJIIN_COMBO_MAP, CHUNJIIN_COMBO_MAP_COUNT, symb, &sJamo1, &sJamo2)) {
        if (ET9K_CHUNJIIN_UNI_DOTDOT == symb) { /* double dot: add a single dot to the reversed buffer */
            pReversedCJI[bufLen++] = ET9K_CHUNJIIN_UNI_DOT;
            symb = ET9K_CHUNJIIN_UNI_DOT;
            break;
        }
        else if (ET9K_CHUNJIIN_UNI_DOT == symb) {
            break;
        }
        else {
            pReversedCJI[bufLen++] = sJamo2;
            symb = sJamo1;
        }
    }
    /* now append the stored CJI parts into Jamo sequence in the correct order */
    bResult = AppendJamo(pCompactJamo, pbCompactJamoType, symb, (ET9U8)ET9EXPLICITSYM);
    *pbSymbolCount = (ET9U8)(bufLen + 1); /* reversed buffer length + symb */
    for (; bufLen > 0; --bufLen) {
        bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, pReversedCJI[bufLen - 1], (ET9U8)ET9EXPLICITSYM);
    }
    return bResult;
}

/* Return (1) if the compact operation succeeded, else (0) */
static ET9BOOL ET9LOCALCALL CompactJamo(JamoIMF        * const pIMF,
                                        ET9SimpleWord  * const pCompactJamo,
                                        ET9U8          * const pbCompactJamoType,
                                        ET9BOOL                bJoinCJI)
{
    ET9SYMB sJamo1, sJamo2;
    ET9BOOL bResult = 1;
    /* must find compact form of Initial, Medial, and Final in proper order */
    if (HasInitial(pIMF)) {
        ET9Assert(pIMF->initialSymbCount == 1);
        bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, pIMF->initial, (ET9U8)ET9EXPLICITSYM);
    }
    if (HasMedial(pIMF)) {
        ET9U8 bSymbType = (ET9U8)ET9EXPLICITSYM;
        if (pIMF->bIsMultiSymbMedial) {
            bSymbType = (ET9U8)ET9MULTISYMBEXPLICIT;
        }
        if (bJoinCJI) { /* setting is join CJI, compact medial into 1 or 2 Jamos */
            if (IsDot(pIMF->medial)) {
                bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, pIMF->medial, (ET9U8)ET9EXPLICITSYM);
                pIMF->medialSymbCount = 1;
            }
            else if (IsCombo(MEDIAL_COMBO_MAP, MEDIAL_COMBO_MAP_COUNT, pIMF->medial, &sJamo1, &sJamo2)) {
                /* combo medial */
                bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, sJamo1, (ET9U8)(IsVorH(sJamo1) ? ET9EXPLICITSYM : bSymbType));
                bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, sJamo2, (ET9U8)(IsVorH(sJamo2) ? ET9EXPLICITSYM : bSymbType));
                pIMF->medialSymbCount = 2;
            }
            else { /* single medial */
                bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, pIMF->medial, bSymbType);
                pIMF->medialSymbCount = 1;
            }
        }
        else { /* setting is NOT join CJI */
            if (pIMF->bIsMultiSymbMedial) { /* decompose medial into CJI parts, no change in medial symb count */
                bResult = bResult && DecomposeToCJI(pIMF->medial, pCompactJamo, pbCompactJamoType, &pIMF->medialSymbCount);
            }
            else if (IsCombo(MEDIAL_COMBO_MAP, MEDIAL_COMBO_MAP_COUNT, pIMF->medial, &sJamo1, &sJamo2)) {
                /* combo medial */
                bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, sJamo1, (ET9U8)(IsVorH(sJamo1) ? ET9EXPLICITSYM : bSymbType));
                bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, sJamo2, (ET9U8)(IsVorH(sJamo2) ? ET9EXPLICITSYM : bSymbType));
                pIMF->medialSymbCount = 2;
            }
            else { /* simple medial */
                bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, pIMF->medial, bSymbType);
                pIMF->medialSymbCount = 1;
            }
        }
    }
    if (HasFinal(pIMF)) {
        if (IsCombo(FINAL_COMBO_MAP, FINAL_COMBO_MAP_COUNT, pIMF->final, &sJamo1, &sJamo2)) {
            /* combo final */
            ET9Assert(pIMF->finalSymbCount == 2);
            bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, sJamo1, (ET9U8)ET9EXPLICITSYM);
            bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, sJamo2, (ET9U8)ET9EXPLICITSYM);
        }
        else { /* simple final */
            ET9Assert(pIMF->finalSymbCount == 1);
            bResult = bResult && AppendJamo(pCompactJamo, pbCompactJamoType, pIMF->final, (ET9U8)ET9EXPLICITSYM);
        }
    }
    return bResult;
}

/* find compact Jamo from IMF,
   combine Jamos in the IMF struct,
   append the results into the Hangul word struct.
   Return (1) if the compact operation succeeded, else (0) */
static ET9BOOL ET9LOCALCALL CommitJamoIMF(JamoIMF        * const pIMF,
                                          ET9SimpleWord  * const pCompactJamo,
                                          ET9U8          * const pbCompactJamoType,
                                          ET9KHangulWord * const pHangul,
                                          ET9BOOL                bJoinCJI)
{
    ET9BOOL bResult;
    /* Compact Jamo as needed and change symb count to compact form */
    bResult = CompactJamo(pIMF, pCompactJamo, pbCompactJamoType, bJoinCJI);

    if (HasInitial(pIMF)) {
        if (HasMedial(pIMF)) {
            if (IsDot(pIMF->medial)) {
                /* I,.,_: add I and "." into Hangul word individually
                   never has I,.,F */
                AppendHangul(pHangul, pIMF->initial, pIMF->initialSymbCount);
                AppendHangul(pHangul, pIMF->medial, pIMF->medialSymbCount);
                ET9Assert(!HasFinal(pIMF));
            }
            else {
                /* I,M,_ or I,M,F: can be combined into 1 Hangul */
                ET9U16 initialIndex, medialIndex, finalIndex;
                ET9SYMB sHangulUni;
                ET9U8 bSymbCount;
                initialIndex = pIMF->initial - ET9K_INITIAL_UNI_MIN;
                medialIndex = pIMF->medial - ET9K_MEDIAL_UNI_MIN;
                if (HasFinal(pIMF)) {
                    /* I,M,F */
                    finalIndex = GetFinalIndex(pIMF->final);
                }
                else {
                    /* I,M,_ */
                    finalIndex = 0;
                }
                sHangulUni = (ET9SYMB)(initialIndex * ET9K_MEDIAL_X_FINAL_COUNT
                                      + medialIndex * ET9K_FINAL_COUNT
                                      + finalIndex
                                      + ET9K_HANGUL_UNI_MIN);
                bSymbCount = (ET9U8)(pIMF->initialSymbCount
                                     + pIMF->medialSymbCount
                                     + pIMF->finalSymbCount);
                AppendHangul(pHangul, sHangulUni, bSymbCount);
            }
        }
        else {
            /* I,_,_: add I into Hangul word individually
               never has I,_,F */
            AppendHangul(pHangul, pIMF->initial, pIMF->initialSymbCount);
            ET9Assert(!HasFinal(pIMF));
        }
    }
    else {
        if (HasMedial(pIMF)) {
            /* _,M,_: add M into Hangul word individually
               never has _,M,F */
            AppendHangul(pHangul, pIMF->medial, pIMF->medialSymbCount);
            ET9Assert(!HasFinal(pIMF));
        }
        else {
            if (HasFinal(pIMF)) {
                /* _,_,F */
                /* cannot combine, add F into Hangul word individually */
                AppendHangul(pHangul, pIMF->final, pIMF->finalSymbCount);
            }
            else {
                /* _,_,_ */
                /* empty IMF, do nothing */
            }
        }
    }
    return bResult;
}

static ET9INT ET9LOCALCALL UpdateJamoIMF(JamoIMF        * const pIMF,
                                         ET9SYMB                symb,
                                         ET9U8                  bSymbType,
                                         ET9SYMB                nextSymb)
{
    ET9SYMB sJamoCombo;

    ET9Assert(IsKoreanJamo(symb));
    /* current symbol is a consonant */
    if (IsInitialJamo(symb)) {
        /* _,_,F or I,M,F */
        if (HasFinal(pIMF)) {
            sJamoCombo = CombineJamos(FINAL_COMBO_MAP, FINAL_COMBO_MAP_COUNT, pIMF->final, symb);
            /* F+C can combine */
            if (sJamoCombo) {
                /* F = F+C */
                SetFinal(pIMF, sJamoCombo, (ET9U8)(pIMF->finalSymbCount + 1));
            }
            else { /* F and C cannot combine, no IMF udpate */
                return 0;
            }
        }
        /* x,M,_ */
        else if (HasMedial(pIMF)) {
            /* _,M,_ OR this consonant cannot be a final OR this medial is a Chun-Ji-In dot; no IMF update */
            if (!HasInitial(pIMF) || IsNonFinalConsonant(symb) || IsDot(pIMF->medial)) {
                return 0;
            }
            else { /* I,M,_ + C --> I,M,F */
                SetFinal(pIMF, symb, 1);
            }
        }
        /* I,_,_ */
        else if (HasInitial(pIMF)) {
            sJamoCombo = CombineJamos(FINAL_COMBO_MAP, FINAL_COMBO_MAP_COUNT, pIMF->initial, symb);
            /* I+C can combine, it becomes the final */
            if (sJamoCombo) {
                /* clrIMF, F = I+C --> _,_,F */
                ClrJamoIMF(pIMF);
                SetFinal(pIMF, sJamoCombo, 1 + 1); /* initial symb count always == 1 */
            }
            else { /* I+C cannot combine --> I,_,_ no IMF update */
                return 0;
            }
        }
        /* _,_,_ (empty IMF) */
        else {
            /* I = C --> I,_,_*/
            SetInitial(pIMF, symb, 1);
        }
    }
    /* current symbol is a vowel or a Chun-Ji-In dot */
    else {
        ET9Assert(IsMedialJamo(symb) || IsDot(symb));
        /* x,x,F */
        if (HasFinal(pIMF)) {
            if (IsMedialJamo(symb) || IsVorH(nextSymb)) {
                /* There is/will be a medial after F: F will be changed */
                ET9SYMB sC1, sC2;
                /* if F = C1 + C2 */
                if (IsCombo(FINAL_COMBO_MAP, FINAL_COMBO_MAP_COUNT, pIMF->final, &sC1, &sC2)) {
                    /* F = C1, caller will commitIMF, clrIMF, and back-track to C2, I = C2, M = V; Note: C1, C2 always have 1 symb each */
                    SetFinal(pIMF, sC1, 1);
                    return -1;
                }
                else {
                    /* F is not combo: F will become I of next IMF */
                    SetFinal(pIMF, 0, 0);
                    return -1;
                }
            }
            else { /* cannot be a medial after F: F will not change. */
                return 0;
            }
        }
        /* x,M,_ */
        else if (HasMedial(pIMF)) {
            /* try medial combo */
            sJamoCombo = CombineJamos(MEDIAL_COMBO_MAP, MEDIAL_COMBO_MAP_COUNT, pIMF->medial, symb);
            if (0 == sJamoCombo) {
                /* not medial combo, try ChunJiIn combo */
                sJamoCombo = CombineJamos(CHUNJIIN_COMBO_MAP, CHUNJIIN_COMBO_MAP_COUNT, pIMF->medial, symb);
                if (sJamoCombo) {
                    if (IsDot(symb) || IsDot(pIMF->medial) || (ET9U8)ET9MULTISYMBEXPLICIT == bSymbType) {
                        pIMF->bIsMultiSymbMedial = 1;
                    }
                    else if (!pIMF->bIsMultiSymbMedial) {
                        /* reject combos if not formed by CJI so that explicit inputs will not combine by CJI rules */
                        return 0;
                    }
                }
                else { /* M+V cannot combine, no IMF update */
                    return 0;
                }
            }
            /* M = M+V */
            ET9Assert(sJamoCombo);
            SetMedial(pIMF, sJamoCombo, (ET9U8)ET9EXPLICITSYM, (ET9U8)(pIMF->medialSymbCount + 1));
        }
        /* I,_,_ or _,_,_ (empty IMF) */
        else {
            /* M = V */
            SetMedial(pIMF, symb, bSymbType, 1);
        }
    }
    /* updated IMF, consumed 1 symbol */
    return 1;
}

static void ET9LOCALCALL FixCompleteLenByDot(ET9KHangulWord * const pHangul)
{
    ET9SYMB sLastSymb, s2ndLastSymb;
    /* When the last char is dot or double-dot,
       the 2nd last char may still change if it is a consonant or it has/is a final Jamo.
       So, should exclude the 2nd last char from the complete length. */
    if (pHangul->wLen >= 2 && pHangul->wComplete + 2 > pHangul->wLen) {

        sLastSymb = pHangul->sString[pHangul->wLen - 1];
        s2ndLastSymb = pHangul->sString[pHangul->wLen - 2];

        if (IsDot(sLastSymb)) {
            if (IsInitialJamo(s2ndLastSymb) ||
                HangulHasFinalConsonant(s2ndLastSymb) ||
                GetFinalIndex(s2ndLastSymb)) {
                /* last is dot/double-dot, 2nd last is a consonant or it has/is a final Jamo,
                   exclude the 2nd last char from the complete length */
                pHangul->wComplete--;
            }
        }
    }
}

/* Return (1) if the compact operation succeeded, else (0) */
ET9BOOL ET9FARCALL EncodeJamo( const ET9SYMB * pJamoStr,
                               ET9U16          wJamoStrLen,
                               const ET9U8   * pbJamoType,
                               ET9SimpleWord * pOutJamo,
                               ET9U8         * pbOutJamoType,
                               ET9KHangulWord* pHangul,
                               ET9BOOL         bJoinCJI)
{
    ET9UINT i;
    ET9INT iCounsumedCount;
    JamoIMF jamoIMF;
    ET9SYMB currentJamo, nextJamo;
    ET9U8 currentJamoType;
    ET9BOOL bResult = 1;

    /* init the Hangul word */
    pHangul->wLen = 0;
    pHangul->wComplete = 0;
    /* init jamo IMF struct */
    ClrJamoIMF(&jamoIMF);

    /* loop through the Jamo string */
    for (i = 0; i < wJamoStrLen; i += iCounsumedCount) {
        currentJamo = ToInternalJamo(pJamoStr[i]);
        currentJamoType = pbJamoType[i];
        if (IsKoreanJamo(currentJamo)) {
            ET9UINT j;
            nextJamo = 0;
            /* skip consecutive dots to the next Jamo */
            for (j = i + 1; j < wJamoStrLen; j++) {
                nextJamo = ToInternalJamo(pJamoStr[j]);
                if (!IsDot(nextJamo)) {
                    break;
                }
            }
            if (j == wJamoStrLen) {
                /* dots through the end, no next Jamo */
                nextJamo = 0;
            }
            iCounsumedCount = UpdateJamoIMF(&jamoIMF, currentJamo, currentJamoType, nextJamo);
            if (iCounsumedCount < 1) {
                bResult = bResult && CommitJamoIMF(&jamoIMF, pOutJamo, pbOutJamoType, pHangul, bJoinCJI);
                ClrJamoIMF(&jamoIMF);
            }
        }
        else {
            /* non-Korean symb cannot combine into IMF,
               accept the current IMF and then append the non-Korean symbol */
            bResult = bResult && CommitJamoIMF(&jamoIMF, pOutJamo, pbOutJamoType, pHangul, bJoinCJI);
            ClrJamoIMF(&jamoIMF);
            bResult = bResult && AppendJamo(pOutJamo, pbOutJamoType, currentJamo, (ET9U8)ET9EXPLICITSYM);
            AppendHangul(pHangul, currentJamo, 1);
            iCounsumedCount = 1;
        }
    }
    /* complete length excludes the last IMF content */
    pHangul->wComplete = pHangul->wLen;
    /* commit the remaining content of IMF into Hangul word */
    bResult = bResult && CommitJamoIMF(&jamoIMF, pOutJamo, pbOutJamoType, pHangul, bJoinCJI);
    FixCompleteLenByDot(pHangul);
    return bResult;
}

/* Fix Hangul complete length to exclude any part that may change during multitap */
static void ET9LOCALCALL FixCompleteLenInMT(const ET9SimpleWord * const pJamo,
                                            const ET9U8         * const pbJamoType,
                                            ET9KHangulWord      * const pHangul,
                                            const ET9SYMB       * const pMTSymb,
                                            ET9UINT                     nMTSymbCount)
{
    ET9UINT i;
    ET9SYMB sSelectedSymb;
    ET9U16 wMinCompleteLen;
    ET9KHangulWord sMultitapHangul;
    ET9SimpleWord sTmpJamo, sTmpCompactJamo; /* dummy place holder */
    ET9U8 pbTmpCompactJamoType[ET9MAXWORDSIZE]; /* dummy place holder */

    sTmpJamo = *pJamo;
    wMinCompleteLen = pHangul->wComplete;
    sSelectedSymb = sTmpJamo.sString[sTmpJamo.wLen - 1];
    /* try each symbol in MT seq to get the minimum complete length */
    for(i = 0; i < nMTSymbCount; i++) {
        ET9U16 w, wLen;
        if (pMTSymb[i] == sSelectedSymb) {
            /* skip the selected symbol since it has already been evaluated */
            continue;
        }
        /* replace the last symbol with the current symbol in the multitap sequence */
        sTmpJamo.sString[sTmpJamo.wLen - 1] = pMTSymb[i];
        /* encoding the hangul with the current multitap symbol */
        /* dummy place holder for CompactJamo, CompactJamoType, and JoinCJI flag since they do not affect Hangul complete length */
        sTmpCompactJamo.wLen = 0;
        EncodeJamo(sTmpJamo.sString, sTmpJamo.wLen, pbJamoType, &sTmpCompactJamo, pbTmpCompactJamoType, &sMultitapHangul, /* bJoinCJI */1);

        /* find the minimum complete length among all trials */
        wLen = __ET9Min(wMinCompleteLen, sMultitapHangul.wLen);
        for ( w = 0; w < wLen; w++ )
        {
            if ( sMultitapHangul.sString[w] != pHangul->sString[w] )
                break;
        }
        if ( w < wMinCompleteLen )
            wMinCompleteLen = w;
    }
    /* use the minimum complete length to exclude any portion that may still change */
    pHangul->wComplete = wMinCompleteLen;
    ET9Assert(pHangul->wLen > 0);
    if (pHangul->wComplete == pHangul->wLen) {
        /* last Hangul char is non-Korean, but should exclude it from complete length during multitap */
        pHangul->wComplete--;
    }
}

/** Build Hangul from the content of Word Symbol Info.
 *
 * @param pKLingInfo            pointer to Korean linguistic module.
 * @param pHangul               for output of the Hangul string.
 * @param pMTSequence           array of symbs in an active mulitap sequence (NULL if not used).
 * @param wMTSymbCount          number of symbs in the multitap sequence (0 if not used).
 *
 * @return ET9STATUS_NONE           Succeeded
 * @return ET9STATUS_BAD_PARAM      one of the parameters is invalid
 * @return ET9STATUS_NO_INIT        system not properly initialized
 * @return ET9STATUS_INVALID_INPUT  system cannot interpret input
 * @return ET9STATUS_EMPTY          input buffer is empty
 */
ET9STATUS ET9FARCALL ET9KBuildHangul(ET9KLingInfo       * pKLingInfo,
                                     ET9KHangulWord     * pHangul,
                                     const ET9SYMB      * pMTSequence,
                                     ET9U16               wMTSymbCount)
{
    ET9WordSymbInfo *pWSI;
    ET9SimpleWord sJamo;
    ET9SimpleWord sCompactJamo;
    ET9U8 pbJamoType[ET9MAXWORDSIZE], pbCompactJamoType[ET9MAXWORDSIZE], i;
    ET9BOOL bCompactOkay;

    ET9_K_CHECK_LINGINFO(pKLingInfo);
    /* validate inputs */
    if (NULL == pHangul) {
        return ET9STATUS_BAD_PARAM;
    }

    if (NULL == pMTSequence && wMTSymbCount > 0) {
        return ET9STATUS_BAD_PARAM;
    }
    pWSI = pKLingInfo->Base.pWordSymbInfo;

    ET9GetExactWord(pWSI, &sJamo, NULL, NULL);
    if(0 == sJamo.wLen) {
        /* no input */
        return ET9STATUS_EMPTY;
    }
    sCompactJamo.wLen = 0;
    /* get input type of each Jamo */
    for (i = 0; i < sJamo.wLen; i++) {
        pbJamoType[i] = pWSI->SymbsInfo[i].eInputType;
        if ( pWSI->SymbsInfo[i].eInputType == ET9MULTITAPKEY )
        {
            sJamo.sString[i] = ET9K_TO_LOWER_JAMO(sJamo.sString[i]);
        }
    }

    /* encode the original Jamo into Hangul and get the compact Jamo */
    bCompactOkay = EncodeJamo(sJamo.sString, sJamo.wLen, pbJamoType, &sCompactJamo, pbCompactJamoType, pHangul, pKLingInfo->bJoinCJI);

    if (pMTSequence && wMTSymbCount > 0) {
        /* multitap sequence is given: adjust Hangul complete length to exclude any part that may change during multitap */
        FixCompleteLenInMT(&sJamo, pbJamoType, pHangul, pMTSequence, wMTSymbCount);
    }

    if (bCompactOkay && !IsEqual(&sJamo, &sCompactJamo) /* && sCompactJamo.wLen <= sJamo.wLen */) {
        /* compact Jamo is different from original, replace WSI with the compact Jamo sequence */
        sCompactJamo.wCompLen = 0;
        _ET9ExplicifyWord(pWSI, &sCompactJamo);
        /* update input type in WSI */
        for (i = 0; i < sCompactJamo.wLen; i++) {
            pWSI->SymbsInfo[i].eInputType = pbCompactJamoType[i];
        }
    }
    
    if ( ET9KOutputCompatibilityJamo(pKLingInfo) )
        ET9KJamoToCompatibilityJamo(pHangul->sString, pHangul->wLen);
    return ET9STATUS_NONE;
}

static ET9U16 ET9LOCALCALL ToJamoString(ET9INT nInitialIndex, ET9INT nMedialIndex, ET9INT nFinalIndex, ET9SYMB * pString)
{
    const JamoDemap *pDemap;
    ET9U16 wLen = 0;
    if (nInitialIndex >= 0)
        pString[wLen++] = (ET9SYMB)(ET9K_INITIAL_UNI_MIN + nInitialIndex);
    if ( nMedialIndex >= 0 )
    {
        if (nMedialIndex < MEDIAL_DECOMBO_MAP_OFFSET) {
            pString[wLen++] = (ET9SYMB)(ET9K_MEDIAL_UNI_MIN + nMedialIndex);
        } else {
            pDemap = MEDIAL_DECOMBO_MAP + (nMedialIndex - MEDIAL_DECOMBO_MAP_OFFSET);
            pString[wLen++] = pDemap->sJamo1;
            if (pDemap->sJamo2) {
                pString[wLen++] = pDemap->sJamo2;
            }
        }
    }
    if ( nFinalIndex > 0 ) 
    {
        pDemap = FINAL_DECOMBO_MAP + (nFinalIndex - FINAL_DECOMBO_MAP_OFFSET);
        pString[wLen++] = pDemap->sJamo1;
        if (pDemap->sJamo2) {
            pString[wLen++] = pDemap->sJamo2;
        }
    }
    return wLen;
}

ET9STATUS ET9FARCALL ET9_K_Hangul2Jamo(const ET9SYMB * pHangulStr, ET9U16 wHangulStrLen, ET9SimpleWord * pJamo, ET9BOOL bLeadingUpper)
{
    ET9UINT i;
    ET9UINT j;
    ET9INT nInitial;
    ET9INT nMedial;
    ET9INT nFinal;
    ET9SYMB sHangul;
    ET9SYMB sString[5];
    ET9U16 wLen;

    /* validate param */
    if (NULL == pHangulStr || NULL == pJamo) {
        return ET9STATUS_BAD_PARAM;
    }

    /* init Jamo output structure */
    pJamo->wLen = 0;

    /* convert each given Hangul into Jamos */
    for (i = 0; i < wHangulStrLen; i++) {
        sHangul = pHangulStr[i];
        sHangul = ToInternalJamo(sHangul);
        if ( ET9K_MEDIAL_UNI_MIN <= sHangul && sHangul <= ET9K_MEDIAL_UNI_MAX )
        {
            nMedial = (ET9INT)(sHangul - ET9K_MEDIAL_UNI_MIN);
            wLen = ToJamoString( -1, nMedial, -1, sString );
        }
        else if ( ET9K_FINAL_UNI_MIN <= sHangul && sHangul <= ET9K_FINAL_UNI_MAX )
        {
            nFinal = (ET9INT)(sHangul - ET9K_FINAL_UNI_MIN);
            wLen = ToJamoString( -1, -1, nFinal, sString );
        }
        else if ( sHangul < ET9K_HANGUL_UNI_MIN || sHangul > ET9K_HANGUL_UNI_MAX ) 
        {  /* non-Hangul symbol is added to Jamo string directly */
            sString[0] = sHangul;
            wLen = 1;
        }   /* END OF non-Hangul symbol */
        else /* ET9K_HANGUL_UNI_MIN <= sHangul && sHangul <= ET9K_HANGUL_UNI_MAX */
        {
            /* convert Hangul symbol into index */
            sHangul = sHangul - ET9K_HANGUL_UNI_MIN;

            /* Compute the index of the initial, medial and final jamo */
            nInitial = (ET9INT)(sHangul / ET9K_MEDIAL_X_FINAL_COUNT);
            nMedial = (ET9INT)((sHangul - (nInitial * ET9K_MEDIAL_X_FINAL_COUNT)) / ET9K_FINAL_COUNT);
            nFinal = (ET9INT)(sHangul % ET9K_FINAL_COUNT);

            wLen = ToJamoString( nInitial, nMedial, nFinal, sString );
        } /* END OF convert Hangul to Jamo */

        if ( wLen > 0 )
        {
            if (pJamo->wLen + wLen > ET9MAXWORDSIZE) {
                return ET9STATUS_FULL;
            }

            pJamo->sString[pJamo->wLen++] = bLeadingUpper? ET9K_TO_UPPER_JAMO(sString[0]): sString[0];
            
            for ( j = 1; j < wLen; j++ ) {
                pJamo->sString[pJamo->wLen++] = sString[j];
            }
        }
    }
    /* finished decoding the whole Hangul string successfully */

    return ET9STATUS_NONE;
}

/** Decode Hangul sequence into Jamos.
 *
 * @param pKLingInfo            Pointer to korean information structure.
 *
 * @param pHangul               Hangul sequence.
 *                              wComplete and pSymbCounts are ignored.
 * @param pJamo                 for output of the Jamo string.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT        system not properly initialized
 * @return ET9STATUS_BAD_PARAM  one of the parameters is invalid
 */
ET9STATUS ET9FARCALL ET9KDecodeHangul(ET9KLingInfo         * pKLingInfo,
                                      const ET9KHangulWord * pHangul,
                                      ET9SimpleWord        * pJamo)
{
    ET9STATUS status;

    ET9_K_CHECK_LINGINFO(pKLingInfo);
    if ( pHangul == NULL || pJamo == NULL )
        return ET9STATUS_BAD_PARAM;
    
    status = ET9_K_Hangul2Jamo(pHangul->sString, pHangul->wLen, pJamo, /*bLeadingUpper*/0);

    if (status == ET9STATUS_NONE && ET9KOutputCompatibilityJamo(pKLingInfo) )
    {
        ET9KJamoToCompatibilityJamo(pJamo->sString, pJamo->wLen);
    }

    return status;
}

ET9STATUS ET9FARCALL ET9_K_Jamo2Hangul(const ET9SYMB * pJamoStr, ET9U16 wJamoStrLen, const ET9U8 * pbJamoType, ET9KHangulWord * pHangul)
{
    ET9SimpleWord sCompactJamo;
    ET9U8 pbCompactJamoType[ET9MAXWORDSIZE];
    ET9BOOL bCompactOkay;
    ET9U16 i;

    sCompactJamo.wLen = 0;
    bCompactOkay = EncodeJamo(pJamoStr, wJamoStrLen, pbJamoType, &sCompactJamo, pbCompactJamoType, pHangul, /* bJoinCJI */0);
    
    for ( i = 0; i < pHangul->wLen; i++ )
    {
        pHangul->sString[i] = pHangul->sString[i];
    }
    return ET9STATUS_NONE;
}


/* 0x3131 to 0x318E  compatibility jamo  */
static ET9U16 g_acInternalJamo[] =
    { 0x1100, 0x1101, 0x11AA, 0x1102, 0x11AC, 0x11AD, 0x1103, 0x1104, 0x1105, 0x11B0,
      0x11B1, 0x11B2, 0x11B3, 0x11B4, 0x11B5, 0x11B6, 0x1106, 0x1107, 0x1108, 0x11B9,
/*                                            0x111A,                         0x1121, */
      0x1109, 0x110A, 0x110B, 0x110C, 0x110D, 0x110E, 0x110F, 0x1110, 0x1111, 0x1112,
      0x1161, 0x1162, 0x1163, 0x1164, 0x1165, 0x1166, 0x1167, 0x1168, 0x1169, 0x116A,
      0x116B, 0x116C, 0x116D, 0x116E, 0x116F, 0x1170, 0x1171, 0x1172, 0x1173, 0x1174,
      0x1175, 0x1160, 0x1114, 0x1115, 0x11C7, 0x11C8, 0x11CC, 0x11CE, 0x11D3, 0x11D7,
      0x11D9, 0x111C, 0x11DD, 0x11DF, 0x111D, 0x111E, 0x1120, 0x1122, 0x1123, 0x1127,
      0x1129, 0x112B, 0x112C, 0x112D, 0x112E, 0x112F, 0x1132, 0x1136, 0x1140, 0x1147,
      0x114C, 0x11F1, 0x11F2, 0x1157, 0x1158, 0x1159, 0x1184, 0x1185, 0x1188, 0x1191,
      0x1192, 0x1194, 0x119E, 0x11A1 };

/* [0x1100 - 0x1112]   --- 19  */
static ET9U16 acInitialToCompatibility[] = 
    {0x3131, 0x3132, 0x3134, 0x3137, 0x3138, 0x3139, 0x3141, 0x3142, 0x3143, 0x3145,
     0x3146, 0x3147, 0x3148, 0x3149, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E};
/* [0x1161 - 0x1175]   --- 21  */
static ET9U16 acMedialToCompatibility[]  = 
    {0x314F, 0x3150, 0x3151, 0x3152, 0x3153, 0x3154, 0x3155, 0x3156, 0x3157, 0x3158, 
     0x3159, 0x315A, 0x315B, 0x315C, 0x315D, 0x315E, 0x315F, 0x3160, 0x3161, 0x3162, 
     0x3163};
/* [0x11A8 - 0x11C2]  not include "no jamo"   --- 27  */
static ET9U16 acFinalToCompatibility[]   = 
    {0x3131, 0x3132, 0x3133, 0x3134, 0x3135, 0x3136, 0x3137, 0x3139, 0x313A, 0x313B,
     0x313C, 0x313D, 0x313E, 0x313F, 0x3140, 0x3141, 0x3142, 0x3144, 0x3145, 0x3146,
     0x3147, 0x3148, 0x314A, 0x314B, 0x314C, 0x314D, 0x314E};

static ET9U16 ET9LOCALCALL ToCompatibilityJamo(ET9U16 wJamo)
{
    if ( 0x1100 <= wJamo && wJamo <= 0x1112 )
        return acInitialToCompatibility[wJamo - 0x1100];
    if ( 0x1161 <= wJamo && wJamo <= 0x1175 )
        return acMedialToCompatibility[wJamo - 0x1161];
    if ( 0x11A8 <= wJamo && wJamo <= 0x11C2 )
        return acFinalToCompatibility[wJamo - 0x11A8];
    if ( 0x119E == wJamo )  /* ET9K_CHUNJIIN_UNI_DOT */
        return 0x318D;
    if ( 0x11A2 == wJamo )  /* ET9K_CHUNJIIN_UNI_DOTDOT */
        return 0x2025;

    return wJamo;
}

static ET9U16 ET9LOCALCALL ToInternalJamo(ET9U16 wJamo)
{
    if ( 0x3131 <= wJamo && wJamo <= 0x318E )
        return g_acInternalJamo[wJamo - 0x3131];
    /* ET9K_CHUNJIIN_UNI_DOT  0x318D  included in above table 
    if ( 0x318D == wJamo )
        return 0x119E;  
    */
    if ( 0x2025 == wJamo )  /* ET9K_CHUNJIIN_UNI_DOTDOT */
        return 0x11A2;
    return wJamo;
}


/** Convert Jamo string to Compatibility Jamo string
 *
 * @param pwJamo                Input string and output string.
 * @param wLen                  Input string length.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_BAD_PARAM  pwJamo is NULL
 */
ET9STATUS ET9FARCALL ET9KJamoToCompatibilityJamo(ET9U16* pwJamo, ET9U16 wLen)
{
    ET9U16 w;

    if ( pwJamo == NULL )
        return ET9STATUS_BAD_PARAM;
    for ( w = 0; w < wLen; w++ )
    {
        pwJamo[w] = ToCompatibilityJamo( ET9K_TO_LOWER_JAMO(pwJamo[w]) );
    }
    return ET9STATUS_NONE;
}

/** Convert Compatibility Jamo string to Jamo string
 *
 * @param pwJamo                Input string and output string.
 * @param wLen                  Input string length.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_BAD_PARAM  pwJamo is NULL
 */
ET9STATUS ET9FARCALL ET9KCompatibilityJamoToJamo(ET9U16* pwJamo, ET9U16 wLen)
{
    ET9U16 w;

    if ( pwJamo == NULL )
        return ET9STATUS_BAD_PARAM;
    for ( w = 0; w < wLen; w++ )
    {
        pwJamo[w] = ToInternalJamo(pwJamo[w]);
    }
    return ET9STATUS_NONE;
}

/* ----------------------------------< eof >--------------------------------- */
