/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                    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: 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"
#include "et9sym.h"


/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                    
 */

static 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

/*---------------------------------------------------------------------------*/
/**
 * Turn on context based prediction.
 *
 * @param pKLingInfo         Pointer to korean information structure.
 *
 * @return ET9STATUS_NONE             Succeeded
 * @return ET9STATUS_NO_INIT          Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY   Pointer passed was set to NULL.
 */
ET9STATUS ET9FARCALL ET9KEnableContextBasedPrediction(ET9KLingInfo * const pKLingInfo)
{    
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    return ET9AWSetContextBasedPrediction(&pKLingInfo->sET9AWLingInfo);
}

/*---------------------------------------------------------------------------*/
/**
 * Turn off context based prediction.
 *
 * @param pKLingInfo         Pointer to korean information structure.
 *
 * @return ET9STATUS_NONE             Succeeded
 * @return ET9STATUS_NO_INIT          Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY   Pointer passed was set to NULL.
 */
ET9STATUS ET9FARCALL ET9KDisableContextBasedPrediction(ET9KLingInfo * const pKLingInfo)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    return ET9AWClearContextBasedPrediction(&pKLingInfo->sET9AWLingInfo);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                
 *
 *                  
 *             
 *               
 *                 
 *                   
 *                   
 *
 *                                
 */
static ET9UINT ET9LOCALCALL __ET9K_PrepareFillContextBuffer(ET9KLingInfo   * const pKLingInfo,
                                                            ET9SYMB        * const psBuf,
                                                            const ET9UINT          nBufLen,
                                                            ET9SYMB        * const psJamoBuf,
                                                            const ET9UINT          nJamoBufLen) 
{
    ET9UINT nIndex = nBufLen;
    ET9U16  wLen = 0;
    ET9S16  wCount = 0;
    ET9UINT nJamoLen = 0;

    ET9SYMB        *psJamo = psJamoBuf;

    ET9SimpleWord   psJamos[ET9NLM_CONTEXT_COUNT];

    ET9Assert(psJamoBuf && nJamoBufLen);

    /* process the words from the end, and decompose to jamo.
     * This iteration stops whenever, 
     *   1. finish collecting ET9NLM_CONTEXT_COUNT number of words
     *   2. decomposed jamo exceeds ET9MAXWORDSIZE
     *   3. more than one space presented
     *   4. byte number of prcoessed words exceed output buffer
     */
    while (nIndex && nJamoLen < nJamoBufLen && wCount < ET9NLM_CONTEXT_COUNT) {
        ET9SYMB sSymb = psBuf[--nIndex];

        if (_ET9_IsWhiteSpace(sSymb)) {

            /* word found, process decomposing */
            if (wLen) {

                ET9SimpleWord * pJamo = &psJamos[wCount];

                ET9Assert(nIndex + 1 + wLen <= nBufLen);

                /* decompose to jamo, and check to see if exceed buffer */
                if (wLen >= ET9MAXWORDSIZE || ET9STATUS_FULL == _ET9K_Hangul2Jamo(psBuf + (nIndex + 1), wLen, pJamo, 1, pKLingInfo->bEnableCJI)) {

                    /* last word is already too long, then don't need further processing */
                    if (!wCount) {
                        return 0;
                    }

                    /* ignore exceeded word, and only prcoess words collected so far */
                    break;
                }
                else {

                    if (nJamoLen + pJamo->wLen >= nJamoBufLen) {
                        break;
                    }

                    nJamoLen += pJamo->wLen;
                    wCount++;
                }

                nJamoLen++;
                wLen = 0;
            }
            else if (nIndex + 1!= nBufLen) { /* not tailing space */

                /* more than two spaces */
                break;
            }
        }
        else {
            wLen++;
        }
    }

    ET9Assert(nJamoLen < nJamoBufLen);

    /* pick up any leftover - this takes care of the case for the very first of three words in the buffer without leading space */
    if (!nIndex && 
        wCount < ET9NLM_CONTEXT_COUNT && 
        wLen < ET9MAXWORDSIZE && 
        ET9STATUS_FULL != _ET9K_Hangul2Jamo(psBuf, wLen, &psJamos[wCount], 1, pKLingInfo->bEnableCJI) &&
        nJamoLen + psJamos[wCount].wLen < nJamoBufLen) {
        wCount++;
    }

    if (!wCount) {
        /* no words to process */
        return 0 ;
    }

    wCount--;

    nIndex = 0;

    /* copy jamo to output buffer
     * NOTE: at this moment, number of bytes to be written should be within nJamoBufLen
     */
    while (wCount >= 0 && nIndex < nJamoBufLen) {
        
        ET9Assert((psJamo + nIndex + psJamos[wCount].wLen) < (psJamoBuf + nJamoBufLen));

        _ET9SymCopy(psJamo + nIndex, psJamos[wCount].sString, psJamos[wCount].wLen);        
        
        nIndex += psJamos[wCount].wLen;

        if (wCount) {
            
            ET9Assert((psJamo + nIndex + 1) < (psJamoBuf + nJamoBufLen));
    
            psJamo[nIndex++] = 0x20;
        }

        wCount--;
    }

    return nIndex;
}

/*---------------------------------------------------------------------------*/
/**
 * Make this the current context.
 *
 * @param pKLingInfo       Pointer to krean information structure.
 * @param psBuf            Buffer to be used.
 * @param nBufLen          Length of buffer.
 *
 * @return ET9STATUS_NONE             Succeeded
 * @return ET9STATUS_NO_INIT          Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY   Buffer pointer passed was set to NULL and buffer length is not zero.
 */
ET9STATUS ET9FARCALL ET9KFillContextBuffer(ET9KLingInfo   * const pKLingInfo,
                                           ET9SYMB        * const psBuf,
                                           const ET9UINT          nBufLen)
{
    ET9SYMB         psJamoBuf[ET9MAXWORDSIZE * ET9NLM_CONTEXT_COUNT];
    ET9UINT         nCount;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if (!psBuf && nBufLen) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (!nBufLen) {
        /* empty buf, then break context */
        return ET9AWFillContextBuffer(&pKLingInfo->sET9AWLingInfo, 0, 0);
    }

    nCount = __ET9K_PrepareFillContextBuffer(pKLingInfo, psBuf, nBufLen, psJamoBuf,  ET9MAXWORDSIZE * ET9NLM_CONTEXT_COUNT);

    return ET9AWFillContextBuffer(&pKLingInfo->sET9AWLingInfo, psJamoBuf, nCount);
}

/*---------------------------------------------------------------------------*/
/**
 * 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            Succeeded.
 * @return ET9STATUS_NO_INIT         Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_LDB_ID_ERROR    LDB is not correct for XT9 Korean.
 * @return ET9STATUS_BAD_PARAM       Callback pointer is NULL.
 * @return ET9STATUS_INVALID_MEMORY  At least one pointer passed was set to NULL.
 * @return ET9STATUS_DB_CORE_INCOMP  LDB is incompatible with the XT9 core.
 * @return ET9STATUS_WRONG_OEMID     LDB OEM ID is incorrect.
 */
ET9STATUS ET9FARCALL ET9KLdbInit(ET9KLingInfo    * const pKLingInfo,
                                 const ET9DBREADCALLBACK ET9LdbReadData)
{
    ET9STATUS eStatus;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    eStatus = ET9AWLdbInit(&pKLingInfo->sET9AWLingInfo, ET9LdbReadData);;
    if ( eStatus == ET9STATUS_NONE ) {
        eStatus = ET9AWLdbSetLanguage(&pKLingInfo->sET9AWLingInfo, ET9PLIDKorean, 0);
        /* if failed using the default (Jamo) load */
        if (eStatus == ET9STATUS_LDB_ID_ERROR) {
            /* try using the CJI identifier */
            eStatus = ET9AWLdbSetLanguage(&pKLingInfo->sET9AWLingInfo, (ET9PLIDKorean | ET9SLIDChunJiIn) , 0);
            /* if CJI, go ahead and set CJI mode and the RUDB pointer */
            if (eStatus == ET9STATUS_NONE) {
                pKLingInfo->bEnableCJI = 1;
                pKLingInfo->sET9AWLingInfo.pLingCmnInfo->pRUDBInfo = pKLingInfo->pCJIRUDBInfo;
            }
        }
        /* if Jamo, make sure CJI mode turned off, and correct RUDB pointer being used */
        else if (eStatus == ET9STATUS_NONE) {
            pKLingInfo->bEnableCJI = 0;
            pKLingInfo->sET9AWLingInfo.pLingCmnInfo->pRUDBInfo = pKLingInfo->pJamoRUDBInfo;
        }
    }
    return eStatus;
}

static void ET9LOCALCALL __ET9K_ClearRUDBHeader(ET9KLingInfo * const pKLingInfo,
                                                     void ET9FARDATA *pRUDBInfo,
                                                     const ET9DBWRITECALLBACK pWriteCB)
{
    ET9UINT       nSize = sizeof(ET9AWRUDBInfo);

    if (pWriteCB) {
        ET9AWRUDBInfo sHeader;

        _ET9ClearMem(&sHeader, nSize);  
        pWriteCB(&pKLingInfo->sET9AWLingInfo, (ET9U8 ET9FARDATA *)pRUDBInfo, (ET9U8 ET9FARDATA *)&sHeader, nSize);
    }
    else {
        ET9U8 ET9FARDATA *pbTo = pRUDBInfo;
        ET9U8 bFrom = 0;

        while (nSize--) {
            *pbTo++ = bFrom;
        }
    }
}

/*---------------------------------------------------------------------------*/
/**
 * 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 >= ET9K_MINRUDBDATABYTES
 * @param pWriteCB       Callback routine pointer (if integration layer handling RUDB writes); otherwise NULL.
 *
 * @return ET9STATUS_NONE                Succeeded.
 * @return ET9STATUS_NO_INIT             Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY      RUDB pointer was passed with a zero size, or a NULL RUDB pointer was passed with a non-zero size.
 * @return ET9STATUS_INVALID_SIZE        Value passed for RUDB buffer size is smaller than  ET9K_MINRUDBDATABYTES.
 * @return ET9STATUS_ALREADY_INITIALIZED Indicates an RUDB that has already been initialized is being replaced with a new one.
 */
ET9STATUS ET9FARCALL ET9KRUDBInit(ET9KLingInfo      * const pKLingInfo,
                                  ET9U8 ET9FARDATA  * const pRUDBInfo,
                                  const ET9U16              wDataSize,
                                  const ET9DBWRITECALLBACK  pWriteCB)
{
    ET9U8  ET9FARDATA *pHalfWay;
    ET9U16             wSizer;     
    ET9STATUS          wStatus;   
    ET9STATUS          wStatusCJI;   
    ET9STATUS          wStatusJAMO;   

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    
    if (((pRUDBInfo != NULL) && !wDataSize) || ((pRUDBInfo == NULL) && wDataSize)) {
        wStatus = ET9STATUS_INVALID_MEMORY;
    }
    /* Check minimum RUDB size */
    else if ((pRUDBInfo != NULL) && (wDataSize < ET9K_MINRUDBDATABYTES)) {
        wStatus = ET9STATUS_INVALID_SIZE;
    }
    else {
        /* Split RUDB in two, one for Jamo and one for CJI */
        wSizer = wDataSize / 2;
        pHalfWay = ((ET9U8 ET9FARDATA *)pRUDBInfo) + wSizer;
        pKLingInfo->pCJIRUDBInfo = (ET9AWRUDBInfo *)pHalfWay;
        pKLingInfo->pJamoRUDBInfo = (ET9AWRUDBInfo *)pRUDBInfo;
        /* if the base header changed size, second RUDB will be incorrect */
        if (pKLingInfo->pJamoRUDBInfo && (pKLingInfo->pJamoRUDBInfo->wDataSize != wSizer)) {
            __ET9K_ClearRUDBHeader(pKLingInfo, pKLingInfo->pCJIRUDBInfo, pWriteCB);
            __ET9K_ClearRUDBHeader(pKLingInfo, pKLingInfo->pJamoRUDBInfo, pWriteCB);
        }
        else if (pKLingInfo->pCJIRUDBInfo && (pKLingInfo->pCJIRUDBInfo->wDataSize != wSizer)) {
            __ET9K_ClearRUDBHeader(pKLingInfo, pKLingInfo->pCJIRUDBInfo, pWriteCB);
        }

        /* Initialize the CJI RUDB */
        wStatusCJI =  ET9AWRUDBInit(&pKLingInfo->sET9AWLingInfo, (ET9AWRUDBInfo *)pHalfWay, wSizer, pWriteCB);
    
        /* HAVE TO CLEAR ENTRY BEFORE NEXT INIT */
        pKLingInfo->sET9AWLingInfo.pLingCmnInfo->pRUDBInfo = NULL;

        /* Initialize the JAMO RUDB */
        wStatusJAMO =  ET9AWRUDBInit(&pKLingInfo->sET9AWLingInfo, (ET9AWRUDBInfo *)pRUDBInfo, wSizer, pWriteCB);

        if (wStatusCJI != ET9STATUS_NONE) {
            wStatus = wStatusCJI;
        }
        else {
            wStatus = wStatusJAMO;
        }
    }
    return wStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * 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       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       Passed Hangul converts to a Jamo that exceeds ET9MAXWORDSIZE.
 * @return ET9STATUS_NO_RUDB    RUDB not properly initalized before calling this function.
 */
ET9STATUS ET9FARCALL ET9KUDBAddWord(ET9KLingInfo  * const pKLingInfo,
                                    const ET9SYMB * const psHangul,
                                    const ET9U16          wHangulLen)
{
    ET9STATUS       eStatus;
    ET9SimpleWord   jamo;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    eStatus = _ET9K_Hangul2Jamo(psHangul, wHangulLen, &jamo, /*bLeadingUpper*/1, pKLingInfo->bEnableCJI);
    if ( eStatus == ET9STATUS_NONE ) {
        eStatus = ET9AWUDBAddWord(&pKLingInfo->sET9AWLingInfo, jamo.sString, jamo.wLen);
    }
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * 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            Succeeded.
 * @return ET9STATUS_NO_INIT         Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY  At least one pointer passed was set to NULL.
 * @return ET9STATUS_NO_RUDB         RUDB not properly initalized before calling this function.
 */
ET9STATUS ET9FARCALL ET9KUDBGetWordCount(ET9KLingInfo * const pKLingInfo,
                                         ET9U16       * const 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                Succeeded.
 * @return ET9STATUS_NO_INIT             Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_NO_MATCHING_WORDS   The linguistic module could not find the word pointed to by psHangul.
 * @return ET9STATUS_BAD_PARAM           One of the parameter values is invalid.
 * @return ET9STATUS_FULL                Passed Hangul converts to a Jamo that exceeds ET9MAXWORDSIZE.
 * @return ET9STATUS_NO_RUDB             RUDB not properly initalized before calling this function.
 */
ET9STATUS ET9FARCALL ET9KUDBDeleteWord(ET9KLingInfo  * const pKLingInfo,
                                       const ET9SYMB * const psHangul,
                                       const ET9U16          wHangulLen)
{
    ET9STATUS      eStatus;
    ET9SimpleWord  jamo;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    eStatus = _ET9K_Hangul2Jamo(psHangul, wHangulLen, &jamo, /*bLeadingUpper*/1, pKLingInfo->bEnableCJI);
    if ( eStatus == ET9STATUS_NONE ) {
        eStatus = ET9AWUDBDeleteWord(&pKLingInfo->sET9AWLingInfo, jamo.sString, jamo.wLen);
    }
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                       
 *
 *                
 *                   
 *                   
 *                     
 *                   
 *                           
 *
 *                                                       
 */
static ET9STATUS ET9LOCALCALL __ET9K_CaseJamo2Hangul(ET9SYMB * const pJamoStr, 
                                                     const ET9U16    wJamoStrLen, 
                                                     ET9SYMB * const pcHangulBuf, 
                                                     const ET9U16    HangulBufSize, 
                                                     ET9U16  * const pwHangulLen,
                                                     ET9BOOL   const bJoinInitConsonants)
{
    ET9STATUS eStatus = ET9STATUS_NONE;
    ET9KHangulWord  hangul;
    ET9U16 w, wTotalLen = 0;
    ET9U8 abJamoType[ET9MAXWORDSIZE];
    ET9U16 wStart = 0;
    ET9SYMB sSave;

    ET9Assert(pJamoStr);
    ET9Assert(pcHangulBuf);
    ET9Assert(pwHangulLen);

    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]);
        eStatus = _ET9K_Jamo2Hangul(pJamoStr + wStart, w, abJamoType, &hangul, bJoinInitConsonants);
        pJamoStr[wStart] = sSave;

        if ( eStatus != ET9STATUS_NONE ) {
            return eStatus;
        }
        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 eStatus;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                      
 *
 *             
 *            
 *
 *                
 */
static void ET9LOCALCALL __ET9K_InPlaceToLower(ET9SYMB * const pwBuf, 
                                               const ET9U16    wLen)
{
    ET9U16 w;

    ET9Assert(pwBuf);

    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              Succeeded.
 *                On success:  *psHangulBuf contains the first/next UDB word.
 *                             *pwHangulLen contains the UDB word length.
 * @return ET9STATUS_NO_INIT            Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_WORD_NOT_FOUND     XT9 could not locate in the UDB the word pointed to by
 *                psHangulBuf and of the size indicated by the value of pwHangulLen. This status indicates a warning, 
 *                not an error:  in this situation, XT9 returns to the beginning of the UDB and retrieves the first word in the database.
 * @return ET9STATUS_NO_MATCHING_WORDS  There are no custom words in the UDB, or all custom words in the UDB have been retrieved.
 * @return ET9STATUS_INVALID_MEMORY     At least one pointer passed was set to NULL.
 * @return ET9STATUS_BUFFER_TOO_SMALL   Passed buffer length is less than ET9MAXWORDSIZE.
 * @return ET9STATUS_BAD_PARAM          Length of the Hangul exceeds ET9MAXUDBWORDSIZE
 */
ET9STATUS ET9FARCALL ET9KUDBGetWord(ET9KLingInfo * const pKLingInfo,
                                    ET9SYMB      * const psHangulBuf,
                                    const ET9U16         wHangulBufSize,
                                    ET9U16       * const pwHangulLen,
                                    const ET9U8          bForward)
{
    ET9STATUS eStatus;
    ET9SimpleWord   jamo;
    ET9U16 wTotalLen = 0;
    ET9SYMB acHangul[ET9MAXWORDSIZE];
    ET9BOOL bJoinInitConsonants;

    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;
    }

    eStatus = _ET9K_Hangul2Jamo(psHangulBuf, *pwHangulLen, &jamo, /*bLeadingUpper*/1, pKLingInfo->bEnableCJI);

    if ( eStatus == ET9STATUS_NONE ) {
        eStatus = ET9AWUDBGetWord(&pKLingInfo->sET9AWLingInfo, jamo.sString, sizeof(jamo.sString) / sizeof(jamo.sString[0]), &jamo.wLen, bForward);
        if ( eStatus == ET9STATUS_NONE || eStatus == ET9STATUS_WORD_NOT_FOUND ) {
            wTotalLen = 0;
            acHangul[0] = 0;
            bJoinInitConsonants = (ET9BOOL)ET9KJoinInitialJamoConsonants(pKLingInfo);
            eStatus = __ET9K_CaseJamo2Hangul(jamo.sString, jamo.wLen, acHangul, ET9MAXWORDSIZE, &wTotalLen, bJoinInitConsonants);

            if ( eStatus == ET9STATUS_NONE ) {
                *pwHangulLen = wTotalLen;
                if ( ET9KOutputCompatibilityJamo(pKLingInfo) ) {
                    ET9KJamoToCompatibilityJamo(acHangul, wTotalLen);
                }
                else {
                    __ET9K_InPlaceToLower(acHangul, wTotalLen);
                }
                _ET9SymCopy(psHangulBuf, acHangul, wTotalLen);
            }
        }
    }
    return eStatus;
}


/*---------------------------------------------------------------------------*/
/**
 * Set RUDB to an initial clean state.
 * Removes all UDB.RDB records from RUDB.
 *
 * @param pKLingInfo      Pointer to korean information structure.
 *
 * @return ET9STATUS_NONE     Succeeded.
 * @return ET9STATUS_NO_INIT  Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_NO_RUDB  RUDB not properly initalized before calling this function.
 */
ET9STATUS ET9FARCALL ET9KRUDBReset(ET9KLingInfo * const pKLingInfo)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    return ET9AWRUDBReset(&pKLingInfo->sET9AWLingInfo);
}



/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                   
 *
 *                                                                
 *
 *                
 */
static void ET9LOCALCALL __InitIndexMapping(ET9KLingInfo * const pKLingInfo)
{
    ET9U8 w;

    ET9Assert(pKLingInfo);

    for ( w = 0; w < ET9MAXSELLISTSIZE; 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_INVALID_MEMORY   At least one pointer passed was set to NULL.
 * @return ET9STATUS_BAD_PARAM        one of the parameters is invalid
 * @return ET9STATUS_ERROR            The Korean linguistic module has encountered an error.
 */
ET9STATUS ET9FARCALL ET9KSysInit(ET9KLingInfo    * const pKLingInfo,
                                 ET9WordSymbInfo * const pWordSymbInfo,
                                 void ET9FARDATA * const pPublicExtension)
{
    ET9STATUS eStatus = ET9STATUS_NONE;

    if (_ET9ByteStringCheckSum(_pbXt9Korean) != 7203520U) {
        return ET9STATUS_ERROR;
    }

    if (!pKLingInfo || !pWordSymbInfo) {
        return ET9STATUS_BAD_PARAM;
    }

    _ET9ClearMem((ET9U8*)pKLingInfo, sizeof(ET9KLingInfo));
    _ET9ClearMem((ET9U8*)pWordSymbInfo, sizeof(ET9WordSymbInfo));

    eStatus = _ET9WordSymbInit(pWordSymbInfo);
    ET9Assert(ET9STATUS_NONE == eStatus);
    if (eStatus) {
        return eStatus;
    }

    pKLingInfo->Base.pWordSymbInfo = pWordSymbInfo;

    pKLingInfo->Base.bSelListInvalidated = 1;
    pKLingInfo->pPublicExtension = pPublicExtension;
    pKLingInfo->wInitOK = ET9GOODSETUP;
    pKLingInfo->bCompatibilityJamo = 1;  /* turn on Compatibility output by default */
    pKLingInfo->bInitialConsonantJoin = 1;  /* turn on joining initial jamo consonants by default */
    pKLingInfo->FctMdbCallBack = NULL;

    pKLingInfo->bEnableCJI = 0; /* Default is disable CJI */
    __InitIndexMapping(pKLingInfo);

    eStatus = ET9AWSysInit( &pKLingInfo->sET9AWLingInfo,
                           &pKLingInfo->sAWLingCmnInfo,
                           pWordSymbInfo,
                           /* bResetLingCmn */1,
                           ET9MAXSELLISTSIZE,
                           pPublicExtension);
    ET9Assert(ET9STATUS_NONE == eStatus);
    if (eStatus) {
        return eStatus;
    }

    pWordSymbInfo->Private.ppEditionsList[ET9EDITION_KO] = &pKLingInfo->Base;

    /* Word completion */
    eStatus = ET9AWSetDBCompletion(&pKLingInfo->sET9AWLingInfo);
    ET9Assert(ET9STATUS_NONE == eStatus);
    if (eStatus) {
        return eStatus;
    }

    eStatus = ET9AWSetWordCompletionPoint(&pKLingInfo->sET9AWLingInfo, 3);
    ET9Assert(ET9STATUS_NONE == eStatus);
    if (eStatus) {
        return eStatus;
    }

    eStatus = ET9AWSetCompletionCount(&pKLingInfo->sET9AWLingInfo, 8);
    ET9Assert(ET9STATUS_NONE == eStatus);
    if (eStatus) {
        return eStatus;
    }

    /* Spell correction off */
    eStatus = ET9AWSetSpellCorrectionMode(&pKLingInfo->sET9AWLingInfo, ET9ASPCMODE_OFF, 0);
    ET9Assert(ET9STATUS_NONE == eStatus);
    if (eStatus) {
        return eStatus;
    }

    eStatus = ET9AWSetSpellCorrectionTraceSearchFilter(&pKLingInfo->sET9AWLingInfo, ET9ASPCTRACESEARCHFILTER_ONE_EXACT);
    if (eStatus) {
        return eStatus;
    }

    eStatus = ET9AWClearAutoAppendInList(&pKLingInfo->sET9AWLingInfo);
    if (eStatus) {
        return eStatus;
    }

    eStatus = ET9ClearDownshiftDefault(&pKLingInfo->sET9AWLingInfo);
    if (eStatus) {
        return eStatus;
    }

    /* Exact on */
    eStatus = ET9AWSetExactInList(&pKLingInfo->sET9AWLingInfo, ET9AEXACTINLIST_DEFAULT);
    ET9Assert(ET9STATUS_NONE == eStatus);
    if (eStatus) {
        return eStatus;
    }

    return eStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * 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_NO_INIT            Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @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            Function call was handled successfully.
 * @return ET9STATUS_NO_INIT         Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY  At least one pointer passed was set to NULL.
 * @return ET9STATUS_NO_MEMORY       The buffer pointed to by psLdbVerBuf is too small to storethe information. 
 *                                   Ensure the buffer is at leastET9MAXVERSIONSTR bytes.
 * @return ET9STATUS_NO_DB_INIT      The LDB was not initialized successfully.
 */
ET9STATUS ET9FARCALL ET9KLdbGetVersion(ET9KLingInfo * const pKLingInfo,
                                       ET9SYMB      * const psLdbVerBuf,
                                       const ET9U16          wBufMaxSize,
                                       ET9U16       * const pwBufSize)
{
    ET9STATUS eStatus = ET9STATUS_NONE;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    eStatus = ET9AWLdbGetVersion(&pKLingInfo->sET9AWLingInfo, psLdbVerBuf, wBufMaxSize, pwBufSize);
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                  
 *
 *                  
 *              
 *                  
 *
 *               
 */
static ET9STATUS ET9LOCALCALL __ET9K_GetAWWord(ET9KLingInfo   * const pKLingInfo, 
                                               const ET9U8            bIndex, 
                                               ET9AWWordInfo ** const ppWordInfo)
{
    ET9STATUS eStatus;

    ET9Assert(pKLingInfo && ppWordInfo);

    eStatus = ET9AWSelLstGetWord(&pKLingInfo->sET9AWLingInfo, ppWordInfo, bIndex);
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *             
 *
 *                
 *            
 *                 
 *                           
 *
 *                                
 */
static ET9BOOL ET9LOCALCALL __SameHangul(const ET9SYMB * const pcHangul, 
                                         const ET9U16          wLen, 
                                         ET9AWWordInfo * const pWordInfo,
                                         ET9BOOL         const bJoinInitConsonants)
{
    ET9STATUS eStatus;
    ET9U16  w, wTotalLen;
    ET9SYMB acHangul[ET9MAXWORDSIZE];

    ET9Assert(pcHangul);
    ET9Assert(pWordInfo);

    eStatus = __ET9K_CaseJamo2Hangul(pWordInfo->sWord, pWordInfo->wWordLen, acHangul, ET9MAXWORDSIZE, &wTotalLen, bJoinInitConsonants);
    if ( eStatus != 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.
 * @param pwGestureValue            (out) possible gesture detected.
 *
 * @return ET9STATUS_NONE            Succeeded.
 * @return ET9STATUS_NO_INIT         Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY  At least one pointer passed was set to NULL.
 */
ET9STATUS ET9FARCALL ET9KBuildSelectionList(ET9KLingInfo * const pKLingInfo, 
                                            ET9U8        * const pbTotalWords, 
                                            ET9U8        * const pbDefaultListIndex,
                                            ET9U16       * const pwGestureValue)
{
    ET9STATUS eStatus;
    ET9WordSymbInfo *pWordSymbInfo;
    ET9U8 i;
    ET9BOOL bJoinInitConsonants;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if (!pbTotalWords || !pbDefaultListIndex || !pwGestureValue) {
        return ET9STATUS_INVALID_MEMORY;
    }

    *pbTotalWords = 0;
    *pbDefaultListIndex = 0;

    __InitIndexMapping(pKLingInfo);

    pWordSymbInfo = pKLingInfo->sAWLingCmnInfo.Base.pWordSymbInfo;
    if (pWordSymbInfo->bNumSymbs > 2) {
        ET9U8 bNumSymbs = pWordSymbInfo->bNumSymbs;
        ET9SymbInfo *pSymbInfo = &pWordSymbInfo->SymbsInfo[bNumSymbs - 1];
        for (i = 0; i < 3; i++, pSymbInfo--) {
            if (!((pSymbInfo->bNumBaseSyms == 1) &&
                (pSymbInfo->DataPerBaseSym[0].sChar[0] == ET9K_CHUNJIIN_UNI_DOT))) {
                break;
            }
        }
        if (i == 3) {
            ET9DeleteSymbs(pWordSymbInfo, bNumSymbs - 2, 2);
        } 
    }

    eStatus = ET9AWSelLstBuild(&pKLingInfo->sET9AWLingInfo, pbTotalWords, pbDefaultListIndex, pwGestureValue);
    if (ET9STATUS_NONE != eStatus) {
        return eStatus;
    }
    if ( *pbTotalWords > 1 ) {
        ET9BOOL fSameWord = 0;
        ET9AWWordInfo * pWordInfo1 = NULL;
        ET9AWWordInfo * pWordInfo2 = NULL;
        ET9STATUS eStatus1 = __ET9K_GetAWWord(pKLingInfo, 0, &pWordInfo1);

        if ( eStatus1 == ET9STATUS_NONE && pWordInfo1->bWordSource == ET9AWORDSOURCE_NEWWORD) {
            ET9SYMB acHangul[ET9MAXWORDSIZE];
            ET9U16 wTotalLen;

            __ET9K_InPlaceToLower( pWordInfo1->sWord, pWordInfo1->wWordLen );
            bJoinInitConsonants = (ET9BOOL)ET9KJoinInitialJamoConsonants(pKLingInfo);
            eStatus1 = __ET9K_CaseJamo2Hangul(pWordInfo1->sWord, pWordInfo1->wWordLen, acHangul, ET9MAXWORDSIZE, &wTotalLen, bJoinInitConsonants);
            if ( eStatus1 == ET9STATUS_NONE ) {
                ET9U8 b, bDupCount = 0;

                for ( b = 1; b < *pbTotalWords; b++ ) {
                    eStatus1 = __ET9K_GetAWWord(pKLingInfo, b, &pWordInfo2);
                    if ( eStatus1 == ET9STATUS_NONE && pWordInfo1->wWordLen == pWordInfo2->wWordLen) {
                        fSameWord = __SameHangul( acHangul, wTotalLen, pWordInfo2, bJoinInitConsonants); 
                        if ( fSameWord ) {
                            ET9U8 w;

                            if ( pKLingInfo->abIndexMapping[0] == 0 ) {
                                pKLingInfo->abIndexMapping[0] = b;
                                if (*pbDefaultListIndex > b ) { /* Protection only. Probably this is not reachable */
                                    *pbDefaultListIndex = (ET9U8)(*pbDefaultListIndex - 1);
                                }
                            }
                            for ( w = b - bDupCount; w < ET9MAXSELLISTSIZE; w++ ) {
                                pKLingInfo->abIndexMapping[w]++;
                            }
                            bDupCount++;
                        }
                    }
                }
                *pbTotalWords = (ET9U8)(*pbTotalWords - bDupCount);
            }
        }
    }
    return eStatus;
}


/*---------------------------------------------------------------------------*/
/**
 * 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                Succeeded.
 * @return ET9STATUS_NO_INIT             Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY      At least one pointer passed was set to NULL.
 * @return ET9STATUS_OUT_OF_RANGE        Index passed is invalid.
 * @return ET9STATUS_NEED_SELLIST_BUILD  Selection list has not been built with the most recent input.
 */
ET9STATUS ET9FARCALL ET9KSelectHangul(ET9KLingInfo * const pKLingInfo, 
                                      const ET9U8          bHangulIndex)
{
    ET9STATUS eStatus = ET9STATUS_NONE;
    ET9U8 bIndex;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if ( bHangulIndex >= ET9MAXSELLISTSIZE ) {
        return ET9STATUS_OUT_OF_RANGE;
    }

    bIndex = pKLingInfo->abIndexMapping[bHangulIndex];

    eStatus = ET9AWSelLstSelWord( &pKLingInfo->sET9AWLingInfo, bIndex );
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * 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            Succeeded.
 * @return ET9STATUS_NO_INIT         Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY  At least one pointer passed was set to NULL.
 * @return ET9STATUS_BAD_PARAM       One of the parameters is invalid.
 * @return ET9STATUS_FULL            Conversion of Hangul has resulted in a Jamo that exceeds ET9MAXWORDSIZE.
 */
ET9STATUS ET9FARCALL ET9KNoteHangulDone(ET9KLingInfo  * const pKLingInfo, 
                                        const ET9SYMB * const psHangul, 
                                        const ET9U16          wHangulLen)
{
    ET9STATUS eStatus = ET9STATUS_NONE;
    ET9SimpleWord   jamo;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    eStatus = _ET9K_Hangul2Jamo(psHangul, wHangulLen, &jamo, /*bLeadingUpper*/1, pKLingInfo->bEnableCJI);

    if (eStatus == ET9STATUS_NONE) {
        eStatus = ET9AWNoteWordDone( &pKLingInfo->sET9AWLingInfo, jamo.sString, jamo.wLen, (ET9U32)jamo.wLen );
    }
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * 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 address for the output Hangul.
 * @param wHangulBufLen             Size of psHangulBuf (number of ET9SYMB).
 * @param pwHangulLen               Pointer to an address for the length of output Hangul (number of ET9SYMB).
 * @param pWordSource               (OPTIONAL) Pointer to an address to store the source of the word
 *
 * @return ET9STATUS_NONE              Succeeded.
 * @return ET9STATUS_NO_INIT           Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_MEMORY    At least one pointer passed was set to NULL.
 * @return ET9STATUS_OUT_OF_RANGE      Index passed is invalid.
 * @return ET9STATUS_BUFFER_TOO_SMALL  Passed Hangul buffer length is less than ET9MAXWORDSIZE.
 */
ET9STATUS ET9FARCALL ET9KGetHangul(ET9KLingInfo   * const pKLingInfo,
                                   const ET9U8            bHangulIndex,
                                   ET9SYMB        * const psHangulBuf,
                                   const ET9U16           wHangulBufLen,
                                   ET9U16         * const pwHangulLen,
                                   ET9AWORDSOURCE * const pWordSource)
{
    ET9STATUS eStatus;
    ET9U8     bIndex;
    ET9BOOL   bJoinInitConsonants;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    if ( psHangulBuf == NULL || pwHangulLen == NULL ) {
        return ET9STATUS_INVALID_MEMORY;
    }

    *pwHangulLen = 0;

    {
        ET9U8 bTotalWords = 0xFF, bDefaultListIndex = 0xFF;
        ET9AWWordInfo * pWordInfo = NULL;

        if ( bHangulIndex >= ET9MAXSELLISTSIZE ) {
            return ET9STATUS_OUT_OF_RANGE;
        }

        if ( pKLingInfo->sAWLingCmnInfo.Base.bSymbsInfoInvalidated || pKLingInfo->sAWLingCmnInfo.Base.bSelListInvalidated ) {
            ET9U16 wGestureValue;
            eStatus = ET9KBuildSelectionList(pKLingInfo, &bTotalWords, &bDefaultListIndex, &wGestureValue);
            if ( eStatus != ET9STATUS_NONE ) {
                return eStatus;
            }
        }

        bIndex = pKLingInfo->abIndexMapping[bHangulIndex];

        eStatus = __ET9K_GetAWWord(pKLingInfo, bIndex, &pWordInfo);
        if ( ET9STATUS_NONE == eStatus ) {
            ET9U16 wTotalLen = 0;
            ET9SYMB acHangul[ET9MAXWORDSIZE];
            if (pWordSource != NULL) {
                *pWordSource = pWordInfo->bWordSource;
            }
            if ( pWordInfo->bWordSource == ET9AWORDSOURCE_NEWWORD ) {
                __ET9K_InPlaceToLower(pWordInfo->sWord, pWordInfo->wWordLen);
            }
            bJoinInitConsonants = (ET9BOOL)ET9KJoinInitialJamoConsonants(pKLingInfo);
            eStatus = __ET9K_CaseJamo2Hangul( pWordInfo->sWord, pWordInfo->wWordLen, acHangul, ET9MAXWORDSIZE, &wTotalLen, bJoinInitConsonants);

            if ( eStatus == ET9STATUS_NONE ) {
                if ( ET9KOutputCompatibilityJamo(pKLingInfo) ) {
                    ET9KJamoToCompatibilityJamo( acHangul, wTotalLen );
                }
                else {
                    __ET9K_InPlaceToLower( acHangul, wTotalLen );
                }

                if ( wHangulBufLen < wTotalLen ) {
                    return ET9STATUS_BUFFER_TOO_SMALL;
                }
                else {
                    *pwHangulLen = wTotalLen;
                    _ET9SymCopy(psHangulBuf, acHangul, wTotalLen);
                }
            }
        }
    }

    return eStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * 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 combinable initial jamo consonants
 *
 * Compatibility Jamo
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT    system not properly initialized
 */
ET9STATUS ET9FARCALL ET9KEnableInitialConsonantJoin(ET9KLingInfo * const pKLingInfo)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pKLingInfo->bInitialConsonantJoin = 1;
    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * Setup the Korean linguistic module to prevent joining combinable initial jamo consonants
 *
 * @param pKLingInfo           pointer to Korean linguistic module.
 *
 * @return ET9STATUS_NONE       Succeeded
 * @return ET9STATUS_NO_INIT    system not properly initialized
 */
ET9STATUS ET9FARCALL ET9KDisableInitialConsonantJoin(ET9KLingInfo * const pKLingInfo)
{
    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pKLingInfo->bInitialConsonantJoin = 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         Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_INPUT   Cannot change setting when there is input in the buffer
 * @return ET9STATUS_NO_LDB          No LDB currently set. LDB must be initialized first.
 * @return ET9STATUS_LDB_ID_ERROR    LDB switch indicates it is not a Chun-Ji-In LDB.
 */
ET9STATUS ET9FARCALL ET9KEnableChunJiIn(ET9KLingInfo * const pKLingInfo)
{
    ET9STATUS eStatus = ET9STATUS_NONE;
    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 */
            eStatus = ET9STATUS_INVALID_INPUT;
        }
        else {
            /* switch to CJI ldb */
            eStatus = ET9AWLdbSetLanguage(&pKLingInfo->sET9AWLingInfo, (ET9PLIDKorean | ET9SLIDChunJiIn), 0);

            if (eStatus == ET9STATUS_NONE) {
                /* set the flag */
                pKLingInfo->bEnableCJI = 1;
                pKLingInfo->sET9AWLingInfo.pLingCmnInfo->pRUDBInfo = pKLingInfo->pCJIRUDBInfo;
            }
        }
    }
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * 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_NO_INIT        Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 * @return ET9STATUS_INVALID_INPUT  Cannot change setting when there is input in the buffer.
 * @return ET9STATUS_NO_LDB         No LDB currently set. LDB must be initialized first.
 * @return ET9STATUS_LDB_ID_ERROR   LDB switch indicates it is not a Korean Jamo LDB. 
 */
ET9STATUS ET9FARCALL ET9KDisableChunJiIn(ET9KLingInfo * const pKLingInfo)
{
    ET9STATUS eStatus = ET9STATUS_NONE;
    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 */
            eStatus = ET9STATUS_INVALID_INPUT;
        }
        else {
            /* switch to statndard ldb */
            eStatus = ET9AWLdbSetLanguage(&pKLingInfo->sET9AWLingInfo, (ET9PLIDKorean | ET9SLIDDEFAULT), 0);
       
            if (eStatus == ET9STATUS_NONE) {
                /* clear the flag */
                pKLingInfo->bEnableCJI = 0;
                pKLingInfo->sET9AWLingInfo.pLingCmnInfo->pRUDBInfo = pKLingInfo->pJamoRUDBInfo;
            }
        }
    }

    return eStatus;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                             
 *
 *                                                                           
 *                                                                           
  *
 *                                                                    
 */
ET9STATUS ET9LOCALCALL __ET9AWReadMdbData(ET9AWLingInfo * const pAWLing,
                                          const ET9REQMDB       eMdbRequestType,
                                          const ET9U16          wReqWordLen,
                                          const ET9U16          wMaxWordLen,
                                          ET9SYMB       * const psBuildTxtBuf,
                                          ET9U16        * const pwWordLen,
                                          ET9U32        * const pdwWordListIdx )
{
    ET9STATUS eStatus = 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 == eStatus ) {
        ET9SYMB asHangulBuf[ET9MAXWORDSIZE];
        ET9SimpleWord   jamo = {0, 0, {0}};

        do {
            eStatus = pKLingInfo->FctMdbCallBack( pKLingInfo, ET9MAXWORDSIZE, asHangulBuf, pwWordLen, pdwWordListIdx );
            if ( eStatus != ET9STATUS_NONE ) {
                break;
            }
            eStatus = _ET9K_Hangul2Jamo(asHangulBuf, *pwWordLen, &jamo, /*bLeadingUpper*/1, 0);
        } while ( eStatus == ET9STATUS_FULL );

        if ( eStatus == ET9STATUS_NONE && jamo.wLen >= wReqWordLen && jamo.wLen <= wMaxWordLen) {
            _ET9SymCopy(psBuildTxtBuf, jamo.sString, jamo.wLen);
            *pwWordLen = jamo.wLen;
            /* return this word */
            break;
        }
    }

    return eStatus;
}


/*---------------------------------------------------------------------------*/
/**
 * Register a manufacturer database.
 *
 * @param pKLingInfo                 Pointer to Korean information structure.
 * @param MdbReadMdbDataFct          Read MDB function.
 *
 * @return ET9STATUS_NONE Succeeded.
 * @return ET9STATUS_NO_INIT Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 */
ET9STATUS ET9FARCALL ET9KRegisterMDB(ET9KLingInfo    * const pKLingInfo,
                                     const ET9K_MDB_CALLBACK MdbReadMdbDataFct)
{
    ET9STATUS eStatus;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pKLingInfo->FctMdbCallBack = MdbReadMdbDataFct;

    eStatus = ET9AWRegisterMDB( &pKLingInfo->sET9AWLingInfo, __ET9AWReadMdbData );
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * Unregister a manufacturer database.
 *
 * @param pKLingInfo                 Pointer to Korean information structure.
 *
 * @return ET9STATUS_NONE Succeeded.
 * @return ET9STATUS_NO_INIT Korean linguistic module has not been properly initialized. Call ET9KSysInit.
 */
ET9STATUS ET9FARCALL ET9KUnregisterMDB(ET9KLingInfo * const pKLingInfo)
{
    ET9STATUS eStatus;

    ET9_K_CHECK_LINGINFO(pKLingInfo);

    pKLingInfo->FctMdbCallBack = NULL;
    eStatus = ET9AWUnregisterMDB( &pKLingInfo->sET9AWLingInfo );
    return eStatus;
}

/*---------------------------------------------------------------------------*/
/*
 * T9KCheckCompileParameters
 * Check the consistency between core data type and integration.
 */

ET9U32 ET9FARCALL ET9KCheckCompileParameters(ET9U8  * pbET9U8,
                                             ET9U8  * pbET9U16,
                                             ET9U8  * pbET9U32,
                                             ET9U8  * pbET9UINT,
                                             ET9U8  * pbET9S8,
                                             ET9U8  * pbET9S16,
                                             ET9U8  * pbET9S32,
                                             ET9U8  * pbET9INT,
                                             ET9U8  * pbET9SYMB,
                                             ET9U8  * pbET9BOOL,
                                             ET9U8  * pbET9FARDATA,
                                             ET9U8  * pbET9FARCALL,
                                             ET9U8  * pbET9LOCALCALL,
                                             ET9U8  * pbVoidPtr,
                                             ET9U16 * pwET9SymbInfo,
                                             ET9U32 * pdwET9WordSymbInfo,
                                             ET9U8  * pbET9MAXSELLISTSIZE,
                                             ET9U32 * pdwET9KLingInfo)
{
    ET9U32 dwError;

    dwError = ET9_CheckCompileParameters(pbET9U8,
                                         pbET9U16,
                                         pbET9U32,
                                         pbET9UINT,
                                         pbET9S8,
                                         pbET9S16,
                                         pbET9S32,
                                         pbET9INT,
                                         pbET9SYMB,
                                         pbET9BOOL,
                                         pbET9FARDATA,
                                         pbET9FARCALL,
                                         pbET9LOCALCALL,
                                         pbVoidPtr,
                                         pwET9SymbInfo,
                                         pdwET9WordSymbInfo);

    if ((dwError == (ET9U32)ET9NULL_POINTERS) ||
        !(pbET9MAXSELLISTSIZE && pdwET9KLingInfo)) {
        return (ET9U32)ET9NULL_POINTERS;
    }

    if (*pbET9MAXSELLISTSIZE != ET9MAXSELLISTSIZE) {
        *pbET9MAXSELLISTSIZE = ET9MAXSELLISTSIZE;
        dwError |= (1L << ET9WRONGSIZE_ET9MAXSELLISTSIZE);
    }

    if ( *pdwET9KLingInfo != sizeof(ET9KLingInfo) ) {
        *pdwET9KLingInfo = sizeof(ET9KLingInfo);
        dwError |= (1L << ET9WRONGSIZE_ET9KLINGINFO);
    }

    return dwError;
}


/*! \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 ET9K_MINRUDBDATABYTES (20480) bytes.

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 >--------------------------------- */
