/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                    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: et9ksys.c                                                   **
;**                                                                           **
;**  Description: Korean XT9 system module.                                   **
;**               Conforming to the development version of Korean XT9.        **
;**                                                                           **
;*******************************************************************************
;******************************************************************************/

#include "et9kapi.h"
#include "et9ksys.h"
#include "et9kjamo.h"
#include "et9imu.h"
#include "et9misc.h"


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                    
 */

const ET9U8 _pbXt9Korean[] = { 'c', 'o', 'm', '.', 'n', 'u', 'a', 'n', 'c', 'e', '.', 'x', 't', '9', '.', 'k', 'o', 'r', 'e', 'a', 'n', 0 };


#define NO_IGNORE_INDEX  0xFFFF
/** Initializes an XT9 Korean LDB.
 *
 * @param  pKLingInfo         pointer to korean linguistic information structure
 * @param  ET9LdbReadData     callback function used to read data from the LDB
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
*/
ET9STATUS ET9FARCALL ET9KLdbInit(ET9KLingInfo   * pKLingInfo,
                                 ET9DBREADCALLBACK ET9LdbReadData)
{
    ET9STATUS status;
    ET9_K_CHECK_LINGINFO(pKLingInfo);
    status = ET9AWLdbInit(&pKLingInfo->sET9AWLingInfo, ET9LdbReadData);;
    if ( status == ET9STATUS_NONE )
    {
        status = ET9AWLdbSetLanguage(&pKLingInfo->sET9AWLingInfo, ET9PLIDKorean, 0);
    }
    return status;
}

/**
 * Sets up the initial RUDB.
 * Either using a previously saved RUDB or a 'fresh' memory buffer.
 * Will reset the provided RUDB if it looks corrupted.
 *
 * @param pKLingInfo     Pointer to korean information structure.
 * @param pRUDBInfo      Pointer to RUDB buffer.
 * @param wDataSize      Size (in bytes) of provided RUDB buffer. Must be >= ET9MINRUDBDATABYTES
 * @param pWriteCB       Callback routine pointer (if integration layer handling RUDB writes); otherwise NULL.
 *
 * @return ET9STATUS_NONE on success, otherwise return error code.
 */
ET9STATUS ET9FARCALL ET9KRUDBInit( ET9KLingInfo      * pKLingInfo,
                                   ET9U8 ET9FARDATA  * pRUDBInfo,
                                   ET9U16              wDataSize,
                                   ET9DBWRITECALLBACK  pWriteCB)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);
    return ET9AWRUDBInit(&pKLingInfo->sET9AWLingInfo, (ET9AWRUDBInfo *)pRUDBInfo, wDataSize, pWriteCB);
}

/**
 * Adds a UDB word to the RUDB.
 *
 * @param pKLingInfo     Pointer to korean information structure.
 * @param psHangul       Pointer to word being added.
 * @param wHangulLen     Length (in symbols) of word being added.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KUDBAddWord(ET9KLingInfo *  pKLingInfo,
                                    const ET9SYMB * psHangul,
                                    ET9U16          wHangulLen)
{
    ET9STATUS status;
    ET9SimpleWord   jamo;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    status = ET9_K_Hangul2Jamo(psHangul, wHangulLen, &jamo, /*bLeadingUpper*/1);
    if ( status == ET9STATUS_NONE ) {
        status = ET9AWUDBAddWord(&pKLingInfo->sET9AWLingInfo, jamo.sString, jamo.wLen);
    }
    return status;
}

/**
 * Gets the number of currently defined UDB words.
 *
 * @param pKLingInfo     Pointer to korean information structure.
 * @param pHangulCount   Pointer to store UDB word count in.
 *
 * @return ET9STATUS_NONE on success, otherwise return error code.
 */
ET9STATUS ET9FARCALL ET9KUDBGetWordCount(ET9KLingInfo * pKLingInfo,
                                         ET9U16       * pHangulCount)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    return ET9AWUDBGetWordCount(&pKLingInfo->sET9AWLingInfo, pHangulCount);
}

/**
 * Delete UDB word.
 * Deletes a given UDB word from the RUDB.
 *
 * @param pKLingInfo     Pointer to korean information structure.
 * @param psHangul       Pointer to word to match for deletion.
 * @param wHangulLen     Length (in symbols) of word pointed to by psBuf.
 *
 * @return ET9STATUS_NONE if UDB word found and deleted;.
 *           ET9STATUS_NO_MATCHING_WORDS if no matching UDB word was found
 */
ET9STATUS ET9FARCALL ET9KUDBDeleteWord(ET9KLingInfo * pKLingInfo,
                                       ET9SYMB      * psHangul,
                                       ET9U16         wHangulLen)
{
    ET9STATUS status;
    ET9SimpleWord   jamo;
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    status = ET9_K_Hangul2Jamo(psHangul, wHangulLen, &jamo, /*bLeadingUpper*/1);
    if ( status == ET9STATUS_NONE ) {
        status = ET9AWUDBDeleteWord(&pKLingInfo->sET9AWLingInfo, jamo.sString, jamo.wLen);
    }
    return status;
}

/**
 * Find UDB word.
 * Finds a given UDB word from the RUDB.
 *
 * @param pKLingInfo     Pointer to korean information structure.
 * @param psHangul       Pointer to word to match for deletion.
 * @param wHangulLen     Length (in symbols) of word pointed to by psBuf.
 *
 * @return ET9STATUS NONE if word is found; ET9STATUS_NO_MATCHING_WORDS if not.
 */
ET9STATUS ET9FARCALL ET9KUDBFindWord(ET9KLingInfo * pKLingInfo,
                                       ET9SYMB      * psHangul,
                                       ET9U16         wHangulLen)
{
    ET9STATUS status;
    ET9SimpleWord   jamo;
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    status = ET9_K_Hangul2Jamo(psHangul, wHangulLen, &jamo, /*bLeadingUpper*/1);
    if ( status == ET9STATUS_NONE ) {
        status = ET9AWUDBFindWord(&pKLingInfo->sET9AWLingInfo, jamo.sString, jamo.wLen);
    }
    return status;
}

static ET9STATUS ET9LOCALCALL ET9_K_CaseJamo2Hangul(ET9SYMB * pJamoStr, ET9U16 wJamoStrLen, ET9SYMB * pcHangulBuf, ET9U16 HangulBufSize, ET9U16 *pwHangulLen)
{
    ET9STATUS status = ET9STATUS_NONE;
    ET9KHangulWord  hangul;
    ET9U16 w, wTotalLen = 0;
    ET9U8 abJamoType[ET9MAXWORDSIZE];
    ET9U16 wStart = 0;
    ET9SYMB sSave;
    while ( wStart < wJamoStrLen )
    {
        for ( w = 0; wStart + w < wJamoStrLen; w++ )
        {
            if ( w > 0 && ET9K_IS_UPPER_JAMO(pJamoStr[wStart + w]) ) 
            {
                break;
            }

            abJamoType[w] = ET9EXPLICITSYM;
        }
        sSave = pJamoStr[wStart];
        pJamoStr[wStart] = ET9K_TO_LOWER_JAMO(pJamoStr[wStart]);
        status = ET9_K_Jamo2Hangul(pJamoStr + wStart, w, abJamoType, &hangul);
        pJamoStr[wStart] = sSave;

        if ( status != ET9STATUS_NONE )
        {
            return status;
        }
        else if ( (ET9U16)(wTotalLen + hangul.wLen) <= HangulBufSize )
        {
            _ET9SymCopy(pcHangulBuf + wTotalLen, hangul.sString, hangul.wLen);
            wTotalLen = (ET9U16)(wTotalLen + hangul.wLen);
            wStart = (ET9U16)(wStart + w);
        }
        else
            return ET9STATUS_BUFFER_TOO_SMALL;
    }
    *pwHangulLen = wTotalLen;

    return status;
}

static void ET9LOCALCALL ET9_K_InPlaceToLower(ET9SYMB * pwBuf, ET9U16 wLen)
{
    if ( pwBuf != NULL )
    {
        ET9U16 w;
        for ( w = 0; w < wLen; w++ )
        {
            pwBuf[w] = ET9K_TO_LOWER_JAMO(pwBuf[w]);
        }
    }
}

/**
 * Get UDB Word.
 * Designed to be used repeatedly to retrieve all of the UDB words contained in the RUDB.<P>
 *
 * The first time the routine is called, the value contained in *pwWordLen
 * should be 0; subsequent calls should contain the previously retrieved
 * UDB word in *psWordBuf and the size of that previously retrieved word in
 * *pwWordLen.<P>
 *
 * If *pwWordLen is non-zero, function first compares the word passed in
 * *psWordBuf against the record pointed to by the internally maintained
 * (and private)pUDBGetEntry (which points to the last processed UDB record).
 * If they don't match, it indicates an update has been made to the
 * RUDB that would disrupt normal, consecutive record processing, and the
 * function restarts the process at the beginning of the RUDB
 * (and returns a status of ET9STATUS_WORD_NOT_FOUND along with the first UDB word).<P>
 *
 * When processing 'forward', the routine will return the first UDB record
 * and proceed forward through the RUDB. When processing in 'reverse', the routine
 * starts with the last UDB word in the current RUDB and then word backwords
 * through the RUDB.<BR>
 * If the API is called again after traversing all UDB words, either forward
 * or in reverse, the routine will return the ET9STATUS_NO_MATCHING_WORDS status.
 *
 * @param pKLingInfo      Pointer to korean information structure.
 * @param psHangulBuf     Pointer to buffer for loading UDB word (should contain the previously retrieved UDB word on subsequent calls).
 * @param wHangulBufSize  Size (in symbols) of the input buffer (should be at least ET9MAXWORDSIZE).
 * @param pwHangulLen     Pointer to load current UDB word length into (should contain the previously retrieved UDB word length on subsequent calls).
 * @param bForward        1 to get first/next word, 0 to get previous word.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.<BR>
 *         On success:  *psWordBuf contains the (first/next) UDB word,
 *                      *pwWordLen contains the UDB word length
 */
ET9STATUS ET9FARCALL ET9KUDBGetWord(ET9KLingInfo *  pKLingInfo,
                                    ET9SYMB      *  psHangulBuf,
                                    ET9U16          wHangulBufSize,
                                    ET9U16       *  pwHangulLen,
                                    ET9U8           bForward)
{
    ET9STATUS status;
    ET9SimpleWord   jamo;
    ET9U16 wTotalLen = 0;
    ET9SYMB acHangul[ET9MAXWORDSIZE];

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    /* conditions copied from ET9AWUDBGetWord() */
    if (psHangulBuf == NULL || pwHangulLen == NULL) {
        return ET9STATUS_INVALID_MEMORY;
    }
    else if (wHangulBufSize < ET9MAXWORDSIZE) {
        return ET9STATUS_BUFFER_TOO_SMALL;
    }
    if (*pwHangulLen > ET9MAXUDBWORDSIZE) {
        return ET9STATUS_BAD_PARAM;
    }

    status = ET9_K_Hangul2Jamo(psHangulBuf, *pwHangulLen, &jamo, /*bLeadingUpper*/1);
    if ( status == ET9STATUS_NONE )
    {
        status = ET9AWUDBGetWord(&pKLingInfo->sET9AWLingInfo, jamo.sString, sizeof(jamo.sString) / sizeof(jamo.sString[0]), &jamo.wLen, bForward);
        if ( status == ET9STATUS_NONE || status == ET9STATUS_WORD_NOT_FOUND ) 
        {
            wTotalLen = 0;
            acHangul[0] = 0;
            status = ET9_K_CaseJamo2Hangul(jamo.sString, jamo.wLen, acHangul, ET9MAXWORDSIZE, &wTotalLen);

            if ( status == ET9STATUS_NONE )
            {
                *pwHangulLen = wTotalLen;
                if ( ET9KOutputCompatibilityJamo(pKLingInfo) )
                    ET9KJamoToCompatibilityJamo(acHangul, wTotalLen);
                else
                {
                    ET9_K_InPlaceToLower(acHangul, wTotalLen);
                }
                _ET9SymCopy(psHangulBuf, acHangul, wTotalLen);
            }
        }
    }
    return status;
}

/**
 * Set RUDB to an initial clean state.
 * Removes all UDB.RDB records from RUDB.
 *
 * @param pKLingInfo      Pointer to korean information structure.
 *
 * @return ET9STATUS_NONE on success, else error status.
 */
ET9STATUS ET9FARCALL ET9KRUDBReset(ET9KLingInfo * pKLingInfo)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    return ET9AWRUDBReset(&pKLingInfo->sET9AWLingInfo);
}

static void ET9LOCALCALL InitIndexMapping(ET9KLingInfo * pKLingInfo)
{
    ET9U8 w;
    for ( w = 0; w < ET9K_SEL_LIST_SIZE; w++ )
    {
        pKLingInfo->abIndexMapping[w] = w;
    }
}

/** Initialize Korean XT9 system.
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 * @param pWordSymbInfo        pointer to word symbol info structure.
 * @param pPublicExtension     a value the integration layer can set and then retrieve in pKLingInfo->pPublicExtension.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_BAD_PARAM  one of the parameters is invalid
 */
ET9STATUS ET9FARCALL ET9KSysInit(ET9KLingInfo       * const pKLingInfo,
                                 ET9WordSymbInfo    * const pWordSymbInfo,
                                 void ET9FARDATA    * const pPublicExtension)
{
    ET9STATUS status = ET9STATUS_NONE;

    if (_ET9ByteStringCheckSum(_pbXt9Korean) != 7203520U) {
        return ET9STATUS_ERROR;
    }

    if (!pKLingInfo || !pWordSymbInfo) {
        return ET9STATUS_BAD_PARAM;
    }

    _ET9ClearMem((ET9U8*)pKLingInfo, sizeof(ET9KLingInfo));

    status = _ET9WordSymbInit(pWordSymbInfo);
    ET9Assert(ET9STATUS_NONE == status);
    if (status) {
        return status;
    }

    pKLingInfo->Base.pWordSymbInfo = pWordSymbInfo;

    pKLingInfo->Base.bSelListInvalidated = 1;
    pKLingInfo->pPublicExtension = pPublicExtension;
    pKLingInfo->wInitOK = ET9GOODSETUP;
    pKLingInfo->bJoinCJI = 1; /* turn on Join ChunJiIn by default */
    pKLingInfo->bCompatibilityJamo = 1;  /* turn on Compatibility output by default */
    pKLingInfo->FctMdbCallBack = NULL;

    pKLingInfo->bEnableCJI = 0; /* Default is disable CJI */
    InitIndexMapping(pKLingInfo);

    status = ET9AWSysInit( &pKLingInfo->sET9AWLingInfo,
                           &pKLingInfo->sAWLingCmnInfo,
                           pWordSymbInfo,
                           /* bResetLingCmn */1,
                           sizeof(pKLingInfo->pWordList) / sizeof(pKLingInfo->pWordList[0]),
                           pKLingInfo->pWordList,
                           pPublicExtension);
    ET9Assert(ET9STATUS_NONE == status);
    if (status) {
        return status;
    }

    pWordSymbInfo->Private.ppEditionsList[ET9EDITION_KO] = &pKLingInfo->Base;

    /* Word completion */
    status = ET9AWSetDBCompletion(&pKLingInfo->sET9AWLingInfo);
    ET9Assert(ET9STATUS_NONE == status);
    if (status) {
        return status;
    }

    status = ET9AWSysSetWordCompletionPoint(&pKLingInfo->sET9AWLingInfo, 3);
    ET9Assert(ET9STATUS_NONE == status);
    if (status) {
        return status;
    }

    status = ET9AWSysSetCompletionCount(&pKLingInfo->sET9AWLingInfo, 8);
    ET9Assert(ET9STATUS_NONE == status);
    if (status) {
        return status;
    }

    /* Spell correction off */
    status = ET9AWSysSetSpellCorrectionMode(&pKLingInfo->sET9AWLingInfo, ET9ASPCMODE_OFF, 0);
    ET9Assert(ET9STATUS_NONE == status);
    if (status) {
        return status;
    }

    status = ET9AWSysSetSpellCorrectionTraceSearchFilter(&pKLingInfo->sET9AWLingInfo, ET9ASPCTRACESEARCHFILTER_ONE_EXACT);
    if (status) {
        return status;
    }

    status = ET9AWClearAutoAppendInList(&pKLingInfo->sET9AWLingInfo);
    if (status) {
        return status;
    }

    status = ET9ClearDownshiftDefault(&pKLingInfo->sET9AWLingInfo);
    if (status) {
        return status;
    }

    /* Exact on */
    status = ET9AWSetExactInList(&pKLingInfo->sET9AWLingInfo, ET9AEXACTINLIST_DEFAULT);
    ET9Assert(ET9STATUS_NONE == status);
    if (status) {
        return status;
    }

    return status;
}

/**
 * Retrieves XT9 core and build version information.
 *
 * @param[in,out] psCodeVerBuf          Pointer to a buffer where XT9 writes the version information. The buffer should be at least ET9MAXVERSIONSTR characters in length.
 * @param[in]     wBufMaxSize           The length in characters of the buffer pointed to by psCodeVerBuf.
 * @param[out]    pwBufSize             Pointer to a value indicating the size of the version information XT9 has written to psCodeVerBuf.
 *
 * @retval ET9STATUS_NONE               Function call was handled successfully.
 * @retval ET9STATUS_INVALID_MEMORY     At least one pointer passed as a function argument was set to null.
 * @retval ET9STATUS_NO_MEMORY          The buffer pointed to by psCodeVerBuf is too small to store the information. Ensure the buffer is at least ET9MAXVERSIONSTR characters.
 *
 * @remarks The string is always 42 chars long. String format is defined as follows: <tt>XT9 VMM.mm.bf.qa hh:mm:ss mm dd yyyy xxxx</tt>, where:<br>
 * \c MM   = major version number<br>
 * \c mm   = minor version number<br>
 * \c bf   = patch/bugfix version number<br>
 * \c qa   = release candidate version number<br>
 * \c hh   = hour of release compile<br>
 * \c mm   = minutes of release compile<br>
 * \c ss   = seconds of release compile<br>
 * \c dd   = day of month<br>
 * \c mmm = month (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec)<br>
 * \c yyyy = four-digit year<br>
 * \c xxxx = OEM ID in hex
 *
 */
ET9STATUS ET9FARCALL ET9KGetCodeVersion(ET9SYMB * const psCodeVerBuf,
                                        const ET9U16    wBufMaxSize,
                                        ET9U16  * const pwBufSize)
{
    return ET9GetCodeVersion(psCodeVerBuf, wBufMaxSize, pwBufSize);
}

/**
 * This function gets the version string form the active/current LDB.
 *
 *  String format is defined as follows:
 *       XT9 LDB Taa.bb Lcc.dd Vee.ff.gg.hh
 *
 *  where
 *          aa = Database type
 *          bb = Database version
 *
 *          cc = Primary language ID
 *          dd = Secondary language ID
 *
 *          ee = Contents major version
 *          ff = Contents minor version
 *          gg = Contents deviation
 *          hh = Module and character set
 *
 * @param pKLingInfo                Pointer to korean information structure.
 * @param psLdbVerBuf               (out) buffer for the LDB version number.
 * @param wBufMaxSize               Maximum buffer size for the LDB version.
 * @param pwBufSize                 (out) actual string length.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KLdbGetVersion(ET9KLingInfo  * const pKLingInfo,
                                       ET9SYMB       * const psLdbVerBuf,
                                       const ET9U16          wBufMaxSize,
                                       ET9U16        * const pwBufSize)
{
    ET9STATUS status = ET9STATUS_NONE;
    ET9_K_CHECK_LINGINFO(pKLingInfo);
    status = ET9AWLdbGetVersion(&pKLingInfo->sET9AWLingInfo, psLdbVerBuf, wBufMaxSize, pwBufSize);
    return status;
}

static ET9STATUS ET9LOCALCALL ET9_K_GetAWWord(ET9KLingInfo *pKLingInfo, ET9U8 bIndex, ET9AWWordInfo ** ppWordInfo)
{
    ET9STATUS status;
    status = ET9AWSelLstGetWord(&pKLingInfo->sET9AWLingInfo, ppWordInfo, bIndex);
    return status;
}

static ET9BOOL ET9LOCALCALL SameHangul(ET9SYMB * pcHangul, ET9U16 wLen, ET9AWWordInfo * pWordInfo)
{
    ET9STATUS status;
    ET9U16  w, wTotalLen;
    ET9SYMB acHangul[ET9MAXWORDSIZE];

    status = ET9_K_CaseJamo2Hangul(pWordInfo->sWord, pWordInfo->wWordLen, acHangul, ET9MAXWORDSIZE, &wTotalLen);
    if ( status != ET9STATUS_NONE )
        return 0;
    if ( wLen != wTotalLen )
        return 0;

    for ( w = 0; w < wLen; w++ )
    {
       if ( pcHangul[w] != acHangul[w] )
            return 0;
    }

    return 1;
}

/**
 * This function builds selection list for the current tap sequence.
 * If needed it will perform a number of list builds to catchup on the input changes.
 *
 * @param pKLingInfo                 Pointer to korean information structure.
 * @param pbTotalWords              (out) number of candidate words found.
 * @param pbDefaultListIndex        (out) suggested default candidate.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KBuildSelectionList(ET9KLingInfo * pKLingInfo, ET9U8 * pbTotalWords, ET9U8 * pbDefaultListIndex)
{
    ET9STATUS status;
    ET9KHangulWord sHangul;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    InitIndexMapping(pKLingInfo);
    if ( !ET9KIsChunJiInEnabled(pKLingInfo) )
    {
        status = ET9AWSelLstBuild(&pKLingInfo->sET9AWLingInfo, pbTotalWords, pbDefaultListIndex);
        if (ET9STATUS_NONE != status) {
            return status;
        }
        if ( *pbTotalWords > 1 )
        {
            ET9BOOL fSameWord = 0;
            ET9AWWordInfo * pWordInfo1 = NULL;
            ET9AWWordInfo * pWordInfo2 = NULL;
            ET9STATUS status1 = ET9_K_GetAWWord(pKLingInfo, 0, &pWordInfo1);
            if ( status1 == ET9STATUS_NONE && pWordInfo1->bWordSource == ET9AWORDSOURCE_NEWWORD)
            {
                ET9SYMB acHangul[ET9MAXWORDSIZE];
                ET9U16 wTotalLen;
                ET9_K_InPlaceToLower( pWordInfo1->sWord, pWordInfo1->wWordLen );
                status1 = ET9_K_CaseJamo2Hangul(pWordInfo1->sWord, pWordInfo1->wWordLen, acHangul, ET9MAXWORDSIZE, &wTotalLen);
                if ( status1 == ET9STATUS_NONE )
                {
                    ET9U8 b, bDupCount = 0;;
                    for ( b = 1; b < *pbTotalWords; b++ )
                    {
                        status1 = ET9_K_GetAWWord(pKLingInfo, b, &pWordInfo2);
                        if ( status1 == ET9STATUS_NONE && pWordInfo1->wWordLen == pWordInfo2->wWordLen)
                        {
                            fSameWord = SameHangul( acHangul, wTotalLen, pWordInfo2 ); 
                            if ( fSameWord )
                            {
                                ET9U8 w;
                                if ( pKLingInfo->abIndexMapping[0] == 0 )
                                {
                                    pKLingInfo->abIndexMapping[0] = b;
                                    if (*pbDefaultListIndex > b ) /* Protection only. Probaly this is not reachable */
                                        *pbDefaultListIndex = (ET9U8)(*pbDefaultListIndex - 1);
                                }
                                for ( w = b - bDupCount; w < ET9K_SEL_LIST_SIZE; w++ )
                                {
                                    pKLingInfo->abIndexMapping[w]++;
                                }
                                bDupCount++;
                            }
                        }
                    }
                    *pbTotalWords = (ET9U8)(*pbTotalWords - bDupCount);
                }
            }
        }
    }
    else
    {
        if (!pbTotalWords || !pbDefaultListIndex) {
            return ET9STATUS_INVALID_MEMORY;
        }
        *pbTotalWords = 0;
        *pbDefaultListIndex = 0;

        status = ET9KBuildHangul(pKLingInfo, &sHangul, NULL, 0);
        if ( ET9STATUS_NONE == status && sHangul.wLen > 0 )
        {
            *pbTotalWords = 1;
        }
    }
    return status;
}

/**
 * Select hangul.
 * This function allows the host to specify which hangul has been selected.
 *
 * @param pKLingInfo    Pointer to korean information structure.
 * @param bHangulIndex  The 0-based index of the selected hangul.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KSelectHangul(ET9KLingInfo * pKLingInfo, ET9U8 bHangulIndex)
{
    ET9STATUS status = ET9STATUS_NONE;
    ET9U8 bIndex;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if ( ET9KIsChunJiInEnabled(pKLingInfo) )
        return ET9STATUS_NONE;

    if ( bHangulIndex >= ET9K_SEL_LIST_SIZE )
        return ET9STATUS_OUT_OF_RANGE;

    bIndex = pKLingInfo->abIndexMapping[bHangulIndex];

    status = ET9AWSelLstSelWord( &pKLingInfo->sET9AWLingInfo, bIndex );
    return status;
}

/**
 * Note Hangul done.

 * This function allows the host to specify which entry has been selected.
 * Similar to ET9KSelectHangul, except it passes the actual selection.
 *
 * @param pKLingInfo                Pointer to Korean information structure.
 * @param psHangul                  Pointer to Hangul word.
 * @param wHangulLen                Length of Hangul word.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KNoteHangulDone(ET9KLingInfo * pKLingInfo, ET9SYMB * psHangul, ET9U16 wHangulLen)
{
    ET9STATUS status = ET9STATUS_NONE;
    ET9SimpleWord   jamo;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if ( ET9KIsChunJiInEnabled(pKLingInfo) )
        return ET9STATUS_INVALID_MODE;

    status = ET9_K_Hangul2Jamo(psHangul, wHangulLen, &jamo, /*bLeadingUpper*/1);

    if (status == ET9STATUS_NONE)
        status = ET9AWNoteWordDone( &pKLingInfo->sET9AWLingInfo, jamo.sString, jamo.wLen );

    return status;
}

/**
 * This function gets an indexed Hangul from the selection list.
 *
 * After ET9KBuildSelectionList(), this function can be called to get specified hangul from the selection list
 *
 * @param pKLingInfo                Pointer to alphabetic information structure.
 * @param bHangulIndex              Hangul index of base 0.
 * @param psHangulBuf               Pointer to an adddress for the output Hangul.
 * @param wHangulBufLen             Size of psHangulBuf (number of ET9SYMB).
 * @param pwHangulLen               Pointer to an adddress for the length of output Hangul (number of ET9SYMB).
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KGetHangul(ET9KLingInfo * pKLingInfo, 
                                   ET9U8          bHangulIndex, 
                                   ET9SYMB      * psHangulBuf,
                                   ET9U16         wHangulBufLen,
                                   ET9U16       * pwHangulLen) 
{
    ET9STATUS status;
    ET9U8 bIndex;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if ( psHangulBuf == NULL || pwHangulLen == NULL )  
        return ET9STATUS_INVALID_MEMORY;

    *pwHangulLen = 0;

    if ( !ET9KIsChunJiInEnabled(pKLingInfo) )
    {
        ET9AWWordInfo * pWordInfo = NULL;

        if ( bHangulIndex >= ET9K_SEL_LIST_SIZE )
            return ET9STATUS_OUT_OF_RANGE;

        bIndex = pKLingInfo->abIndexMapping[bHangulIndex];

        status = ET9_K_GetAWWord(pKLingInfo, bIndex, &pWordInfo);
        if ( ET9STATUS_NONE == status ) {
            ET9U16 wTotalLen = 0;
            ET9SYMB acHangul[ET9MAXWORDSIZE];
            if ( pWordInfo->bWordSource == ET9AWORDSOURCE_NEWWORD )
            {
                ET9_K_InPlaceToLower(pWordInfo->sWord, pWordInfo->wWordLen);
            }
            status = ET9_K_CaseJamo2Hangul( pWordInfo->sWord, pWordInfo->wWordLen, acHangul, ET9MAXWORDSIZE, &wTotalLen );

            if ( status == ET9STATUS_NONE )
            {
                if ( ET9KOutputCompatibilityJamo(pKLingInfo) )
                {
                    ET9KJamoToCompatibilityJamo( acHangul, wTotalLen );
                }
                else
                {
                    ET9_K_InPlaceToLower( acHangul, wTotalLen );
                }

                if ( wHangulBufLen < wTotalLen )
                    return ET9STATUS_BUFFER_TOO_SMALL;
                else
                {
                    *pwHangulLen = wTotalLen;
                    _ET9SymCopy(psHangulBuf, acHangul, wTotalLen);
                }
            }
        }
    }
    else
    {
        ET9KHangulWord hangul;

        if ( bHangulIndex >= 1 )
            return ET9STATUS_OUT_OF_RANGE;

        status = ET9KBuildHangul( pKLingInfo, &hangul, NULL, 0 );
        if ( status == ET9STATUS_NONE )
        {
            if ( wHangulBufLen < hangul.wLen )
                return ET9STATUS_BUFFER_TOO_SMALL;
            else
            {
                *pwHangulLen = hangul.wLen;
                _ET9SymCopy(psHangulBuf, hangul.sString, hangul.wLen);
                ET9_K_InPlaceToLower(psHangulBuf, *pwHangulLen);
            }
        }
    }

    return status;
}

/** Setup the Korean linguistic module to output compatibility Jamo
 *
 * Compatibility Jamo
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT    system not properly initialized
 */
ET9STATUS ET9FARCALL ET9KEnableCompatibilityJamo(ET9KLingInfo * const pKLingInfo)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);
    pKLingInfo->bCompatibilityJamo = 1;
    return ET9STATUS_NONE;
}

/** Setup the Korean linguistic module to output unicode Jamo
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT    system not properly initialized
 */
ET9STATUS ET9FARCALL ET9KDisableCompatibilityJamo(ET9KLingInfo * const pKLingInfo)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);
    pKLingInfo->bCompatibilityJamo = 0;
    return ET9STATUS_NONE;
}


/** Setup the Korean linguistic module to join ChunJiIn into Jamo and modify WordSymbolInfo
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT    system not properly initialized
 * @return ET9STATUS_INVALID_INPUT    cannot change setting when there is input
 */
ET9STATUS ET9FARCALL ET9KEnableJoinChunJiIn(ET9KLingInfo * const pKLingInfo)
{
    ET9WordSymbInfo *pWSI;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pWSI = pKLingInfo->Base.pWordSymbInfo;
    if (0 == pKLingInfo->bJoinCJI) {
        if (pWSI->bNumSymbs > 0) {
            /* cannot change setting when there is input */
            return ET9STATUS_INVALID_INPUT;
        }
        /* set the flag */
        pKLingInfo->bJoinCJI = 1;
    }
    return ET9STATUS_NONE;
}

/** Setup the Korean linguistic module NOT to join ChunJiIn into Jamo.
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT    system not properly initialized
 * @return ET9STATUS_INVALID_INPUT    cannot change setting when there is input
 */
ET9STATUS ET9FARCALL ET9KDisableJoinChunJiIn(ET9KLingInfo * const pKLingInfo)
{
    ET9WordSymbInfo *pWSI;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pWSI = pKLingInfo->Base.pWordSymbInfo;
    if (pKLingInfo->bJoinCJI) {
        if (pWSI && pWSI->bNumSymbs > 0) {
            /* cannot change setting when there is input */
            return ET9STATUS_INVALID_INPUT;
        }
        /* clear the flag */
        pKLingInfo->bJoinCJI = 0;
    }
    return ET9STATUS_NONE;
}

/** Enable Chun-Ji-In input.
 *
 * If Chun-Ji-In input is enabled, Korean Linguistic module will not search Ldb. Insted, it will directly construct Hangul from Jamo and Chun-Ji-In
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT    system not properly initialized
 * @return ET9STATUS_INVALID_INPUT    cannot change setting when there is input
 */
ET9STATUS ET9FARCALL ET9KEnableChunJiIn(ET9KLingInfo * const pKLingInfo)
{
    ET9WordSymbInfo *pWSI;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pWSI = pKLingInfo->Base.pWordSymbInfo;
    if (!ET9KIsChunJiInEnabled(pKLingInfo)) {
        if (pWSI->bNumSymbs > 0) {
            /* cannot change setting when there is input */
            return ET9STATUS_INVALID_INPUT;
        }
        /* set the flag */
        pKLingInfo->bEnableCJI = 1;
    }
    return ET9STATUS_NONE;
}

/** Disable Chun-Ji-In input.
 *
 * If Chun-Ji-In input is disabled, Korean Linguistic module will search Ldb.
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @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    cannot change setting when there is input
 */
ET9STATUS ET9FARCALL ET9KDisableChunJiIn(ET9KLingInfo * const pKLingInfo)
{
    ET9WordSymbInfo *pWSI;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pWSI = pKLingInfo->Base.pWordSymbInfo;
    if (ET9KIsChunJiInEnabled(pKLingInfo)) {
        if (pWSI->bNumSymbs > 0) {
            /* cannot change setting when there is input */
            return ET9STATUS_INVALID_INPUT;
        }
        /* clear the flag */
        pKLingInfo->bEnableCJI = 0;
    }
    return ET9STATUS_NONE;
}

ET9STATUS ET9FARCALL ET9AWReadMdbData( ET9AWLingInfo *pAWLing,
                                       ET9REQMDB eMdbRequestType,
                                       ET9U16 wReqWordLen,
                                       ET9U16 wMaxWordLen,
                                       ET9SYMB *psBuildTxtBuf,
                                       ET9U16 *pwWordLen,
                                       ET9U32 *pdwWordListIdx )
{
    ET9STATUS status = ET9STATUS_NONE;
    ET9KLingInfo * pKLingInfo;

    ET9_UNUSED(eMdbRequestType);

    ET9Assert( !(pAWLing == NULL || psBuildTxtBuf == NULL || pwWordLen == NULL || pdwWordListIdx == NULL) );
    ET9Assert( ET9AWSys_Init_OK(pAWLing) );

    pKLingInfo = (ET9KLingInfo*)pAWLing->pLingCmnInfo->Base.pWordSymbInfo->Private.ppEditionsList[ET9EDITION_KO];
    ET9Assert( pKLingInfo != NULL );
    if ( !pKLingInfo->FctMdbCallBack )
        return ET9STATUS_NO_INIT;

    while ( ET9STATUS_NONE == status ) 
    {
        ET9SYMB asHangulBuf[ET9MAXWORDSIZE];
        ET9SimpleWord   jamo = {0, 0, {0}};

        do
        {
            status = pKLingInfo->FctMdbCallBack( pKLingInfo, ET9MAXWORDSIZE, asHangulBuf, pwWordLen, pdwWordListIdx );
            if ( status != ET9STATUS_NONE )
                break;
            status = ET9_K_Hangul2Jamo(asHangulBuf, *pwWordLen, &jamo, /*bLeadingUpper*/1);
        } while ( status == ET9STATUS_FULL );

        if ( status == ET9STATUS_NONE && jamo.wLen >= wReqWordLen && jamo.wLen <= wMaxWordLen)
        {
            _ET9SymCopy(psBuildTxtBuf, jamo.sString, jamo.wLen);
            *pwWordLen = jamo.wLen;
            /* return this word */
            break;
        }
    }

    return status;
}


/**
 * Register a manufacturer database.
 *
 * @param pKLingInfo                 Pointer to Korean information structure.
 * @param MdbReadMdbDataFct          Read MDB function.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KRegisterMDB(ET9KLingInfo * pKLingInfo,
                                     ET9K_MDB_CALLBACK MdbReadMdbDataFct)
{
    ET9STATUS status;
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pKLingInfo->FctMdbCallBack = MdbReadMdbDataFct;

    status = ET9AWRegisterMDB( &pKLingInfo->sET9AWLingInfo, ET9AWReadMdbData );
    return status;
}

/**
 * Unregister a manufacturer database.
 *
 * @param pKLingInfo                 Pointer to Korean information structure.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */
ET9STATUS ET9FARCALL ET9KUnregisterMDB(ET9KLingInfo * pKLingInfo)
{
    ET9STATUS status;
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pKLingInfo->FctMdbCallBack = NULL;
    status = ET9AWUnregisterMDB( &pKLingInfo->sET9AWLingInfo );
    return status;
}

#ifdef ET9_K_INTERNAL_DEBUG
ET9STATUS ET9FARCALL ET9_K_CaseJamo2Hangul_Internal(ET9SYMB * pJamoStr, ET9U16 wJamoStrLen, ET9SYMB * pcHangulBuf, ET9U16 HangulBufSize, ET9U16 *pwHangulLen)
{
    return ET9_K_CaseJamo2Hangul(pJamoStr, wJamoStrLen, pcHangulBuf, HangulBufSize, pwHangulLen);
}
#endif



/*! \mainpage

\section intro_sec XT9 Korean 7.1 Release Summary

SOURCE CODE: Please be aware that you must obtain approval from Nuance Communications
prior to making any modifications to XT9 source code.<p>

<p>

\subsection  Sub_WordList Word List Based Input
Starting from XT9 Korean Version 7.1, word list based input is supported by the core.  <p>

In order to use word list based input, ET9KDisableChunJiIn() must be called, since word  list input does not support ChunJiIn<p>

Simply put valid jamo into ET9WordSymbInfo, then call ET9KBuildSelectionList(). Then you can call ET9KGetHangul() repeatedly to dispalay the selection list to the end user. When user selects some hangul, integration layer should call ET9KSelectHangul() and ET9KNoteHangulDone() to notify the Korean core and do what ever you want to put the selected hangul, e.g., insert it into the text field.<p>

<p>

\subsection  Sub_UDB RUDB | MDB Support
Starting from XT9 Korean Version 7.1, RUDB and MDB is supported by the core.  <p>

In order to enable RUDB, integration layer must call ET9KRUDBInit() to pass in a buffer of size at least ET9MINRUDBDATABYTES (10240) bytes.<p>

In order to enable MDB, integration layer must call ET9KRegisterMDB() to pass in a callback function to the core so that core can get the Hangul word by word.<p>

<p>

\subsection  Sub_Trace Trace Input
Starting from XT9 Korean Version 7.1, Trace input is supported by the core and input module.  <p>

Only word list based input can support Trace. <p>

Just use ET9KDB_ProcessTrace() to convert the tracing curve to ET9WordSymbInfo, then you can build the selection list like in the tapping mode.

<p>
*/

/* ----------------------------------< eof >--------------------------------- */
