/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                    COPYRIGHT 2008-2012 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(const 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 */




/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                              
 *
 *            
 *
 *                                       
 */
static ET9BOOL ET9LOCALCALL __IsNonFinalConsonant(const ET9SYMB symb)
{
    ET9UINT i;
    for (i = 0; i < NON_FINAL_CONSONANTS_COUNT; i++) {
        if (NON_FINAL_CONSONANTS[i] == symb) {
            return 1;
        }
    }
    return 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *               
 *
 *              
 *              
 *
 *                                     
 */
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;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                              
 *
 *            
 *                
 *            
 *              
 *              
 *
 *                                                                         
 */
static ET9BOOL ET9LOCALCALL __IsCombo(const JamoMap * const pMap,
                                      const ET9UINT         nMapSize,
                                      const ET9SYMB         symb,
                                      ET9SYMB       * const pJamo1,
                                      ET9SYMB       * const pJamo2)
{
    ET9UINT i;

    ET9Assert(pMap && pJamo1 && pJamo2);

    for (i = 0; i < nMapSize; i++) {
        if (pMap[i].sJamoCombo == symb) {
            *pJamo1 = pMap[i].sJamo1;
            *pJamo2 = pMap[i].sJamo2;
            return 1;
        }
    }
    return 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                              
 *
 *            
 *                
 *              
 *              
 *
 *                                                             
 */
static ET9SYMB ET9LOCALCALL __CombineJamos(const JamoMap * const pMap,
                                           const ET9UINT         nMapSize,
                                           const ET9SYMB         sJamo1,
                                           const ET9SYMB         sJamo2)
{
    ET9UINT i;

    ET9Assert(pMap);

    for (i = 0; i < nMapSize; i++) {
        if (pMap[i].sJamo1 == sJamo1 && pMap[i].sJamo2 == sJamo2) {
            return pMap[i].sJamoCombo;
        }
    }
    return 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *           
 *
 *                
 *
 *                
 */
static void ET9LOCALCALL __ClrJamoIMF(JamoIMF * const pJamoIMF)
{
    ET9Assert(pJamoIMF);

    pJamoIMF->bIsMultiSymbMedial = 0;
    pJamoIMF->initialSymbCount = 0;
    pJamoIMF->medialSymbCount = 0;
    pJamoIMF->finalSymbCount = 0;
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                             
 *
 *               
 *
 *                                                             
 */
static ET9U16 ET9LOCALCALL __GetFinalIndex(const ET9U16 jamoUni)
{
    ET9U16 i;

    for (i = 1; i < FINAL_JAMO_COUNT; i++) {
        if (FINAL_JAMO[i] == jamoUni) {
            return i;
        }
    }
    return 0;
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *             
 *
 *             
 *                  
 *            
 *                 
 *
 *                                  
 */
static ET9BOOL ET9LOCALCALL __AppendJamo(ET9SimpleWord * const pJamo,
                                         ET9U8         * const pbJamoType,
                                         const ET9SYMB         symb,
                                         const ET9U8           bSymbType)
{
    ET9Assert(pJamo && pbJamoType);

    if (pJamo->wLen >= ET9MAXWORDSIZE) {
        return 0;
    }
    pJamo->sString[pJamo->wLen] = symb;
    pbJamoType[pJamo->wLen] = bSymbType;
    pJamo->wLen++;
    return 1;
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *               
 *
 *                   
 *                  
 *                  
 *
 *                                  
 */
static ET9BOOL ET9LOCALCALL __AppendHangul(ET9KHangulWord * const pHangulWord,
                                           const ET9SYMB          sHangulUni,
                                           const ET9U8            bSymbCount)
{
    ET9Assert(pHangulWord);

    if (pHangulWord->wLen >= ET9MAXWORDSIZE) {
        return 0;
    }
    pHangulWord->sString[pHangulWord->wLen] = sHangulUni;
    pHangulWord->pSymbCounts[pHangulWord->wLen] = bSymbCount;
    pHangulWord->wLen++;
    return 1;
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                             
 *                                                               
 *
 *                                                         
 *               
 *                
 *
 *                                                 
 */
static ET9U8 ET9LOCALCALL __DecomposeSymbToCJI(const ET9SYMB   symb,
                                               ET9SYMB * const pString,
                                               const ET9BOOL   bInOrder)
{
    ET9SYMB sJamo1 = symb, sJamo2;
    ET9U8   bLen = 0;

    ET9Assert(pString);

    /* since the decompose process is in reverse order, need to store the CJI parts in a buffer first */
    while (bLen < 5 && __IsCombo(CHUNJIIN_COMBO_MAP, CHUNJIIN_COMBO_MAP_COUNT, sJamo1, &sJamo1, &sJamo2)) {

        pString[bLen++] = sJamo2;

        if (ET9K_CHUNJIIN_UNI_DOTDOT == sJamo1) { /* double dot: add a single dot to the reversed buffer */
            pString[bLen++] = ET9K_CHUNJIIN_UNI_DOT;
            sJamo1 = ET9K_CHUNJIIN_UNI_DOT;
            break;
        }
        else if (ET9K_CHUNJIIN_UNI_DOT == sJamo1) {
            break;
        }
    }

    ET9Assert((bLen + 1) <= 5);

    pString[bLen++] = sJamo1;

    /* reverse the order to make in order */
    if ( bInOrder ) {
        ET9U8   bStart = 0, bEnd = bLen - 1;
        ET9SYMB sTmpSymb;
        while (bStart < bEnd) {
            sTmpSymb = pString[bStart];
            pString[bStart++] = pString[bEnd];
            pString[bEnd--] = sTmpSymb;
        }
    }

    return bLen;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                 
 *
 *                                                         
 *                    
 *                         
 *                                                                   
 *
 *                                                         
 */
static ET9BOOL ET9LOCALCALL __DecomposeToCJI(const ET9SYMB         symb,
                                             ET9SimpleWord * const pCompactJamo,
                                             ET9U8         * const pbCompactJamoType,
                                             ET9U8         * const pbSymbolCount)
{
    ET9SYMB pReversedCJI[5];
    ET9U8   bufLen = 0;
    ET9BOOL bResult = 1;

    ET9Assert(pCompactJamo && pbCompactJamoType && pbSymbolCount);

    bufLen = __DecomposeSymbToCJI(symb, pReversedCJI, /* reverse order */ 0);

    *pbSymbolCount = bufLen;//(ET9U8)(bufLen + 1); /* reversed buffer length + symb */
    for (; bufLen > 0; --bufLen) {
        bResult = bResult && __AppendJamo(pCompactJamo, pbCompactJamoType, pReversedCJI[bufLen - 1], (ET9U8)ET9EXPLICITSYM);
    }
    return bResult;
}




/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *            
 *
 *            
 *                    
 *                         
 *                
 *
 *                                                         
 */
static ET9BOOL ET9LOCALCALL __CompactJamo(JamoIMF       * const pIMF,
                                          ET9SimpleWord * const pCompactJamo,
                                          ET9U8         * const pbCompactJamoType,
                                          const ET9BOOL         bJoinCJI)
{
    ET9SYMB sJamo1, sJamo2;
    ET9BOOL bResult = 1;

    ET9Assert(pIMF && pCompactJamo && pbCompactJamoType);

    /* must find compact form of Initial, Medial, and Final in proper order */
    if (ET9K_HASINITIAL(pIMF)) {
        ET9Assert(pIMF->initialSymbCount == 1);
        bResult = bResult && __AppendJamo(pCompactJamo, pbCompactJamoType, pIMF->initial, (ET9U8)ET9EXPLICITSYM);
    }
    if (ET9K_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 (ET9K_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)(ET9K_ISVORH(sJamo1) ? ET9EXPLICITSYM : bSymbType));
                bResult = bResult && __AppendJamo(pCompactJamo, pbCompactJamoType, sJamo2, (ET9U8)(ET9K_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)(ET9K_ISVORH(sJamo1) ? ET9EXPLICITSYM : bSymbType));
                bResult = bResult && __AppendJamo(pCompactJamo, pbCompactJamoType, sJamo2, (ET9U8)(ET9K_ISVORH(sJamo2) ? ET9EXPLICITSYM : bSymbType));
                pIMF->medialSymbCount = 2;
            }
            else { /* simple medial */
                bResult = bResult && __AppendJamo(pCompactJamo, pbCompactJamoType, pIMF->medial, bSymbType);
                pIMF->medialSymbCount = 1;
            }
        }
    }
    if (ET9K_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;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                            
 *                                 
 *                                                
 *
 *            
 *                    
 *                         
 *               
 *                
 *
 *                                                         
 */
static ET9BOOL ET9LOCALCALL __CommitJamoIMF(JamoIMF        * const pIMF,
                                            ET9SimpleWord  * const pCompactJamo,
                                            ET9U8          * const pbCompactJamoType,
                                            ET9KHangulWord * const pHangul,
                                            const ET9BOOL          bJoinCJI)
{
    ET9BOOL bResult;

    ET9Assert(pIMF && pCompactJamo && pbCompactJamoType && pHangul);

    /* Compact Jamo as needed and change symb count to compact form */
    bResult = __CompactJamo(pIMF, pCompactJamo, pbCompactJamoType, bJoinCJI);

    if (ET9K_HASINITIAL(pIMF)) {
        if (ET9K_HASMEDIAL(pIMF)) {
            if (ET9K_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(!ET9K_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 (ET9K_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(!ET9K_HASFINAL(pIMF));
        }
    }
    else {
        if (ET9K_HASMEDIAL(pIMF)) {
            /* _,M,_: add M into Hangul word individually
               never has _,M,F */
            __AppendHangul(pHangul, pIMF->medial, pIMF->medialSymbCount);
            ET9Assert(!ET9K_HASFINAL(pIMF));
        }
        else {
            if (ET9K_HASFINAL(pIMF)) {
                /* _,_,F */
                /* cannot combine, add F into Hangul word individually */
                __AppendHangul(pHangul, pIMF->final, pIMF->finalSymbCount);
            }
            else {
                /* _,_,_ */
                /* empty IMF, do nothing */
            }
        }
    }
    return bResult;
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *              
 *
 *            
 *            
 *                 
 *                
 *                           
 *
 *        
 */
static ET9INT ET9LOCALCALL __UpdateJamoIMF(JamoIMF * const pIMF,
                                           const ET9SYMB   symb,
                                           const ET9U8     bSymbType,
                                           const ET9SYMB   nextSymb,
                                           const ET9BOOL   bJoinInitConsonants)
{
    ET9SYMB sJamoCombo;

    ET9Assert(pIMF);
    ET9Assert(ET9K_ISKOREANJAMO(symb));

    /* current symbol is a consonant */
    if (ET9K_ISINITIALJAMO(symb)) {
        /* _,_,F or I,M,F */
        if (ET9K_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)); */
                pIMF->final = sJamoCombo;
                pIMF->finalSymbCount = (ET9U8)(pIMF->finalSymbCount + 1);
            }
            else { /* F and C cannot combine, no IMF udpate */
                return 0;
            }
        }
        /* x,M,_ */
        else if (ET9K_HASMEDIAL(pIMF)) {
            /* _,M,_ OR this consonant cannot be a final OR this medial is a Chun-Ji-In dot; no IMF update */
            if (!ET9K_HASINITIAL(pIMF) || __IsNonFinalConsonant(symb) || ET9K_ISDOT(pIMF->medial)) {
                return 0;
            }
            else { /* I,M,_ + C --> I,M,F */
                /* SetFinal(pIMF, symb, 1); */
                pIMF->final = symb;
                pIMF->finalSymbCount = 1;
            }
        }
        /* I,_,_ */
        else if (ET9K_HASINITIAL(pIMF)) {
            /* If combining initial Jamo consonants is supported */
            if (bJoinInitConsonants) {
                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); */
                    pIMF->final = sJamoCombo;
                    pIMF->finalSymbCount = 2;

                }
                else { /* I+C cannot combine --> I,_,_ no IMF update */
                    return 0;
                }
            }
            /* else combining initial Jamo consonants is not supported */
            else {
                return 0;
            }
        }
        /* _,_,_ (empty IMF) */
        else {
            /* I = C --> I,_,_*/
            /* SetInitial(pIMF, symb, 1); */
            pIMF->initial = symb;
            pIMF->initialSymbCount = 1;
        }
    }
    /* current symbol is a vowel or a Chun-Ji-In dot */
    else {
        ET9Assert(ET9K_ISMEDIALJAMO(symb) || ET9K_ISDOT(symb));
        /* x,x,F */
        if (ET9K_HASFINAL(pIMF)) {
            if (ET9K_ISMEDIALJAMO(symb) || ET9K_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); */
                    pIMF->final = sC1;
                    pIMF->finalSymbCount = 1;
                    return -1;
                }
                else {
                    /* F is not combo: F will become I of next IMF */
                    /* SetFinal(pIMF, 0, 0); */
                    pIMF->final = 0;
                    pIMF->finalSymbCount = 0;
                    return -1;
                }
            }
            else { /* cannot be a medial after F: F will not change. */
                return 0;
            }
        }
        /* x,M,_ */
        else if (ET9K_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 (ET9K_ISDOT(symb) || ET9K_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)); */
            pIMF->medial = sJamoCombo;
            pIMF->medialSymbCount = (ET9U8)(pIMF->medialSymbCount + 1);
        }
        /* I,_,_ or _,_,_ (empty IMF) */
        else {
            /* M = V */
            /* SetMedial(pIMF, symb, bSymbType, 1); */
            pIMF->medial = symb;
            pIMF->medialSymbCount = 1;
            if ((ET9U8)ET9MULTISYMBEXPLICIT == bSymbType) {
                pIMF->bIsMultiSymbMedial = 1;
            }
        }
    }
    /* updated IMF, consumed 1 symbol */
    return 1;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                      
 *
 *               
 *
 *                
 */
static void ET9LOCALCALL __FixCompleteLenByDot(ET9KHangulWord * const pHangul)
{
    ET9SYMB sLastSymb, s2ndLastSymb;

    ET9Assert(pHangul);

    /* 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 (ET9K_ISDOT(sLastSymb)) {
            if (ET9K_ISINITIALJAMO(s2ndLastSymb) ||
                ET9K_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--;
            }
        }
    }
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                        
 *
 *                
 *                   
 *                  
 *                
 *                     
 *               
 *                
 *                           
 *
 *                                                         
 */
ET9BOOL ET9FARCALL _EncodeJamo(const ET9SYMB  * const pJamoStr,
                               const ET9U16           wJamoStrLen,
                               const ET9U8    * const pbJamoType,
                               ET9SimpleWord  * const pOutJamo,
                               ET9U8          * const pbOutJamoType,
                               ET9KHangulWord * const pHangul,
                               const ET9BOOL          bJoinCJI,
                               const ET9BOOL          bJoinInitConsonants)
{
    ET9UINT i;
    ET9INT iCounsumedCount;
    JamoIMF jamoIMF;
    ET9SYMB currentJamo, nextJamo;
    ET9U8 currentJamoType;
    ET9BOOL bResult = 1;

    ET9Assert(pJamoStr && pbJamoType && pOutJamo && pbOutJamoType);

    /* 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 (ET9K_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 (!ET9K_ISDOT(nextJamo)) {
                    break;
                }
            }
            if (j == wJamoStrLen) {
                /* dots through the end, no next Jamo */
                nextJamo = 0;
            }
            iCounsumedCount = __UpdateJamoIMF(&jamoIMF, currentJamo, currentJamoType, nextJamo, bJoinInitConsonants);
            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;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                               
 *
 *             
 *                  
 *               
 *               
 *                    
 *                           
 *
 *             
 */
static void ET9LOCALCALL __FixCompleteLenInMT(const ET9SimpleWord * const pJamo,
                                              const ET9U8         * const pbJamoType,
                                              ET9KHangulWord      * const pHangul,
                                              const ET9SYMB       * const pMTSymb,
                                              const ET9UINT               nMTSymbCount,
                                              const ET9BOOL               bJoinInitConsonants)
{
    ET9UINT i;
    ET9SYMB sSelectedSymb;
    ET9U16 wMinCompleteLen;
    ET9KHangulWord sMultitapHangul;
    ET9SimpleWord sTmpJamo, sTmpCompactJamo; /* dummy place holder */
    ET9U8 pbTmpCompactJamoType[ET9MAXWORDSIZE]; /* dummy place holder */

    ET9Assert(pJamo && pbJamoType && pHangul && pMTSymb);

    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, bJoinInitConsonants);

        /* 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_NO_INIT    Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_BAD_PARAM  One of the parameters is invalid.
 * @return ET9STATUS_EMPTY      Input buffer is empty; there is no current input sequence.
 */
ET9STATUS ET9FARCALL ET9KBuildHangul(ET9KLingInfo   * const pKLingInfo,
                                     ET9KHangulWord * const pHangul,
                                     const ET9SYMB  * const pMTSequence,
                                     const ET9U16           wMTSymbCount)
{
    ET9WordSymbInfo *pWSI;
    ET9SimpleWord sJamo;
    ET9SimpleWord sCompactJamo;
    ET9U8 pbJamoType[ET9MAXWORDSIZE], pbCompactJamoType[ET9MAXWORDSIZE], i;
    ET9BOOL bCompactOkay;
    ET9BOOL bJoinInitConsonants;

    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] = (ET9U8)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 */
    bJoinInitConsonants = (ET9BOOL)ET9KJoinInitialJamoConsonants(pKLingInfo);
    bCompactOkay = _EncodeJamo(sJamo.sString, sJamo.wLen, pbJamoType, &sCompactJamo, pbCompactJamoType, pHangul, /* pKLingInfo->bJoinCJI */1, bJoinInitConsonants);

    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, bJoinInitConsonants);
    }

    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 = (ET9INPUTTYPE)pbCompactJamoType[i];
        }
    }

    if ( ET9KOutputCompatibilityJamo(pKLingInfo) ) {
        ET9KJamoToCompatibilityJamo(pHangul->sString, pHangul->wLen);
    }
    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *               
 *
 *                     
 *                                          
 *                   
 *              
 *               
 *
 *             
 */
static ET9U16 ET9LOCALCALL __ToJamoString(const ET9INT    nInitialIndex, 
                                          const ET9INT    nMedialIndex, 
                                          const ET9INT    nFinalIndex, 
                                          const ET9BOOL   bToCJI, 
                                          ET9SYMB * const pString)
{
    const JamoDemap *pDemap;
    ET9U16 wLen = 0;

    ET9Assert(pString);

    if (nInitialIndex >= 0) {
        pString[wLen++] = (ET9SYMB)(ET9K_INITIAL_UNI_MIN + nInitialIndex);
    }
    if ( nMedialIndex >= 0 ) {
        if (bToCJI) { /* handle medial CJI conversion */
            wLen = wLen + __DecomposeSymbToCJI((ET9SYMB)(ET9K_MEDIAL_UNI_MIN + nMedialIndex), (pString + wLen), 1);
        } 
        else 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;
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                    
 *
 *                                                         
 *                                                                                
 *                                                       
 *                                                                  
 *                                                                       
 *
 *                                       
 *                                                                               
 *                                                              
 */
ET9STATUS ET9FARCALL _ET9K_Hangul2Jamo(const ET9SYMB * const pHangulStr, 
                                       const ET9U16          wHangulStrLen, 
                                       ET9SimpleWord * const pJamo, 
                                       const ET9BOOL         bLeadingUpper, 
                                       const ET9BOOL         bToCJI)
{
    ET9UINT i;
    ET9UINT j;
    ET9INT nInitial;
    ET9INT nMedial;
    ET9INT nFinal;
    ET9SYMB sHangul;
    ET9SYMB sString[8];
    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 ( bToCJI && sHangul == ET9K_CHUNJIIN_UNI_DOTDOT) {
            wLen = __DecomposeSymbToCJI(sHangul, sString, 1);
        }
        else if ( ET9K_MEDIAL_UNI_MIN <= sHangul && sHangul <= ET9K_MEDIAL_UNI_MAX ) {
            nMedial = (ET9INT)(sHangul - ET9K_MEDIAL_UNI_MIN);
            wLen = __ToJamoString( -1, nMedial, -1, bToCJI, 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, bToCJI, 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, bToCJI, 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.
 * @param bToChunJiIn           Whether decode to ChunJiIn or not
 *
 * @return ET9STATUS_NONE       Succeeded.
 * @return ET9STATUS_NO_INIT    Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_BAD_PARAM  One of the parameter values is invalid.
 * @return ET9STATUS_FULL       Decoded Hangul has resulted in a Jamo that exceeds ET9MAXWORDSIZE.
 */
ET9STATUS ET9FARCALL ET9KDecodeHangul(ET9KLingInfo         * const pKLingInfo,
                                      const ET9KHangulWord * const pHangul,
                                      ET9SimpleWord        * const pJamo,
                                      const ET9BOOL                bToChunJiIn)
{
    ET9STATUS status;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if ( pHangul == NULL || pJamo == NULL ) {
        return ET9STATUS_BAD_PARAM;
    }

    status = _ET9K_Hangul2Jamo(pHangul->sString, pHangul->wLen, pJamo, /*bLeadingUpper*/0, bToChunJiIn);

    if (status == ET9STATUS_NONE && ET9KOutputCompatibilityJamo(pKLingInfo) ) {
        ET9KJamoToCompatibilityJamo(pJamo->sString, pJamo->wLen);
    }

    return status;
}


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                               
 *
 *                
 *                   
 *                  
 *               
 *                           
 *
 *                                       
 */
ET9STATUS ET9FARCALL _ET9K_Jamo2Hangul(const ET9SYMB  * const pJamoStr, 
                                       const ET9U16           wJamoStrLen, 
                                       const ET9U8    * const pbJamoType, 
                                       ET9KHangulWord * const pHangul,
                                       const ET9BOOL          bJoinInitConsonants)
{
    ET9SimpleWord sCompactJamo;
    ET9U8 pbCompactJamoType[ET9MAXWORDSIZE];
    ET9BOOL bCompactOkay;

    /* validate param */
    if (NULL == pJamoStr || NULL == pbJamoType || NULL == pHangul) {
        return ET9STATUS_BAD_PARAM;
    }

    sCompactJamo.wLen = 0;
    bCompactOkay = _EncodeJamo(pJamoStr, wJamoStrLen, pbJamoType, &sCompactJamo, pbCompactJamoType, pHangul, /* bJoinCJI */0, bJoinInitConsonants);

    return ET9STATUS_NONE;
}


/* 0x3131 to 0x318E  compatibility jamo  */
static const 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 const 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 const 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 const 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};

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                      
 *
 *             
 *
 *                                          
 */
static ET9U16 ET9LOCALCALL __ToCompatibilityJamo(const 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;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                 
 *
 *             
 *
 *                            
 */
static ET9U16 ET9LOCALCALL __ToInternalJamo(const ET9U16 wJamo)
{
    if ( 0x3131 <= wJamo && wJamo <= 0x318E ) {
        return g_acInternalJamo[wJamo - 0x3131];
    }
    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_NO_INIT     Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_BAD_PARAM   Value of is pwJamo is NULL. 
 */
ET9STATUS ET9FARCALL ET9KJamoToCompatibilityJamo(ET9U16 * const pwJamo, 
                                                 const 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_NO_INIT     Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_BAD_PARAM   Value of is pwJamo is NULL. 
 */
ET9STATUS ET9FARCALL ET9KCompatibilityJamoToJamo(ET9U16 * const pwJamo, 
                                                 const 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 >--------------------------------- */
