/*******************************************************************************
;*******************************************************************************
;**                                                                           **
;**                  COPYRIGHT 2001-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: et9imu.c                                                    **
;**                                                                           **
;**  Description: Generic input module functionality                          **
;**                                                                           **
;*******************************************************************************
;******************************************************************************/

/*! \addtogroup et9imu Functions for XT9 Generic Module
* Generic input for generic XT9.
* @{
*/

#include "et9imu.h"
#include "et9misc.h"
#include "et9sym.h"

#ifdef ET9_ALPHABETIC_MODULE
#include "et9aimu.h"
#endif /* ET9_ALPHABETIC_MODULE */


#ifdef ET9_DEBUGLOG4
#ifdef _WIN32
#pragma message ("*** ET9_DEBUGLOG4 ACTIVATED ***")
#endif
#include <stdio.h>
#include <string.h>
#define WLOG4(q) { if (pLogFile4 == NULL) { pLogFile4 = fopen("zzzET9IMU.txt", "w"); } { q fflush(pLogFile4); } }
static FILE *pLogFile4 = NULL;
#else
#define WLOG4(q)
#endif


/* ************************************************************************************************************** */
/* * LOG SUPPORT ************************************************************************************************ */
/* ************************************************************************************************************** */

#ifdef ET9_DEBUGLOG4

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                      
 *
 *                                                                               
 *                                                 
 *
 *             
 */

static void ET9LOCALCALL __LogInputSymbsIMU(ET9WordSymbInfo    * const pWordSymbInfo,
                                            FILE               * const pfLogFile)
{
    const ET9U8 bCount = pWordSymbInfo->bNumSymbs;

    ET9U16 wIndex;
    ET9INT snLockPos = -1;

    fprintf(pfLogFile, "\n\nINPUT SYMBS\n\n");

    for (wIndex = 0; wIndex < bCount; ++wIndex) {

        ET9SymbInfo * const pSymbInfo = &pWordSymbInfo->SymbsInfo[wIndex];

        if (pSymbInfo->bLocked) {
            snLockPos = wIndex;
        }
    }

    for (wIndex = 0; wIndex < bCount; ++wIndex) {

        ET9U16 wBaseIndex;
        ET9U16 wSymsIndex;

        ET9SymbInfo * const pSymbInfo = &pWordSymbInfo->SymbsInfo[wIndex];

        fprintf(pfLogFile, "Pos %2d ", wIndex);

        switch (pSymbInfo->eInputType)
        {
            case ET9DISCRETEKEY:        fprintf(pfLogFile, "ET9DISCRETEKEY"); break;
            case ET9REGIONALKEY:        fprintf(pfLogFile, "ET9REGIONALKEY"); break;
            case ET9HANDWRITING:        fprintf(pfLogFile, "ET9HANDWRITING"); break;
            case ET9MULTITAPKEY:        fprintf(pfLogFile, "ET9MULTITAPKEY"); break;
            case ET9CUSTOMSET:          fprintf(pfLogFile, "ET9CUSTOMSET"); break;
            case ET9EXPLICITSYM:        fprintf(pfLogFile, "ET9EXPLICITSYM"); break;
            case ET9MULTISYMBEXPLICIT:  fprintf(pfLogFile, "ET9MULTISYMBEXPLICIT"); break;
            default:                    fprintf(pfLogFile, "Input<%d>", pSymbInfo->eInputType); break;
        }

        fprintf(pfLogFile, "  ");

        switch (pSymbInfo->bAmbigType)
        {
            case ET9AMBIG:              fprintf(pfLogFile, "ET9AMBIG"); break;
            case ET9EXACT:              fprintf(pfLogFile, "ET9EXACT"); break;
            default:                    fprintf(pfLogFile, "Ambig<%d>", pSymbInfo->bAmbigType); break;
        }

        fprintf(pfLogFile, "  ");

        switch (pSymbInfo->bSymbType)
        {
            case ET9KTINVALID:          fprintf(pfLogFile, "ET9KTINVALID"); break;
            case ET9KTLETTER:           fprintf(pfLogFile, "ET9KTLETTER"); break;
            case ET9KTPUNCTUATION:      fprintf(pfLogFile, "ET9KTPUNCTUATION"); break;
            case ET9KTNUMBER:           fprintf(pfLogFile, "ET9KTNUMBER"); break;
            case ET9KTSTRING:           fprintf(pfLogFile, "ET9KTSTRING"); break;
            case ET9KTFUNCTION:         fprintf(pfLogFile, "ET9KTFUNCTION"); break;
            case ET9KTSMARTPUNCT:       fprintf(pfLogFile, "ET9KTSMARTPUNCT"); break;
            case ET9KTUNKNOWN:          fprintf(pfLogFile, "ET9KTUNKNOWN"); break;
            default:                    fprintf(pfLogFile, "Symb<%d>", pSymbInfo->bSymbType); break;
        }

        if (pSymbInfo->eShiftState == ET9SHIFT) {
            fprintf(pfLogFile, " SHIFT");
        }
        else if (pSymbInfo->eShiftState == ET9CAPSLOCK) {
            fprintf(pfLogFile, " CAPSLOCK");
        }
        else if (pSymbInfo->bForcedLowercase) {
            fprintf(pfLogFile, " FORCED LOWERCASE");
        }

        switch (pSymbInfo->bLocked)
        {
            case ET9NOLOCK:             break;
            case ET9STEMLOCK:           fprintf(pfLogFile, " STEMLOCK"); break;
            case ET9EXACTLOCK:          fprintf(pfLogFile, " EXACTLOCK"); break;
            default:                    fprintf(pfLogFile, " LOCK<%d>", pSymbInfo->bLocked); break;
        }

        if (pSymbInfo->wKeyIndex != ET9UNDEFINEDKEYVALUE) {
            fprintf(pfLogFile, " (Key %d)", (int)pSymbInfo->wKeyIndex);
        }

        if (pSymbInfo->wTapX != ET9UNDEFINEDTAPVALUE && pSymbInfo->wTapY != ET9UNDEFINEDTAPVALUE) {
            fprintf(pfLogFile, " [%d,%d]", (int)pSymbInfo->wTapX, (int)pSymbInfo->wTapY);
        }

        if (pSymbInfo->eInputType == ET9MULTISYMBEXPLICIT) {
            fprintf(pfLogFile, " (built by %d)", (int)pSymbInfo->wInputIndex);
        }

        fprintf(pfLogFile, " (ext %d %d %3.1f)", (int)pSymbInfo->bTraceProbability, (int)pSymbInfo->bTraceIndex, (float)pSymbInfo->fTraceLen);

        fprintf(pfLogFile, "\n");

        if (wIndex <= snLockPos) {

            ET9SYMB sChar = pSymbInfo->sLockedSymb;

            fprintf(pfLogFile, "  Locked symb ");
            if (sChar > 0x20 && sChar <= 0x7F || sChar >= 0xA1 && sChar <= 0xFF) {
                fprintf(pfLogFile, "%c", sChar);
            }
            else {
                fprintf(pfLogFile, "<%x>", sChar);
            }
            fprintf(pfLogFile, "\n");
        }

        for (wBaseIndex = 0; wBaseIndex < pSymbInfo->bNumBaseSyms; ++wBaseIndex) {

            fprintf(pfLogFile, "  SymbsInfo %2u (%3u %3u)%c  ", wBaseIndex, pSymbInfo->DataPerBaseSym[wBaseIndex].bSymFreq, pSymbInfo->DataPerBaseSym[wBaseIndex].bSymDist, (pSymbInfo->DataPerBaseSym[wBaseIndex].bLimited ? 'L' : ' '));

            for (wSymsIndex = 0; wSymsIndex < pSymbInfo->DataPerBaseSym[wBaseIndex].bNumSymsToMatch; ++wSymsIndex) {
                ET9SYMB sChar = pSymbInfo->DataPerBaseSym[wBaseIndex].sChar[wSymsIndex];
                if (sChar > 0x20 && sChar <= 0x7F || sChar >= 0xA1 && sChar <= 0xFF) {
                    fprintf(pfLogFile, "%c", sChar);
                }
                else {
                    fprintf(pfLogFile, "<%x>", sChar);
                }
            }
            fprintf(pfLogFile, " ");
            for (wSymsIndex = 0; wSymsIndex < pSymbInfo->DataPerBaseSym[wBaseIndex].bNumSymsToMatch; ++wSymsIndex) {
                ET9SYMB sChar = pSymbInfo->DataPerBaseSym[wBaseIndex].sUpperCaseChar[wSymsIndex];
                if (sChar > 0x20 && sChar <= 0x7F || sChar >= 0xA1 && sChar <= 0xFF) {
                    fprintf(pfLogFile, "%c", sChar);
                }
                else {
                    fprintf(pfLogFile, "<%x>", sChar);
                }
            }

            fprintf(pfLogFile, "\n");
        }
    }

    fprintf(pfLogFile, "\n");

    fflush(pfLogFile);
}

#else

#define __LogInputSymbsIMU(pWordSymbInfo, pfLogFile)

#endif

/*******************************************************************************
 **
 **          G L O B A L S   A N D   L O C A L   S T A T I C S
 **
 ** ET9 does not make use of any dynamic global or local static variables!!
 ** It is acceptable to make use of constant globals or local statics.
 ** If you need persistent dynamic memory in the ET9 core, it should be
 ** allocated in the ET9AWLingPrivate data structure and fogged through the definitions
 ** found in the et9asystm.h file.
 **
 ******************************************************************************/

#define ET9MAGICSTR_FIRSTSET 5


#if 0
/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                       
 *
 *                                                                        
 *
 *                                                                    
 */

static void ET9LOCALCALL __LockSymbInfo(ET9WordSymbInfo     * const pWordSymbInfo)

{
    ET9UINT nCount;
    ET9SymbInfo *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return;
    }

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {

        pSymbInfo->sLockedSymb = pSymbInfo->eShiftState ? pSymbInfo->DataPerBaseSym[0].sUpperCaseChar[0] : pSymbInfo->DataPerBaseSym[0].sChar[0];

        pSymbInfo->bLocked = 1;
    }
}
#endif

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                     
 *
 *                                                                        
 *                                                     
 *                                                  
 *
 *             
 */

static void ET9LOCALCALL __ExplicifyTail(ET9WordSymbInfo   * const pWordSymbInfo,
                                         ET9SYMB           * const psSymbs,
                                         const ET9UINT             nSymbCount)
{
    ET9STATUS   wStatus;
    ET9UINT     nCount;
    ET9SYMB     *psSymb;

    ET9Assert(psSymbs);
    ET9Assert(pWordSymbInfo);

    WLOG4(fprintf(pLogFile4, "__ExplicifyTail\n");)

    /* valid */

    if (!pWordSymbInfo->bNumSymbs) {
    }

    /* enter using explicits */

    psSymb = psSymbs;

    for (nCount = nSymbCount; nCount; --nCount, ++psSymb) {

        wStatus = ET9AddExplicitSymb(pWordSymbInfo,
                                     *psSymb,
                                     0,
                                     (_ET9SymIsUpper(*psSymb, pWordSymbInfo->Private.dwLocale) ? ET9SHIFT : ET9NOSHIFT),
                                     ET9_NO_ACTIVE_INDEX);

        if (wStatus) {
            WLOG4(fprintf(pLogFile4, "__ExplicifyTail, explicit symb failed\n");)
        }
    }

    /* potentially prevent catchup builds etc */

    _ET9ContentExplicified(pWordSymbInfo);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                 
 *
 *                                                                                                                                                         
 *                                                                       
 *
 *             
 */

static void ET9LOCALCALL __OnInputBecameEmpty(ET9WordSymbInfo * const pWordSymbInfo, const ET9BOOL bResetRequired)
{
    ET9Assert(!pWordSymbInfo->bNumSymbs);

    WLOG4(fprintf(pLogFile4, "__OnInputBecameEmpty, new input started\n");)

    pWordSymbInfo->Private.bInputRestarted = 1;

    if (bResetRequired) {

        if (pWordSymbInfo->Private.sRequiredWord.wLen) {

            WLOG4(fprintf(pLogFile4, "__OnInputBecameEmpty, clearing required word\n");)

            _ET9InitSimpleWord(&pWordSymbInfo->Private.sRequiredWord);
        }

        pWordSymbInfo->Private.bRequiredLocate = 0;
        pWordSymbInfo->Private.bRequiredVerifyInput = 0;
        pWordSymbInfo->Private.bRequiredInhibitOverride = 0;
        pWordSymbInfo->Private.bRequiredInhibitCorrection = 0;
        pWordSymbInfo->Private.bRequiredHasRegionalInfo = 0;
    }

    if (pWordSymbInfo->Private.eLastShiftState == ET9SHIFT) {
         pWordSymbInfo->Private.eLastShiftState = ET9NOSHIFT;
    }

    pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_OFF;
    pWordSymbInfo->Private.bCompoundingDownshift = 0;

    pWordSymbInfo->Private.eLastInputEvent = ET9InputEvent_none;
    pWordSymbInfo->Private.bClearSymbEpisodeCount = 0;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Clears the most recently added value from the input sequence.
 *
 * @param[in]     pWordSymbInfo   Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the current input sequence.
 *
 * @retval ET9STATUS_NONE                   The function call was handled successfully.
 * @retval ET9STATUS_INVALID_MEMORY         At least one pointer passed as a function argument was set to null.
 * @retval ET9STATUS_EMPTY                  There is no current input sequence (the value of pWordSymbInfo->bNumSymbs is 0).
 * @retval ET9STATUS_NEED_SELLIST_BUILD     Text has been reselected, but the selection list has not been rebuilt.
 *                                          Only returned if calling ET9ClearOneSymb does not clear all symbols of the reselected word.
 *
 * @see ET9ClearAllSymbs()
 */

ET9STATUS ET9FARCALL ET9ClearOneSymb(ET9WordSymbInfo * const pWordSymbInfo)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (!pWordSymbInfo->bNumSymbs) {
        __OnInputBecameEmpty(pWordSymbInfo, 1);
        return ET9STATUS_EMPTY;
    }

    if ((pWordSymbInfo->bNumSymbs > 1) && pWordSymbInfo->Private.bRequiredVerifyInput) {

        /* if reselecting (and not the last symb being deleted), need to build first */

        return ET9STATUS_NEED_SELLIST_BUILD;
    }

    /* potentially explicify on first delete on reselect */

    if (pWordSymbInfo->Private.bRequiredLocate) {

        ET9SimpleWord sTmpWord = pWordSymbInfo->Private.sRequiredWord;  /* needed to not get reset in explicify */

        if (_ET9HasRegionalInfo(pWordSymbInfo) || _ET9HasTraceInfo(pWordSymbInfo)) {

            _ET9ExplicifyWord(pWordSymbInfo, &sTmpWord);

            pWordSymbInfo->Private.bRequiredLocate = 0;
            pWordSymbInfo->Private.bRequiredVerifyInput = 0;
            pWordSymbInfo->Private.bRequiredInhibitOverride = 1;
            pWordSymbInfo->Private.bRequiredInhibitCorrection = 1;
            _ET9InitSimpleWord(&pWordSymbInfo->Private.sRequiredWord);
        }
        else if (_ET9HasDiscreteOnlyInfo(pWordSymbInfo) && sTmpWord.wLen > pWordSymbInfo->bNumSymbs) {

            __ExplicifyTail(pWordSymbInfo, &sTmpWord.sString[pWordSymbInfo->bNumSymbs], (sTmpWord.wLen - pWordSymbInfo->bNumSymbs));

            pWordSymbInfo->Private.bRequiredLocate = 0;
            pWordSymbInfo->Private.bRequiredVerifyInput = 0;
            pWordSymbInfo->Private.bRequiredInhibitOverride = 1;
            pWordSymbInfo->Private.bRequiredInhibitCorrection = 1;
            _ET9InitSimpleWord(&pWordSymbInfo->Private.sRequiredWord);
        }
    }

    /* if clearing all, call clear-all instead */

    {
        const ET9U16 wInputIndex = pWordSymbInfo->SymbsInfo[0].wInputIndex;

        ET9UINT nCount;
        ET9SymbInfo *pSymbInfo;

        pSymbInfo = &pWordSymbInfo->SymbsInfo[0];
        for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {
            if (!wInputIndex || pSymbInfo->wInputIndex != wInputIndex) {
                break;
            }
        }

        if (!nCount || pWordSymbInfo->bNumSymbs == 1) {
            return ET9ClearAllSymbs(pWordSymbInfo);
        }
    }

    /* track */

    _ET9TrackInputEvents(pWordSymbInfo, ET9InputEvent_clear);

    /* clear one will actually clear more than one symbol if it was entered with one key press, for example emoticons */

    _ET9ImminentSymb(pWordSymbInfo, ET9_NO_ACTIVE_INDEX, 0, 0);

    {
        ET9SymbInfo *pSymbInfo = &pWordSymbInfo->SymbsInfo[pWordSymbInfo->bNumSymbs - 1];

        const ET9U16 wInputIndex = pSymbInfo->wInputIndex;

        while (pWordSymbInfo->bNumSymbs > 0) {

            const ET9BOOL bMultiChar = pSymbInfo->wInputIndex ? 1 : 0;

            _ET9ClearMem((ET9U8 *)pSymbInfo, sizeof(ET9SymbInfo));
            --pWordSymbInfo->bNumSymbs;

            if (bMultiChar && pWordSymbInfo->bNumSymbs) {
                --pSymbInfo;
                if (pSymbInfo->wInputIndex != wInputIndex) {
                    break;
                }
            }
            else {
                break;
            }
        }
    }

    _ET9InvalidateSymbInfo(pWordSymbInfo);

    ET9Assert(pWordSymbInfo->bNumSymbs);

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/** \private
 * @brief Actually clears the entire input sequence.
 *
 * @param[in]     pWordSymbInfo        Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the current input sequence.
 * @param[in]     bResetRequired       If the required should be reset or not.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 *
 * @see ET9ClearOneSymb()
 */

static ET9STATUS ET9LOCALCALL __ClearAllSymbs(ET9WordSymbInfo * const pWordSymbInfo, const ET9BOOL bResetRequired)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (pWordSymbInfo->bNumSymbs) {
        pWordSymbInfo->Private.peInputTracks[pWordSymbInfo->Private.bCurrInputTrackIndex] = _ET9HasTraceInfo(pWordSymbInfo) ? ET9InputTrack_trace : ET9InputTrack_tap;
        pWordSymbInfo->Private.bCurrInputTrackIndex = (pWordSymbInfo->Private.bCurrInputTrackIndex + 1) % ET9_INPUT_TRACK_SIZE;
    }

    _ET9ImminentSymb(pWordSymbInfo, ET9_NO_ACTIVE_INDEX, 0, 0);

    _ET9ClearMem((ET9U8 *)pWordSymbInfo->SymbsInfo, (ET9MAXWORDSIZE * sizeof(ET9SymbInfo)));

    pWordSymbInfo->bNumSymbs = 0;

    _ET9InvalidateSelList(pWordSymbInfo);
    _ET9InvalidateSymbInfo(pWordSymbInfo);

    __OnInputBecameEmpty(pWordSymbInfo, bResetRequired);

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Clears the entire input sequence.
 *
 * @param[in]     pWordSymbInfo        Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the current input sequence.
 *
 * @retval ET9STATUS_NONE              The function call was handled successfully.
 * @retval ET9STATUS_INVALID_MEMORY    At least one pointer passed as a function argument was set to null.
 *
 *
 * @see ET9ClearOneSymb()
 */

ET9STATUS ET9FARCALL ET9ClearAllSymbs(ET9WordSymbInfo * const pWordSymbInfo)
{
    return __ClearAllSymbs(pWordSymbInfo, 1);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                    
 *
 *                                                                               
 *                                              
 *
 *             
 */

void ET9FARCALL _ET9TrackInputEvents(ET9WordSymbInfo * const pWordSymbInfo, const ET9InputEvent eInputEvent)
{
    if (eInputEvent == ET9InputEvent_clear && pWordSymbInfo->Private.eLastInputEvent == ET9InputEvent_add) {
        ++pWordSymbInfo->Private.bClearSymbEpisodeCount;    /* ok to roll-over to zero... */
    }

    pWordSymbInfo->Private.eLastInputEvent = eInputEvent;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                 
 *
 *                                                                               
 *                                                                          
 *                                                              
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL ET9DeleteSymbs(ET9WordSymbInfo * const pWordSymbInfo,
                                    const ET9U8             bIndex,
                                    const ET9U8             bCount)
{
    ET9U8 bDstIndex;
    ET9U8 bMoveCount;
    ET9SymbInfo *pSrc;
    ET9SymbInfo *pDst;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }
    if (!pWordSymbInfo->bNumSymbs) {
        /* Make sure autocapping status cleared and downshifting logic disabled if no syms  */
        pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_OFF;
        pWordSymbInfo->Private.bCompoundingDownshift = 0;
        return ET9STATUS_EMPTY;
    }
    if (bIndex >= pWordSymbInfo->bNumSymbs) {
        return ET9STATUS_BAD_PARAM;
    }
    if (bIndex + bCount > pWordSymbInfo->bNumSymbs) {
        return ET9STATUS_BAD_PARAM;
    }
    if (!bCount) {
        return ET9STATUS_NONE;
    }
    /* if reselecting (and all symbs not being deleted), need to build first */
    if ((pWordSymbInfo->bNumSymbs > bCount) && pWordSymbInfo->Private.bRequiredVerifyInput) {
        return ET9STATUS_NEED_SELLIST_BUILD;
    }

    /* copy symbs - if needed */

    pDst = &pWordSymbInfo->SymbsInfo[bIndex];
    pSrc = &pWordSymbInfo->SymbsInfo[bIndex + bCount];

    bDstIndex = bIndex;
    bMoveCount = (ET9U8)(pWordSymbInfo->bNumSymbs - (bIndex + bCount));

    for (; bMoveCount; --bMoveCount, ++pDst, ++pSrc, ++bDstIndex) {
        *pDst = *pSrc;
        _ET9InvalidateOneSymb(pWordSymbInfo, bDstIndex);
    }

    pWordSymbInfo->bNumSymbs = (ET9U8)(pWordSymbInfo->bNumSymbs - bCount);
    if (!pWordSymbInfo->bNumSymbs) {
        /* Make sure autocapping status cleared and downshifting logic disabled if no syms  */
        pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_OFF;
        pWordSymbInfo->Private.bCompoundingDownshift = 0;
    }

    /* invalidate, especially if nothing got moved */

    _ET9InvalidateSymbInfo(pWordSymbInfo);

    /* done */

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                          
 *
 *                                                                               
 *                                                        
 *                                                      
 *                                                            
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL ET9MoveSymbs(ET9WordSymbInfo * const pWordSymbInfo,
                                  const ET9U8             bFromIndex,
                                  const ET9U8             bToIndex,
                                  const ET9U8             bCount)
{
    ET9U8 bIndex;
    ET9U8 bMoveCount;
    ET9SymbInfo *pSrc;
    ET9SymbInfo *pDst;
    ET9SymbInfo *pCurr;
    ET9SymbInfo sTmp;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }
    if (!pWordSymbInfo->bNumSymbs) {
        /* Make sure autocap status is off if no syms */
        pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_OFF;
        /* Make sure downshifting logic disabled if no syms  */
        pWordSymbInfo->Private.bCompoundingDownshift = 0;
        return ET9STATUS_EMPTY;
    }
    if (bFromIndex >= pWordSymbInfo->bNumSymbs) {
        return ET9STATUS_BAD_PARAM;
    }
    if (bFromIndex + bCount > pWordSymbInfo->bNumSymbs) {
        return ET9STATUS_BAD_PARAM;
    }
    if (bToIndex >= pWordSymbInfo->bNumSymbs) {
        return ET9STATUS_BAD_PARAM;
    }
    if (bToIndex + bCount > pWordSymbInfo->bNumSymbs) {
        return ET9STATUS_BAD_PARAM;
    }
    if (!bCount) {
        return ET9STATUS_NONE;
    }
    if (bFromIndex == bToIndex) {
        return ET9STATUS_NONE;
    }
    /* if reselecting, need to build first */
    if (pWordSymbInfo->Private.bRequiredVerifyInput) {
        return ET9STATUS_NEED_SELLIST_BUILD;
    }

    /* assure that unlocked symbols are not moved into a locked area */

    {
        ET9U16          wLockPoint;
        ET9SymbInfo     *pSymbInfo;

        wLockPoint = pWordSymbInfo->bNumSymbs;

        if (wLockPoint) {
            for (pSymbInfo = &pWordSymbInfo->SymbsInfo[wLockPoint - 1]; wLockPoint; --wLockPoint, --pSymbInfo) {                            /* ignore invalid Insure++ warnings */
                if (pSymbInfo->bLocked) {
                    break;
                }
            }
        }

        if (wLockPoint) {
            if (bToIndex < wLockPoint && bFromIndex + bCount > wLockPoint) {
                return ET9STATUS_ERROR;
            }
        }
    }

    /* make an overlapping move into a non overlapping one */

    pDst = &pWordSymbInfo->SymbsInfo[bToIndex];
    pSrc = &pWordSymbInfo->SymbsInfo[bFromIndex];

    if ((pDst > pSrc) && (pDst < pSrc + bCount)) {
        bMoveCount = (ET9U8)(pDst - pSrc);
        pDst = pSrc;
        pSrc += bCount;
    }
    else {
        bMoveCount = bCount;
    }

    /* moving one symbol at a time - most likely case, less complex and most memory preserving */

    if (pDst < pSrc) {
        for (; bMoveCount; --bMoveCount) {
            sTmp = *pSrc;
            for (pCurr = pSrc; pCurr > pDst; --pCurr) {
                *pCurr = *(pCurr-1);
            }
            *pDst = sTmp;
            ++pDst;
            ++pSrc;
        }
    }
    else {
        pSrc += (bMoveCount - 1);
        pDst += (bMoveCount - 1);
        for (; bMoveCount; --bMoveCount) {
            sTmp = *pSrc;
            for (pCurr = pSrc; pCurr < pDst; ++pCurr) {
                *pCurr = *(pCurr+1);
            }
            *pDst = sTmp;
            --pDst;
            --pSrc;
        }
    }

    /* invalidate all affected symbols */

    if (bFromIndex < bToIndex) {
        bIndex = bFromIndex;
    }
    else {
        bIndex = bToIndex;
    }

    for (; bIndex < pWordSymbInfo->bNumSymbs; ++bIndex) {
        _ET9InvalidateOneSymb(pWordSymbInfo, bIndex);
    }

    _ET9InvalidateSymbInfo(pWordSymbInfo);

    /* done */

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Adds an explicitly specified character to the input sequence.
 *
 * @param[in]     pWordSymbInfo         Pointer to the Word Symbol Data Structure (ET9WordSymbInfo),
 *                                      which contains information about the current input sequence.
 * @param[in]     sSymb                 Character to be added.
 * @param[in]     dwTimeMS              Timing info in MS. Optional, can be 0 to indicate no timing available.
 * @param[in]     eShiftState           Shift state to be associated with character in sSymb. Valid values are:<br>
 *                                      <ul><li> ET9NOSHIFT if an unshifted state
 *                                      <li> ET9SHIFT if a shifted state.
 *                                      <li> ET9CAPSLOCK if a caps lock state</ul>
 * @param[in]     bCurrIndexInList      0-based index value that indicates which word in the selection list is currently selected.
 *                                      XT9 locks this word before it adds the specified input value.<br>
 *                                      If there is no current input sequence, set the value of this parameter to ET9_NO_ACTIVE_INDEX.<br>
 *                                      If the integration layer passes as an invalid argument for this parameter, XT9 will still add
 *                                      the new input, but it will not lock the word. Also, it will not return an error status.
 *
 * @retval ET9STATUS_NONE               The function call was handled successfully.
 * @retval ET9STATUS_INVALID_MEMORY     At least one pointer passed as a function argument was set to null.
 * @retval ET9STATUS_FULL               The active word is already the maximum size allowed (ET9MAXWORDSIZE).
 * @retval ET9STATUS_INVALID_TEXT       The explicitly specified character in sSymb is not valid. Examples of invalid characters include white space and unknown characters.
 *
 * @remarks Explicitly-specified characters can include special characters that are not in the core symbol tables.
 */

ET9STATUS ET9FARCALL ET9AddExplicitSymb(ET9WordSymbInfo * const  pWordSymbInfo,
                                        const ET9SYMB            sSymb,
                                        const ET9U32             dwTimeMS,
                                        const ET9INPUTSHIFTSTATE eShiftState,
                                        const ET9U8              bCurrIndexInList)
{
    ET9SymbInfo         *pSymbInfo;
    ET9U8               bNumSymbs;
    ET9DataPerBaseSym   *pDPBSym;

    ET9_UNUSED(dwTimeMS);

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }
    if (pWordSymbInfo->bNumSymbs >= ET9MAXWORDSIZE) {
        return ET9STATUS_FULL;
    }
    if (pWordSymbInfo->Private.bPreventWhiteSpaceInput && _ET9_IsWhiteSpace(sSymb)) {
        return ET9STATUS_INVALID_TEXT;
    }

    _ET9TrackInputEvents(pWordSymbInfo, ET9InputEvent_add);

    _ET9ImminentSymb(pWordSymbInfo, bCurrIndexInList, _ET9_IsPunctChar(sSymb), 1);

    bNumSymbs = (ET9U8)(pWordSymbInfo->bNumSymbs + 1);      /* imminent symb can shange the num symbs count */

    if (bNumSymbs > ET9MAXWORDSIZE) {
        return ET9STATUS_FULL;
    }

    pSymbInfo = &pWordSymbInfo->SymbsInfo[bNumSymbs - 1];

    _ET9ClearMem((ET9U8*)pSymbInfo, sizeof(ET9SymbInfo));

    pDPBSym = pSymbInfo->DataPerBaseSym;

    if (_ET9SymIsUpper(sSymb, pWordSymbInfo->Private.dwLocale)) {
        pDPBSym->sUpperCaseChar[0] = sSymb;
        pDPBSym->sChar[0] = _ET9SymToLower(sSymb, pWordSymbInfo->Private.dwLocale);
    }
    else {
        pDPBSym->sChar[0] = sSymb;
        pDPBSym->sUpperCaseChar[0] = _ET9SymToUpper(sSymb, pWordSymbInfo->Private.dwLocale);
    }

    {
        const ET9SymbClass eClass = _ET9_GetSymbolClass(sSymb);

        switch (eClass)
        {
            case ET9_PunctSymbClass:
                pSymbInfo->bSymbType = ET9KTPUNCTUATION;
                break;
            case ET9_NumbrSymbClass:
                pSymbInfo->bSymbType = ET9KTNUMBER;
                break;
            case ET9_UnassSymbClass:
                pSymbInfo->bSymbType = ET9KTUNKNOWN;
                break;
            default:
                pSymbInfo->bSymbType = ET9KTLETTER;
                break;
        }
    }

    pDPBSym->bSymFreq = 3;
    pSymbInfo->bNumBaseSyms = 1;
    pDPBSym->bNumSymsToMatch = 1;
    pSymbInfo->eInputType = ET9EXPLICITSYM;
    pSymbInfo->eShiftState = eShiftState;
    pSymbInfo->bForcedLowercase = 0;
    pSymbInfo->bAmbigType = (ET9U8)ET9EXACT;
    pSymbInfo->bTraceProbability = 0;
    pSymbInfo->bTraceIndex = 0;
    pSymbInfo->bFreqsInvalidated = 1;

    pSymbInfo->dwKdb1 = 0;
    pSymbInfo->wPage1 = 0;
    pSymbInfo->dwKdb2 = 0;
    pSymbInfo->wPage2  = 0;

    pSymbInfo->wInputIndex = 0;
    pSymbInfo->wKeyIndex = 0;
    pSymbInfo->wTapX = ET9UNDEFINEDTAPVALUE;
    pSymbInfo->wTapY = ET9UNDEFINEDTAPVALUE;
    pSymbInfo->pKdbKey = NULL;

    _ET9InvalidateOneSymb(pWordSymbInfo, (ET9U8)(bNumSymbs - 1));

    pWordSymbInfo->Private.eLastShiftState = pSymbInfo->eShiftState;
    ++pWordSymbInfo->bNumSymbs;

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Adds an ambiguous input value to the active word, specifying all possible characters for the input along with their associated probabilities.
 * If all symbols are classified as punctuation, then the input will be treated as a punctuation key. Otherwise it will behave as a letter key.
 * If all symbols share the same probability, then the input will behave as a discrete input. Otherwise it will behave as regional input.
 *
 * @param[in]     pWordSymbInfo     Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the current input sequence.
 * @param[in]     psSymbs           Pointer to an array of symbols that correspond to the ambiguous input value being entered. The array should not contain any whitespace characters.<br>
 *                                  Also, the integration layer should ensure that the array contains only valid Unicode character values. For more
 *                                  information which values are valid, see the description below.<br>
 *                                  If the array contains more symbols that can be fit, only the ones with the highest probability will be processed.
 *                                  The symbols in the array can be stored in whatever order you prefer.
 *                                  Symbols with the same probability will be treated as alternative symbols. When using alternatives the total number of symbols processed from the array can be higher than ET9MAXBASESYMBS.
 * @param[in]     pbSymbProbs       Pointer to an array of probabilities corresponding to the characters in the array pointed to by psSymbs.
 *                                  Probabilities are expressed as integers ranging from 1 to 255, with higher numbers indicating greater probabilities.<br>
 *                                  Nuance suggests that the sum of all probabilities in the array totals 255.
 * @param[in]     nNumSymbsInSet    Number of characters in the array pointed to by psSymbs. Valid values range from 1 and up.
 * @param[in]     dwTimeMS          Timing info in MS. Optional, can be 0 to indicate no timing available.
 * @param[in]     eShiftState       Shift state to be associated with the characters in psSymbs. Valid values are.
 *                                  <ul><li> ET9NOSHIFT if an unshifted state
 *                                      <li> ET9SHIFT if a shifted state.
 *                                      <li> ET9CAPSLOCK if a caps lock state</ul>
 * @param[in]     bCurrIndexInList  0-based index value that indicates which word in the selection list is currently selected. XT9 locks this word before it adds the specified input value.<br>
 *                                  If there is no current input sequence, set the value of this parameter to ET9_NO_ACTIVE_INDEX.<br>
 *                                  If the integration layer passes as an invalid argument for this parameter, XT9 will still add the new input,
 *                                  but it will not lock the word. Also, it will not return an error status.
 *
 *
 * @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_BAD_PARAM          One of the probabilities in pnSymbProbs is zero. Ensure all probabilities have a value ranging from 1 to 255.
 * @retval ET9STATUS_FULL               The ambiguous input value cannot be appended to the active word because the active word is already the maximum size allowed (ET9MAXWORDSIZE).
 * @retval ET9STATUS_INVALID_TEXT       At least one of the characters in psSymbs is not valid. Examples of invalid characters include white space and unknown characters.
 * @retval ET9STATUS_OUT_OF_RANGE       Parameter nNumSymbsInSet is set to 0.
 *
 * @remarks
 * This function enables customers to bypass XT9's input modules when adding ambiguous values to an input sequence.<br>
 * The integration layer must ensure that all the symbols in psSymbs are supported by the XT9 core.
 */

ET9STATUS ET9FARCALL ET9AddCustomSymbolSet(ET9WordSymbInfo * const  pWordSymbInfo,
                                           ET9SYMB * const          psSymbs,
                                           ET9U8   * const          pbSymbProbs,
                                           const ET9UINT            nNumSymbsInSet,
                                           const ET9U32             dwTimeMS,
                                           const ET9INPUTSHIFTSTATE eShiftState,
                                           const ET9U8              bCurrIndexInList)
{
    ET9_UNUSED(dwTimeMS);

    WLOG4(fprintf(pLogFile4, "ET9AddCustomSymbolSet, nNumSymbsInSet = %d\n", nNumSymbsInSet);)

    if (!pWordSymbInfo || !psSymbs || !pbSymbProbs || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }
    if (!nNumSymbsInSet) {
        return ET9STATUS_OUT_OF_RANGE;
    }
    if (pWordSymbInfo->bNumSymbs >= ET9MAXWORDSIZE) {
        return ET9STATUS_FULL;
    }
    if (pWordSymbInfo->Private.bPreventWhiteSpaceInput && _ET9FindSpacesAndUnknown(psSymbs, nNumSymbsInSet)) {
        return ET9STATUS_INVALID_TEXT;
    }

    {
        ET9UINT     nCount;
        ET9U8       *pbProb = pbSymbProbs;

        for (nCount = nNumSymbsInSet; nCount; --nCount, ++pbProb) {
            if (!*pbProb) {
                return ET9STATUS_BAD_PARAM;
            }
        }
    }

    _ET9TrackInputEvents(pWordSymbInfo, ET9InputEvent_add);

    _ET9ImminentSymb(pWordSymbInfo, bCurrIndexInList, 0, 1);

    if (pWordSymbInfo->bNumSymbs + 1 > ET9MAXWORDSIZE) {
        return ET9STATUS_FULL;
    }

    {
        ET9SymbInfo         * const pSymbInfo = &pWordSymbInfo->SymbsInfo[pWordSymbInfo->bNumSymbs];

        ET9DataPerBaseSym   *pDPBSym;

        ET9UINT             nCount;
        ET9UINT             nProbCount;
        ET9UINT             nCurrProb;
        ET9UINT             nNextProb;
        ET9BOOL             bAllPuncts;
        ET9SYMB             *psSymb;
        ET9U8               *pbProb;

        nProbCount = 0;
        bAllPuncts = 1;

        pSymbInfo->bNumBaseSyms = 1;

        pDPBSym = pSymbInfo->DataPerBaseSym;
        pDPBSym->bSymFreq = 0;
        pDPBSym->bNumSymsToMatch = 0;

        /* iterate all given probabilities, highest first */

        for (nCurrProb = 0xFFFF; nCurrProb && pSymbInfo->bNumBaseSyms < ET9MAXBASESYMBS; nCurrProb = nNextProb) {

            WLOG4(fprintf(pLogFile4, "ET9AddCustomSymbolSet, nCurrProb = %d\n", nCurrProb);)

            psSymb = psSymbs;
            pbProb = pbSymbProbs;
            nNextProb = nCurrProb;

            if (nCurrProb <= 0xFF) {
                ++nProbCount;
            }

            /* pick up all symbols of the current probability */

            for (nCount = nNumSymbsInSet; nCount && pSymbInfo->bNumBaseSyms <= ET9MAXBASESYMBS; --nCount, ++psSymb, ++pbProb) {

                /* keep track of (find) next highest probability */

                if (*pbProb < nCurrProb && (nNextProb == nCurrProb || *pbProb > nNextProb)) {
                    nNextProb = *pbProb;
                }

                /* skip if not the active probability */

                if (*pbProb != nCurrProb) {
                    continue;
                }

                /* move to next base symb if full or new probability */

                if (pDPBSym->bNumSymsToMatch >= ET9MAXALTSYMBS || pDPBSym->bSymFreq != nCurrProb) {

                    if (pSymbInfo->bNumBaseSyms >= ET9MAXBASESYMBS) {
                        break;
                    }

                    /* only move if not empty */

                    if (pDPBSym->bNumSymsToMatch) {
                        ++pDPBSym;
                        ++pSymbInfo->bNumBaseSyms;
                    }

                    /* fresh start */

                    pDPBSym->bSymFreq = (ET9U8)nCurrProb;
                    pDPBSym->bNumSymsToMatch = 0;
                }

                WLOG4(fprintf(pLogFile4, "ET9AddCustomSymbolSet, adding, symb = %x (%c)\n", *psSymb, (char)*psSymb);)

                if (bAllPuncts && !_ET9_IsPunctChar(*psSymb)) {
                    bAllPuncts = 0;
                }

                if (_ET9SymIsUpper(*psSymb, pWordSymbInfo->Private.dwLocale)) {
                    pDPBSym->sUpperCaseChar[pDPBSym->bNumSymsToMatch] = *psSymb;
                    pDPBSym->sChar[pDPBSym->bNumSymsToMatch] = _ET9SymToLower(*psSymb, pWordSymbInfo->Private.dwLocale);
                }
                else {
                    pDPBSym->sChar[pDPBSym->bNumSymsToMatch] = *psSymb;
                    pDPBSym->sUpperCaseChar[pDPBSym->bNumSymsToMatch] = _ET9SymToUpper(*psSymb, pWordSymbInfo->Private.dwLocale);
                }

                ++pDPBSym->bNumSymsToMatch;
            }

            if (nNextProb >= nCurrProb) {
                break;
            }
        }

        /* assign symb type, either single punct, smart punct or letter */

        if (bAllPuncts) {
            pSymbInfo->bSymbType = ET9KTPUNCTUATION;
        }
        else {
            pSymbInfo->bSymbType = ET9KTLETTER;
        }

        /* input type is based on the number of probabilities */

        pSymbInfo->eInputType = (nProbCount == 1 ? ET9DISCRETEKEY : ET9REGIONALKEY);

        /* always ambig */

        pSymbInfo->bAmbigType = (ET9U8)ET9AMBIG;

        /* other attributes */

        pSymbInfo->eShiftState = eShiftState;
        pSymbInfo->bForcedLowercase = 0;
        pSymbInfo->wInputIndex = 0;

        pSymbInfo->wKeyIndex = 0xFFFF;              /* for reselect */
        pSymbInfo->wTapX = ET9UNDEFINEDTAPVALUE;
        pSymbInfo->wTapY = ET9UNDEFINEDTAPVALUE;
        pSymbInfo->pKdbKey = NULL;
        pSymbInfo->bTraceProbability = 0;
        pSymbInfo->bTraceIndex = 0;
        pSymbInfo->bFreqsInvalidated = 1;

        pSymbInfo->dwKdb1 = 0;
        pSymbInfo->wPage1 = 0;
        pSymbInfo->dwKdb2 = 0;
        pSymbInfo->wPage2 = 0;

        _ET9InvalidateOneSymb(pWordSymbInfo, pWordSymbInfo->bNumSymbs);
        ++pWordSymbInfo->bNumSymbs;
        pWordSymbInfo->Private.eLastShiftState = pSymbInfo->eShiftState;
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                            
 *
 *                                                                        
 *                                                               
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL _ET9LockWord(ET9WordSymbInfo       * const pWordSymbInfo,
                                  ET9SimpleWord         * const pWord)

{
    ET9U16             i;
    ET9U16             wCount;
    ET9U8              bInitialNumSymbs;
    ET9SYMB            *psSymb;
    ET9INPUTSHIFTSTATE eShiftState;

    if (!pWord || !pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (pWord->wLen == 0 || pWord->wLen > ET9MAXWORDSIZE || pWord->wCompLen > ET9MAXWORDSIZE) {
        return ET9STATUS_BAD_PARAM;
    }

    /* if reselecting, need to build first */

    if (pWordSymbInfo->Private.bRequiredVerifyInput) {
        return ET9STATUS_NEED_SELLIST_BUILD;
    }

    /* validate incoming string, zero is really bad... */

    psSymb = pWord->sString;

    for (wCount = pWord->wLen; wCount; --wCount, ++psSymb) {
        if (pWordSymbInfo->Private.bPreventWhiteSpaceInput && _ET9_IsWhiteSpace(*psSymb)) {
            return ET9STATUS_INVALID_TEXT;
        }
    }

    /* get shift state */

    if (pWordSymbInfo->bNumSymbs) {
        eShiftState = (pWordSymbInfo->Private.eLastShiftState == ET9CAPSLOCK ? ET9CAPSLOCK : ET9NOSHIFT);
    }
    else {
        eShiftState = ET9NOSHIFT;
    }

    /* can't lock trace info - explicifying instead */

    if (_ET9HasTraceInfo(pWordSymbInfo)) {

        const ET9U16 wLen = pWord->wLen;
        const ET9U16 wCompLen = pWord->wCompLen;

        const ET9U8 bTraceIndex = pWordSymbInfo->SymbsInfo[pWordSymbInfo->bNumSymbs - 1].bTraceIndex;

        if (wCompLen) {
            pWord->wLen = (ET9U16)(wLen - wCompLen);
            pWord->wCompLen = 0;
        }

        _ET9ExplicifyWord(pWordSymbInfo, pWord);

        if (pWordSymbInfo->bNumSymbs && bTraceIndex) {
            pWordSymbInfo->SymbsInfo[pWordSymbInfo->bNumSymbs - 1].bTraceLockPoint = 1;
        }

        pWord->wLen = wLen;
        pWord->wCompLen = wCompLen;
    }

    /* initial num symbs */

    bInitialNumSymbs = pWordSymbInfo->bNumSymbs;

    /* remove symbs if word is shorter */

    while (pWord->wLen < pWordSymbInfo->bNumSymbs) {

        ET9ClearOneSymb(pWordSymbInfo);
    }

    /* perform lock */

    for (i = 0; i < pWord->wLen; ++i) {

        /* add missing and assign lock point */

        if (i >= pWordSymbInfo->bNumSymbs) {

            /* fill in the pSymbInfos here */
            /* DO _NOT_ CHANGE THE 'ET9_NO_ACTIVE_WORD' PARAMETER IN THE FOLLOWING CALL! */

            ET9AddExplicitSymb(pWordSymbInfo, pWord->sString[i], 0, eShiftState, ET9_NO_ACTIVE_INDEX);
            _ET9InvalidateOneLock(pWordSymbInfo, (ET9U8)i);
        }

        /* locked symbs must be assigned here */

        pWordSymbInfo->SymbsInfo[i].sLockedSymb = pWord->sString[i];
    }

    /* the normal lock (at the end) */

    pWordSymbInfo->SymbsInfo[pWord->wLen - 1].bLocked = (ET9U8)ET9STEMLOCK;

    /* words with completion should have the completion start point tagged for lock as well,
       but adjusted if there already are symbols in place */

    if (pWord->wCompLen && pWord->wLen > pWord->wCompLen && pWord->wLen > bInitialNumSymbs) {

        ET9INT snTagPoint = pWord->wLen - pWord->wCompLen;

        if (snTagPoint < bInitialNumSymbs) {
            snTagPoint = bInitialNumSymbs;
        }

        ET9Assert(snTagPoint <= pWordSymbInfo->bNumSymbs);

        pWordSymbInfo->SymbsInfo[snTagPoint - 1].bLocked = (ET9U8)ET9STEMLOCK;
    }

    /* invalidate */

    _ET9InvalidateSymbInfo(pWordSymbInfo);

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Locks the word at the indicated index position in the current selection list, allowing the word to be "frozen" as the stem to which additional characters can be appended.
 *
 * @param[in]     pWordSymbInfo             Pointer to the Word Symbol Data Structure (ET9WordSymbInfo).
 * @param[in]     pWord                     Pointer to an instance of ET9AWWordInfo, which contains the word to be locked.
 *
 * @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_BAD_PARAM          Values in ET9AWWordInfo are incorrect.
 * @retval ET9STATUS_INVALID_TEXT       Values in ET9AWWordInfo are incorrect.
 * @retval ET9STATUS_NEED_SELLIST_BUILD Text has been reselected, but the selection list has not been rebuilt.
 *
 * @remarks To lock a word, the selection list must be valid and the input cannot be manipulated before being locked.<br>
 * If locking a stem from reselected text, the integration layer must update the selection list prior to calling ET9LockWord by calling ET9AWSelLstBuild
 * to request that XT9 rebuild the list and then calling ET9AWSelLstGetWord one or more times to retrieve words from the list.
 */

ET9STATUS ET9FARCALL ET9LockWord(ET9WordSymbInfo * const pWordSymbInfo,
                                 ET9SimpleWord   * const pWord)

{
    return _ET9LockWord(pWordSymbInfo, pWord);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                
 *
 *                                                                        
 *                                                                 
 *                                              
 *                                                
 *                                                                                           
 *                                                                                                               
 *
 *                                                                    
 */

static ET9STATUS ET9LOCALCALL __ET9GetExactWordStem(ET9WordSymbInfo      * const pWordSymbInfo,
                                                    ET9SimpleWord        * const pWord,
                                                    const ET9U16                 wIndex,
                                                    const ET9UINT                nLength,
                                                    const ET9CONVERTSYMBCALLBACK pConvertSymb,
                                                    void                 * const pConvertSymbInfo)
{
    ET9STATUS           wStatus = ET9STATUS_NONE;
    ET9SYMB             *pStr;
    ET9INT              nLockIdx = -1;
    ET9U8               i;
    ET9INT              j;
    ET9INT              nStart;
    ET9DataPerBaseSym   *pDataPerBaseSym;
    ET9U8               bType;
    ET9SymbInfo         *pSymbInfo;

    ET9Assert(pWord);
    ET9Assert(pWordSymbInfo);
    ET9Assert(wIndex + nLength <= pWordSymbInfo->bNumSymbs);

    WLOG4(fprintf(pLogFile4, "__ET9GetExactWordStem\n");)

    __LogInputSymbsIMU(pWordSymbInfo, pLogFile4);

    pWord->wLen = 0;
    pWord->wCompLen = 0;

    if (!nLength) {
        return ET9STATUS_NO_MATCHING_WORDS;
    }

    pStr = pWord->sString;

    /* locate lock */

    j = wIndex + nLength;
    pSymbInfo =  &pWordSymbInfo->SymbsInfo[j];
    while (--j >= wIndex) {
        --pSymbInfo;
        if (pSymbInfo->bLocked) {
            nLockIdx = j;
            break;
        }
    }

    /* get locked symbs */

    pSymbInfo =  &pWordSymbInfo->SymbsInfo[wIndex];
    for (j = wIndex; j <= nLockIdx; ++j, ++pSymbInfo) {
        *pStr++ = pSymbInfo->sLockedSymb;
        ++pWord->wLen;
        ET9Assert(*(pStr-1) && *(pStr-1) != (ET9SYMB)0xcccc);
    }

    /* get non locked symbs */

    nStart = (nLockIdx < 0) ? wIndex : nLockIdx + 1;

    pSymbInfo = &pWordSymbInfo->SymbsInfo[nStart];
    for (j = nStart; j < (ET9INT)(wIndex + nLength); ++j, ++pSymbInfo) {

        if (pSymbInfo->bTraceIndex) {
            pWord->wLen = 0;
            return ET9STATUS_NO_MATCHING_WORDS;
        }

        pDataPerBaseSym = pSymbInfo->DataPerBaseSym;

        if (pDataPerBaseSym->bDefaultCharIndex == 0xFF) {
            pWord->wLen = 0;
            return ET9STATUS_NO_MATCHING_WORDS;
        }

        bType = pSymbInfo->bSymbType;

        if (bType == ET9KTSTRING) {
            for (i = 0; i < pDataPerBaseSym->bNumSymsToMatch; ++i) {
                *pStr++ = pDataPerBaseSym->sChar[i];
                ++pWord->wLen;
                ET9Assert(*(pStr-1) && *(pStr-1) != (ET9SYMB)0xcccc);
            }
        }
        else if (pSymbInfo->eShiftState) {
            *pStr++ = pDataPerBaseSym->sUpperCaseChar[0];
            ++pWord->wLen;
            ET9Assert(*(pStr-1) && *(pStr-1) != (ET9SYMB)0xcccc);
        }
        else {
            *pStr++ = pDataPerBaseSym->sChar[0];
            ++pWord->wLen;
            ET9Assert(*(pStr-1) && *(pStr-1) != (ET9SYMB)0xcccc);
        }
    }

    /* apply convert function for proper symbols */

    if (pConvertSymb != NULL) {

        ET9U16 wCount = pWord->wLen;
        ET9SYMB *pSymb = pWord->sString;

        for (; wCount; --wCount, ++pSymb) {
            pConvertSymb(pConvertSymbInfo, pSymb);
        }
    }

#ifdef ET9_DEBUG
    {
        /* verify that all chars are valid (non zero) */

        ET9U16 wCount = pWord->wLen;
        ET9SYMB *pSymb = pWord->sString;

        while (wCount--) {
            ET9Assert(*pSymb && *pSymb != (ET9SYMB)0xcccc);
            ++pSymb;
        }
    }
#endif

    return wStatus;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Retrieves the exact word--the word that most exactly matches the input sequence.
 *
 * @param[in]     pWordSymbInfo         Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the current input sequence.
 * @param[out]    pWord                 Pointer to an instance of ET9AWWordInfo, which contains the word that most exactly matches the input sequence. The word is stored in ET9AWWordInfo.sWord.
 * @param[out]    pConvertSymb          Pointer to ET9CONVERTSYMBCALLBACK or NULL.
 * @param[in]     pConvertSymbInfo      Pointer passed back by ET9CONVERTSYMBCALLBACK or NULL.
 *
 * @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_WORD            There is no current active word or input sequence.
 */

ET9STATUS ET9FARCALL ET9GetExactWord(ET9WordSymbInfo      * const  pWordSymbInfo,
                                     ET9SimpleWord        * const  pWord,
                                     const ET9CONVERTSYMBCALLBACK pConvertSymb,
                                     void                 * const pConvertSymbInfo)
{
    ET9STATUS wStatus;

    if (!pWord || !pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    wStatus = __ET9GetExactWordStem(pWordSymbInfo, pWord, 0, pWordSymbInfo->bNumSymbs, pConvertSymb, pConvertSymbInfo);

    return wStatus;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                              
 *
 *                                                           
 *                                                
 *
 *                                              
 */

static ET9BOOL ET9LOCALCALL __ET9_IsSentenceInitPunct(ET9WordSymbInfo     const * const pWordSymbInfo,
                                                      const ET9SYMB                     sPunct)
{
    ET9SYMB const *psTable;
    ET9UINT nTableSize;

    ET9Assert(pWordSymbInfo);

    switch (pWordSymbInfo->Private.dwLocale & ET9PLIDMASK)
    {
        case 0: /* compiler warning fix */
        default:
            {
                /* 0xBF   = upside-down ? */
                /* 0xA1   = upside-down ! */
                /* 0x17d9 = KHMER SIGN PHNAEK MUAN */

                static const ET9SYMB psStandardSentenceInitPunct[] = { 0x0022, 0x0028, 0x005b, 0x007b, 0x00bf, 0x00a1, 0x17d9 };

                psTable = psStandardSentenceInitPunct;
                nTableSize = sizeof(psStandardSentenceInitPunct) / sizeof(ET9SYMB);
            }
            break;
    }

    {
        ET9UINT nCount;
        ET9SYMB const * psSymb;

        psSymb = psTable;
        for (nCount = nTableSize; nCount; --nCount, ++psSymb) {
            if (sPunct == *psSymb) {
                return 1;
            }
        }
    }

    return 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                  
 *
 *                                                           
 *                                                
 *
 *                                               
 */

static ET9BOOL ET9LOCALCALL __ET9_IsSentenceTermPunct(ET9WordSymbInfo     const * const pWordSymbInfo,
                                                      const ET9SYMB                     sPunct)
{
    ET9SYMB const *psTable;
    ET9UINT nTableSize;

    ET9Assert(pWordSymbInfo);

    switch (pWordSymbInfo->Private.dwLocale & ET9PLIDMASK)
    {
        case ET9PLIDGreek:
            {
                /* . ? ! ; Greek Question Mark   ARABIC QUESTION MARK  ARABIC FULL STOP  DEVANAGARI DANDA   DEVANAGARI DOUBLE DANDA*/
                /* SINHALA PUNCTUATION KUNDDALIYA  THAI CHARACTER ANGKHANKHU   THAI CHARACTER KHOMUT  TIBETAN MARK SHAD          */
                /* TIBETAN MARK NYIS SHAD   MYANMAR SIGN LITTLE SECTION    MYANMAR SIGN SECTION    GEORGIAN PARAGRAPH SEPARATOR  */
                /* ETHIOPIC FULL STOP   ETHIOPIC PREFACE COLON  ETHIOPIC QUESTION MARK  ETHIOPIC PARAGRAPH SEPARATOR             */
                /* KHMER SIGN KHAN    KHMER SIGN BARIYOOSAN   KHMER SIGN KOOMUUT                                                 */

                static const ET9SYMB psGreekSentenceTermPunct[] =
                   { 0x002e, 0x003f, 0x0021, 0x003b, 0x037e, 0x061f, 0x06d4, 0x0964, 0x0965, 0x0df4, 0x0e5a, 0x0e5b, 0x0f0d, 0x0f0e, 0x104a, 0x104b,
                     0x10fb, 0x1362, 0x1366, 0x1367, 0x1368, 0x17d4, 0x17d5, 0x17da };

                psTable = psGreekSentenceTermPunct;
                nTableSize = sizeof(psGreekSentenceTermPunct) / sizeof(ET9SYMB);
            }
            break;

        default:
            {
                /* . ? ! Greek Question Mark   ARABIC QUESTION MARK  ARABIC FULL STOP  DEVANAGARI DANDA   DEVANAGARI DOUBLE DANDA*/
                /* SINHALA PUNCTUATION KUNDDALIYA  THAI CHARACTER ANGKHANKHU   THAI CHARACTER KHOMUT  TIBETAN MARK SHAD          */
                /* TIBETAN MARK NYIS SHAD   MYANMAR SIGN LITTLE SECTION    MYANMAR SIGN SECTION    GEORGIAN PARAGRAPH SEPARATOR  */
                /* ETHIOPIC FULL STOP   ETHIOPIC PREFACE COLON  ETHIOPIC QUESTION MARK  ETHIOPIC PARAGRAPH SEPARATOR             */
                /* KHMER SIGN KHAN    KHMER SIGN BARIYOOSAN   KHMER SIGN KOOMUUT                                                 */

                static const ET9SYMB psStandardSentenceTermPunct[] =
                   { 0x002e, 0x003f, 0x0021, 0x037e, 0x061f, 0x06d4, 0x0964, 0x0965, 0x0df4, 0x0e5a, 0x0e5b, 0x0f0d, 0x0f0e, 0x104a, 0x104b, 0x10fb,
                     0x1362, 0x1366, 0x1367, 0x1368, 0x17d4, 0x17d5, 0x17da };

                psTable = psStandardSentenceTermPunct;
                nTableSize = sizeof(psStandardSentenceTermPunct) / sizeof(ET9SYMB);
            }
            break;
    }

    {
        ET9UINT nCount;
        ET9SYMB const * psSymb;

        psSymb = psTable;
        for (nCount = nTableSize; nCount; --nCount, ++psSymb) {
            if (sPunct == *psSymb) {
                return 1;
            }
        }
    }

    return 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                                             
 *
 *                                                                                                      
 *                                                          
 *                                                           
 *
 *                                              
 */

ET9BOOL ET9FARCALL _ET9IsAutoCapSituation(ET9WordSymbInfo const * const pWordSymbInfo,
                                          ET9SYMB         const * const psString,
                                          const ET9U16                  wStringLen)
{
    ET9BOOL bAutoCap = 0;
    ET9UINT nPunctDetected = 0;
    ET9UINT nInitPunctDetected = 0;
    ET9UINT nWhitespaceDetected = 0;

    ET9SYMB const * psSymb;

    /* if no symbols , assume beginning of field - AUTOCAP SITUATION */

    if (!wStringLen) {
        return 1;
    }

    /* otherwise work backwards through buffer */

    for (psSymb = &psString[wStringLen - 1]; psSymb >= psString; --psSymb) {

        const ET9SymbClass eSymbClass = _ET9_GetSymbolClass(*psSymb);

        /* if the symbol is whitespace... */

        if (eSymbClass == ET9_WhiteSymbClass) {

            /* if sequence already had punct*/

            if (nPunctDetected) {

                /* For now, this is weird, intervening punct  */

                break;
            }
            else {

                ++nWhitespaceDetected;

                /* if only whitespace returned */

                if (nWhitespaceDetected + nInitPunctDetected == wStringLen) {
                    bAutoCap = 1;
                    break;
                }
            }
        }

        /* if the symbol is punctuation */

        else if (eSymbClass == ET9_PunctSymbClass){

            /* track it with counter */

            ++nPunctDetected;

            /* should violate sequence if no intervening whitespace */

            if (!nWhitespaceDetected) {

                /* UNLESS it's 'sentence initiating' punctuation */

                if (!__ET9_IsSentenceInitPunct(pWordSymbInfo, *psSymb)) {
                    break;
                }

                /* if sentence initiating punct already found, weird.. skip out */

                else if (nInitPunctDetected) {
                    break;
                }

                /* otherwise don't record sentence inititating punct as punctuation */

                else {

                    /* if no other syms, consider this a autocap situation */

                    if (wStringLen == 1) {
                        bAutoCap = 1;
                        break;
                    }

                    /* otherwise skip over it, keep checking */

                    else {
                        ++nInitPunctDetected;
                        --nPunctDetected;
                    }
                }
            }

            /* if sequence had whitespace following sentence terminating punctuation, */
            /* consider it an AUTOCAP SITUATION                                       */

            else if (__ET9_IsSentenceTermPunct(pWordSymbInfo, *psSymb)) {
                bAutoCap = 1;
                break;
            }
        }

        /* if ET9SYMNUMBRMASK/ET9SYMALPHAMASK/ET9SYMUNKNMASK, get out */

        else {
            break;
        }
    }

    return bAutoCap;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief This function evaluates whether shift should be used for a 'start-of-sentence' scenario.
 *
 * @param[in]     pWordSymbInfo             Pointer to the Word Symbol Data Structure (ET9WordSymbInfo).
 * @param[in]     pbAutoCap                 Pointer to yes/no for applying autocap shift.
 * @param[in]     pBufferRead               Pointer to a (callback) buffer retrieval routine.
 * @param[in]     pBufferReadInfo           Integeration layer pointer that will be passed back in the callback, or NULL.
 *
 * @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.
 *
 * @remarks This function also determines whether a compounding situation exists in order to force downshifting.
 */

ET9STATUS ET9FARCALL ET9GetAutoCapSituation(ET9WordSymbInfo * const pWordSymbInfo,
                                            ET9BOOL         *       pbAutoCap,
                                            const ET9BUFFERREADCALLBACK pBufferRead,
                                            void            * const pBufferReadInfo)
{
    ET9STATUS wStatus = ET9STATUS_NONE;
    ET9SYMB   psBuffer[ET9AUTOCAP_READ_SIZE];
    ET9U16    wSymsRead = 0;

    if (!pBufferRead || !pbAutoCap || !pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    /* assume this is NOT an autocap situation */

    *pbAutoCap = 0;

    if (pWordSymbInfo->bNumSymbs <= 1 && !ET9CAPS_MODE(pWordSymbInfo->dwStateBits)) {

        /* use callback to retrieve buffer data */

        wStatus = pBufferRead(pBufferReadInfo, ET9AUTOCAP_READ_SIZE, psBuffer, &wSymsRead);

        if (!wStatus) {

            *pbAutoCap = _ET9IsAutoCapSituation(pWordSymbInfo, psBuffer, wSymsRead);

            if (*pbAutoCap) {
                pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_PENDING;
            }
        }
    }

    /* if not an autocap situation, no current word (but syms read), and no postshift mandated */

    if (!*pbAutoCap &&
        !wStatus &&
        wSymsRead &&
        !pWordSymbInfo->bNumSymbs &&
        !ET9CAPS_MODE(pWordSymbInfo->dwStateBits) &&
        pWordSymbInfo->Private.eCurrPostShiftMode == ET9POSTSHIFTMODE_DEFAULT) {

        /* look at sym immediately before cursor */

        if (_ET9_GetSymbolClass(psBuffer[wSymsRead-1]) != ET9_WhiteSymbClass) {

            /* if this is word being compounded, keep flag */

            pWordSymbInfo->Private.bCompoundingDownshift = 1;
        }
        else{
            pWordSymbInfo->Private.bCompoundingDownshift = 0;
        }
    }

    return wStatus;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                 
 *                                                                            
 *
 *                                                                        
 *                                                                   
 *                                                        
 *                                                         
 *
 *             
 */

void ET9FARCALL _ET9ImminentSymb(ET9WordSymbInfo    * const pWordSymbInfo,
                                 const ET9U8                bCurrIndexInList,
                                 const ET9BOOL              bImplicitLock,
                                 const ET9BOOL              bAddingSymb)
{
    ET9Assert(pWordSymbInfo);
    ET9_UNUSED(bImplicitLock); /* in some cases */

    WLOG4(fprintf(pLogFile4, "_ET9ImminentSymb, bNumSymbs = %d\n", pWordSymbInfo->bNumSymbs);)

    pWordSymbInfo->Private.bCurrSelListIndex = bCurrIndexInList;

    /* handle "imminent symb" - edition specific */

#ifdef ET9_ALPHABETIC_MODULE

    if (pWordSymbInfo->Private.ppEditionsList[ET9EDITION_AW_1]) {
        _ET9AWImminentSymb(pWordSymbInfo->Private.ppEditionsList[ET9EDITION_AW_1], bImplicitLock, bAddingSymb);
    }

    if (pWordSymbInfo->Private.ppEditionsList[ET9EDITION_AW_2]) {
        _ET9AWImminentSymb(pWordSymbInfo->Private.ppEditionsList[ET9EDITION_AW_2], bImplicitLock, bAddingSymb);
    }

#else

    ET9_UNUSED(bAddingSymb);

#endif /* ET9_ALPHABETIC_MODULE */

    /* handle post shift "lock" */

    if (pWordSymbInfo->Private.eCurrPostShiftMode != ET9POSTSHIFTMODE_DEFAULT) {

        ET9BOOL                 bFirst;
        ET9U16                  wCount;
        ET9SymbInfo             *pSymbInfo;
        const ET9POSTSHIFTMODE  eMode = pWordSymbInfo->Private.eCurrPostShiftMode;

        WLOG4(fprintf(pLogFile4, "_ET9ImminentSymb, locking post shift state\n");)

        /* symbol shift change */

        bFirst = 1;
        wCount = pWordSymbInfo->bNumSymbs;
        pSymbInfo = pWordSymbInfo->SymbsInfo;
        for (; wCount; --wCount, ++pSymbInfo, bFirst = 0) {

            if (eMode == ET9POSTSHIFTMODE_UPPER || (eMode == ET9POSTSHIFTMODE_INITIAL && bFirst)) {

                pSymbInfo->eShiftState = ET9SHIFT;
                pSymbInfo->bForcedLowercase = 0;

                /* if the sym is locked, make sure locked sym is correct case (CR 23767) */

                if (pSymbInfo->sLockedSymb) {
                    pSymbInfo->sLockedSymb = _ET9SymToUpper(pSymbInfo->sLockedSymb, pWordSymbInfo->Private.dwLocale);
                }
            }
            else {
                pSymbInfo->eShiftState = ET9NOSHIFT;

                /* if the sym is locked, make sure locked sym is correct case (CR 23767) */

                if (pSymbInfo->sLockedSymb) {
                    pSymbInfo->sLockedSymb = _ET9SymToLower(pSymbInfo->sLockedSymb, pWordSymbInfo->Private.dwLocale);
                }
                if (eMode == ET9POSTSHIFTMODE_LOWER) {
                    pSymbInfo->bForcedLowercase = 1;
                }
            }
        }

        /* shift state change */

        if (eMode == ET9POSTSHIFTMODE_UPPER) {
            pWordSymbInfo->dwStateBits &= ~(ET9STATE_SHIFT_MASK);
            pWordSymbInfo->dwStateBits |= ET9STATE_CAPS_MASK;
            pWordSymbInfo->Private.eLastShiftState = ET9CAPSLOCK;
        }
        else {
            pWordSymbInfo->dwStateBits &= ~(ET9STATE_SHIFT_MASK | ET9STATE_CAPS_MASK);
            pWordSymbInfo->Private.eLastShiftState = ET9NOSHIFT;
        }

        /* reset mode */

        pWordSymbInfo->Private.eCurrPostShiftMode = ET9POSTSHIFTMODE_DEFAULT;
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                            
 *
 *                                                                        
 *                                                      
 *
 *             
 */

void ET9FARCALL _ET9InvalidateOneSymb(ET9WordSymbInfo * const pWordSymbInfo, const ET9U8 bIndex)
{
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {

        if (*ppLing) {
            (*ppLing)->bSymbInvalidated[bIndex] = 1;
            (*ppLing)->bSymbsInfoInvalidated = 1;
        }
    }

    /* the required word should not be verified or located after an input modification */

    pWordSymbInfo->Private.bRequiredLocate = 0;
    pWordSymbInfo->Private.bRequiredVerifyInput = 0;
}

#if 0

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                          
 *
 *                                                                        
 *                                                    
 *
 *             
 */

void ET9FARCALL _ET9ValidateOneSymb(ET9WordSymbInfo * const pWordSymbInfo, const ET9U8 bIndex)
{
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {

        if (*ppLing) {
            (*ppLing)->bSymbInvalidated[bIndex] = 0;
        }
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                      
 *
 *                                                                        
 *
 *             
 */

void ET9FARCALL _ET9ValidateAllSymbs(ET9WordSymbInfo * const pWordSymbInfo)
{
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {

        if (*ppLing) {

            ET9U8  bIndex;

            for (bIndex = 0; bIndex < pWordSymbInfo->bNumSymbs; ++bIndex) {
                (*ppLing)->bSymbInvalidated[bIndex] = 0;
            }
        }
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                    
 *
 *                                                                        
 *                                                    
 *
 *             
 */

void ET9FARCALL _ET9ValidateOneSymbAW(ET9WordSymbInfo * const pWordSymbInfo, const ET9U8 bIndex)
{
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = &pWordSymbInfo->Private.ppEditionsList[ET9EDITION_AW];

    if (*ppLing) {
        (*ppLing)->bSymbInvalidated[bIndex] = 0;
    }
}

#endif

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                          
 *
 *                                                                        
 *                                                      
 *
 *             
 */

void ET9FARCALL _ET9InvalidateOneLock(ET9WordSymbInfo * const pWordSymbInfo, const ET9U8 bIndex)
{
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {

        if (*ppLing) {
            (*ppLing)->bLockInvalidated[bIndex] = 1;
            (*ppLing)->bSymbsInfoInvalidated = 1;
        }
    }

    /* the required word should not be verified or located after an input modification */

    pWordSymbInfo->Private.bRequiredLocate = 0;
    pWordSymbInfo->Private.bRequiredVerifyInput = 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                        
 *
 *                                                                        
 *
 *             
 */

void ET9FARCALL _ET9InvalidateSymbInfo(ET9WordSymbInfo * const pWordSymbInfo)
{
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {

        if (*ppLing) {
            (*ppLing)->bSymbsInfoInvalidated = 1;
        }
    }

    /* the required word should not be verified or located after an input modification */

    pWordSymbInfo->Private.bRequiredLocate = 0;
    pWordSymbInfo->Private.bRequiredVerifyInput = 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                          
 *
 *                                                                        
 *
 *             
 */

void ET9FARCALL _ET9InvalidateSelList(ET9WordSymbInfo * const pWordSymbInfo)
{
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {

        if (*ppLing) {
            (*ppLing)->bSelListInvalidated = 1;
        }
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                             
 *
 *                                                                        
 *
 *             
 */

void ET9FARCALL _ET9ContentExplicified(ET9WordSymbInfo * const pWordSymbInfo)
{
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;

    ET9Assert(pWordSymbInfo);

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {

        if (*ppLing) {
            (*ppLing)->bContentExplicified = 1;
        }
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                  
 *
 *                                                                        
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL _ET9WordSymbInit(ET9WordSymbInfo * const pWordSymbInfo)
{
    ET9STATUS           wStatus;
    ET9U8               bEditionCount;
    ET9BaseLingInfo     **ppLing;
    ET9U16              wSaveCount;
    ET9SavedInputWord   *pSavedWord;

    ET9Assert(pWordSymbInfo != NULL);

    if (pWordSymbInfo->wInitOK == ET9GOODSETUP) {
        return ET9STATUS_NONE;
    }

    _ET9ClearMem((ET9U8*)pWordSymbInfo, sizeof(ET9WordSymbInfo));

    wStatus = _ET9CheckFundamentalTypes();

    if (wStatus) {
        return wStatus;
    }

    wStatus = _ET9CheckCharProps();

    if (wStatus) {
        return wStatus;
    }

    pWordSymbInfo->wInitOK = ET9GOODSETUP;
    pWordSymbInfo->pPublicExtension = NULL;
    pWordSymbInfo->Private.eCurrPostShiftMode = ET9POSTSHIFTMODE_DEFAULT;
    pWordSymbInfo->Private.bPreventWhiteSpaceInput = 1;

    pWordSymbInfo->Private.eStateFreeSymbols = ET9_SettingValue_Auto;
    pWordSymbInfo->Private.eStateFreeDoubleLetters = ET9_SettingValue_Auto;

    ppLing = pWordSymbInfo->Private.ppEditionsList;

    for (bEditionCount = ET9MAXEDITIONS; bEditionCount; --bEditionCount, ++ppLing) {
        *ppLing = NULL;
    }

    pWordSymbInfo->Private.bInputRestarted = 1;
    pWordSymbInfo->Private.sSavedInputWords.wCurrInputSaveIndex = 0;

    pSavedWord = pWordSymbInfo->Private.sSavedInputWords.pSavedWords;

    for (wSaveCount = ET9MAXSAVEINPUTWORDS; wSaveCount; --wSaveCount, ++pSavedWord) {
        pSavedWord->wStorePos = UNDEFINED_STORE_INDEX;
    }

    _ET9_GetDefaultLocale(&pWordSymbInfo->Private.dwLocale, &pWordSymbInfo->Private.bManualLocale);

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *
 *                                               
 *                                                                                         
 *
 *                                                                        
 *
 *                                                 
 */

ET9BOOL ET9FARCALL _ET9IsMagicStringKey(ET9WordSymbInfo *pWordSymbInfo)
{
    ET9BOOL bRet = 0;
    ET9INT i;
    ET9SYMB curSym;
    ET9SymbInfo *pSymbsInfo;

    ET9Assert(pWordSymbInfo);
    ET9Assert(pWordSymbInfo->bNumSymbs == ET9MAXLDBWORDSIZE);

    curSym = pWordSymbInfo->SymbsInfo[0].DataPerBaseSym[0].sChar[0];
    pSymbsInfo = &pWordSymbInfo->SymbsInfo[1];
    for (i = 1; i < ET9MAGICSTR_FIRSTSET; ++i, ++pSymbsInfo) {
        if (pSymbsInfo->DataPerBaseSym[0].sChar[0] != curSym) {
           break;
        }
    }
    if (i == ET9MAGICSTR_FIRSTSET) {
        curSym = pWordSymbInfo->SymbsInfo[ET9MAGICSTR_FIRSTSET].DataPerBaseSym[0].sChar[0];
        if (curSym != pWordSymbInfo->SymbsInfo[0].DataPerBaseSym[0].sChar[0]) {
            for (; i < ET9MAXLDBWORDSIZE; ++i, ++pSymbsInfo) {
                if (pSymbsInfo->DataPerBaseSym[0].sChar[0] != curSym) {
                   break;
                }
            }
            if (i == ET9MAXLDBWORDSIZE) {
                bRet = 1;
            }
        }
    }
    return bRet;
}

#ifdef ET9_DEBUGLOG4
/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                      
 *
 *                                                                        
 *                                                 
 *
 *             
 */

static void ET9LOCALCALL __LogSavedWords(ET9WordSymbInfo   * const pWordSymbInfo,
                                         FILE              * const pfLog)
{
    ET9U16 wIndex;

    ET9SavedInputWords  * const pSavedInputWords = &pWordSymbInfo->Private.sSavedInputWords;

    fprintf(pfLog, "\n--------------------- Saved Words ---------------------\n\n");

    for (wIndex = 0; wIndex < ET9MAXSAVEINPUTWORDS; ++wIndex) {

        ET9SYMB *ps;
        ET9U16 wCount;
        ET9SavedInputInfo *pInfo;
        ET9SavedInputWord * const pWord = &pSavedInputWords->pSavedWords[wIndex];

        if (pWord->wStorePos == UNDEFINED_STORE_INDEX) {
            continue;
        }

        fprintf(pfLog, "[%03d]", wIndex);

        if (wIndex == pSavedInputWords->wCurrInputSaveIndex) {
            fprintf(pfLog, " * ");
        }
        else {
            fprintf(pfLog, " : ");
        }

        fprintf(pfLog, "[%04d]", pWord->wStorePos);

        fprintf(pfLog, " %08x ", pWord->dwStringHash);

#ifdef ET9_DEBUG

        for (ps = pWord->psString, wCount = pWord->wStringLen; wCount; ++ps, --wCount) {
            if (*ps >= 0x20 && *ps <= 0x7F) {
                fprintf(pfLog, "%c", (char)*ps);
            }
            else {
                fprintf(pfLog, "<%x>", (int)*ps);
            }
        }

#endif

        switch (pWord->eLastShiftState)
        {
            case ET9NOSHIFT:
                fprintf(pfLog, " NOSHIFT");
                break;
            case ET9SHIFT:
                fprintf(pfLog, " SHIFT");
                break;
            case ET9CAPSLOCK:
                fprintf(pfLog, " CAPS");
                break;
            default:
                fprintf(pfLog, " \?\?\?");
                break;
        }

        fprintf(pfLog, "\n[   ]   ");

        pInfo = &pSavedInputWords->sInputs[pWord->wStorePos];

        for (wCount = pWord->wInputLen; wCount; --wCount, ++pInfo) {

            if (pInfo->eKind == _ET9_SavedInputInfoKind_symb) {

                ET9SavedInputSymb const * const pSymb = &pInfo->u.sInputSymb;

                switch (pSymb->eInputType)
                {
                    case ET9DISCRETEKEY:        fprintf(pfLog, "DIS"); break;
                    case ET9REGIONALKEY:        fprintf(pfLog, "REG"); break;
                    case ET9HANDWRITING:        fprintf(pfLog, "HWR"); break;
                    case ET9MULTITAPKEY:        fprintf(pfLog, "MTK"); break;
                    case ET9CUSTOMSET:          fprintf(pfLog, "CST"); break;
                    case ET9EXPLICITSYM:        fprintf(pfLog, "EXP"); break;
                    case ET9MULTISYMBEXPLICIT:  fprintf(pfLog, "MTE"); break;
                    default:                    fprintf(pfLog, "Inp<%d>", pSymb->eInputType); break;
                }

                if (pSymb->eInputType == ET9CUSTOMSET || pSymb->eInputType == ET9EXPLICITSYM) {
                }
                else if (pSymb->wTapX != ET9UNDEFINEDTAPVALUE && pSymb->wTapY != ET9UNDEFINEDTAPVALUE) {
                    fprintf(pfLog, " [%u,%u %08x:%u %08x:%u]", (int)pSymb->wTapX, (int)pSymb->wTapY, (int)pSymb->dwKdb1, (int)pSymb->wPage1, (int)pSymb->dwKdb2, (int)pSymb->wPage2);
                }
                else {
                    fprintf(pfLog, " (Key %u %08x:%u %08x:%u)", (int)pSymb->wKeyIndex, (int)pSymb->dwKdb1, (int)pSymb->wPage1, (int)pSymb->dwKdb2, (int)pSymb->wPage2);
                }

                fprintf(pfLog, " (ext %u %u)", (int)pSymb->bTraceProbability, (int)pSymb->bTraceIndex);

                if (pSymb->eInputType == ET9MULTISYMBEXPLICIT) {
                    fprintf(pfLog, " (BB %u)", (int)pSymb->bInputIndex);
                }

                if (pSymb->bLocked) {
                    fprintf(pfLog, " LP");
                }

                if (pSymb->eShiftState == ET9SHIFT) {
                    fprintf(pfLog, " SH");
                }
                else if (pSymb->eShiftState == ET9CAPSLOCK) {
                    fprintf(pfLog, " CL");
                }

                fprintf(pfLog, "; ");
            }
            else if (pInfo->eKind == _ET9_SavedInputInfoKind_points) {

                ET9SavedInputPoints const * const pSavedPoints = &pInfo->u.sInputPoints;

                ET9U8 bIndex;

                fprintf(pfLog, "<PTS>");

                for (bIndex = 0; bIndex < pSavedPoints->bCount; ++bIndex) {
                    fprintf(pfLog, "[%u,%u]", pSavedPoints->pwX[bIndex], pSavedPoints->pwY[bIndex]);
                }

                fprintf(pfLog, "; ");
            }
            else {
                fprintf(pfLog, "<unknown kind of saved info>; ");
            }
        }

        fprintf(pfLog, "\n");
    }

    fprintf(pfLog, "\n-------------------------------------------------------\n\n");

    fflush(pfLog);
}
#else
#define __LogSavedWords(x,y)
#endif

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                               
 *
 *                                                      
 *
 *                                                                        
 *                                                                                  
 *
 *             
 */

static ET9U16 ET9LOCALCALL __EstimatedNumberOfSaveSymbs(ET9WordSymbInfo   * const pWordSymbInfo,
                                                        ET9U16            * const pwPointCount)
{
    ET9INT snIndex;
    ET9U16 wSymbsUsed;
    ET9UINT nLastPathPos;

    if (!_ET9HasTraceInfo(pWordSymbInfo)) {
        return pWordSymbInfo->bNumSymbs;
    }

    ET9Assert(pWordSymbInfo->bNumSymbs);

    WLOG4(fprintf(pLogFile4, "__EstimatedNumberOfSaveSymbs, available points (nLastPathPos) %u\n", pWordSymbInfo->Private.sCurrTrace.nPointCount);)

    wSymbsUsed = 0;
    nLastPathPos = pWordSymbInfo->Private.sCurrTrace.nPointCount;

    for (snIndex = pWordSymbInfo->bNumSymbs - 1; snIndex >= 0; --snIndex) {

        ET9SymbInfo const * const pSymbInfo = &pWordSymbInfo->SymbsInfo[snIndex];

        pwPointCount[snIndex] = 0;

        if (!pSymbInfo->bTraceIndex) {
            ++wSymbsUsed;
            continue;
        }

        if (!snIndex || pSymbInfo->bTraceIndex != pSymbInfo[-1].bTraceIndex) {

            ET9Assert(nLastPathPos >= pSymbInfo->wTraceStartPos);

            pwPointCount[snIndex] = (ET9U16)(nLastPathPos - pSymbInfo->wTraceStartPos);

            wSymbsUsed = (ET9U16)(wSymbsUsed + 1 + pwPointCount[snIndex] / _ET9_MAX_SAVE_POINTS + 1);

            nLastPathPos = pSymbInfo->wTraceStartPos;

            WLOG4(fprintf(pLogFile4, "  [%2u] point count %u, new nLastPathPos %u\n", snIndex, pwPointCount[snIndex], nLastPathPos);)
        }
    }

    return wSymbsUsed;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                        
 *
 *                                                     
 *                                                
 *
 *             
 */

static void ET9LOCALCALL __TransferSymbToSaveSymb(ET9SavedInputInfo * const pDst,
                                                  ET9SymbInfo       * const pSrc)
{
    ET9SavedInputSymb * const pSaveSymb = &pDst->u.sInputSymb;

    pDst->eKind = _ET9_SavedInputInfoKind_symb;

    pSaveSymb->wTapX                = pSrc->wTapX;
    pSaveSymb->wTapY                = pSrc->wTapY;
    pSaveSymb->wKeyIndex            = pSrc->wKeyIndex;
    pSaveSymb->bInputIndex          = (ET9U8)pSrc->wInputIndex;
    pSaveSymb->bLocked              = pSrc->bLocked;
    pSaveSymb->eInputType           = pSrc->eInputType;
    pSaveSymb->eShiftState          = pSrc->eShiftState;
    pSaveSymb->bForcedLowercase     = pSrc->bForcedLowercase;
    pSaveSymb->bTraceProbability    = pSrc->bTraceProbability;
    pSaveSymb->bTraceIndex          = pSrc->bTraceIndex;
    pSaveSymb->dwKdb1               = pSrc->dwKdb1;
    pSaveSymb->wPage1               = pSrc->wPage1;
    pSaveSymb->dwKdb2               = pSrc->dwKdb2;
    pSaveSymb->wPage2               = pSrc->wPage2;
    pSaveSymb->sSymb                = pSrc->DataPerBaseSym[0].sChar[0];
    pSaveSymb->sLockedSymb          = pSrc->sLockedSymb;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                 
 *
 *                                                                        
 *                                                        
 *                                                
 *                                                           
 *
 *                              
 */

static ET9U16 ET9LOCALCALL __TransferPointsToSaveSymb(ET9WordSymbInfo   * const pWordSymbInfo,
                                                      ET9SavedInputInfo * const pDst,
                                                      ET9SymbInfo       * const pSrc,
                                                      const ET9U16              wPointCount)
{
    ET9U16 wSymbsUsed;
    ET9U16 wRemainingCount;
    ET9SavedInputInfo *pCurrDst;
    ET9TracePointTime const *pCurrPoint;

    WLOG4(fprintf(pLogFile4, "__TransferPointsToSaveSymb, wPointCount %u\n", wPointCount);)

    if (!wPointCount) {
        ET9Assert(0);
        return 0;
    }

    wSymbsUsed = 0;
    pCurrDst = pDst;
    pCurrPoint = &pWordSymbInfo->Private.sCurrTrace.pPoints[pSrc->wTraceStartPos];
    for (wRemainingCount = wPointCount; wRemainingCount; ++pCurrDst, ++wSymbsUsed) {

        ET9SavedInputPoints * const pSavePoints = &pCurrDst->u.sInputPoints;

        pCurrDst->eKind = _ET9_SavedInputInfoKind_points;

        for (pSavePoints->bCount = 0; wRemainingCount && pSavePoints->bCount < _ET9_MAX_SAVE_POINTS; ++pSavePoints->bCount, ++pCurrPoint, --wRemainingCount) {

            pSavePoints->pwX[pSavePoints->bCount] = (ET9U16)(pCurrPoint->fX + 0.5f);
            pSavePoints->pwY[pSavePoints->bCount] = (ET9U16)(pCurrPoint->fY + 0.5f);
        }
    }

    return wSymbsUsed;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                            
 *
 *                                                                        
 *                                                      
 *                                                       
 *
 *             
 */

void ET9FARCALL _ET9SaveWord(ET9WordSymbInfo   * const pWordSymbInfo,
                             ET9SYMB           * const pString,
                             const ET9U16              wLen)
{
    ET9SavedInputWords  * const pSavedInputWords = &pWordSymbInfo->Private.sSavedInputWords;

    ET9U16 pwPointCount[ET9MAXWORDSIZE];

    const ET9U16 wEstimatedNumberOfSaveSymbs = __EstimatedNumberOfSaveSymbs(pWordSymbInfo, pwPointCount);

    ET9Assert(pWordSymbInfo);
    ET9Assert(pString);
    ET9Assert(wLen <= ET9MAXWORDSIZE);

    WLOG4(fprintf(pLogFile4, "_ET9SaveWord, wEstimatedNumberOfSaveSymbs %u\n", wEstimatedNumberOfSaveSymbs);)

    /* don't save anything on empty input (NWP) */

    if (!pWordSymbInfo->bNumSymbs) {
        return;
    }

    /* find a position for the word */

    if (pWordSymbInfo->Private.bInputRestarted) {

        WLOG4(fprintf(pLogFile4, "_ET9SaveWord, input restarted\n");)

        ET9Assert(pSavedInputWords->wCurrInputSaveIndex < ET9MAXSAVEINPUTWORDS);

        if (pSavedInputWords->pSavedWords[pSavedInputWords->wCurrInputSaveIndex].wStorePos != UNDEFINED_STORE_INDEX) {
            pSavedInputWords->wCurrInputSaveIndex = (ET9U16)((pSavedInputWords->wCurrInputSaveIndex + 1) % ET9MAXSAVEINPUTWORDS);
        }

        pWordSymbInfo->Private.bInputRestarted = 0;
    }

    ET9Assert(pSavedInputWords->wCurrInputSaveIndex < ET9MAXSAVEINPUTWORDS);

    WLOG4(fprintf(pLogFile4, "_ET9SaveWord, saving word @ %d\n", pSavedInputWords->wCurrInputSaveIndex);)

    /* find a position for the data */

    {
        ET9U16          wPos;
        const ET9U16    wSaveLen = wEstimatedNumberOfSaveSymbs;
        const ET9U16    wPrevWord = (ET9U16)((pSavedInputWords->wCurrInputSaveIndex + (ET9MAXSAVEINPUTWORDS - 1)) % ET9MAXSAVEINPUTWORDS);

        ET9Assert(wPrevWord < ET9MAXSAVEINPUTWORDS);

        wPos = pSavedInputWords->pSavedWords[wPrevWord].wStorePos;

        if (wPos == UNDEFINED_STORE_INDEX) {
            wPos = 0;
        }
        else {
            wPos = wPos + pSavedInputWords->pSavedWords[wPrevWord].wInputLen;
        }

        if (wPos + wSaveLen > ET9SAVEINPUTSTORESIZE) {
            wPos = 0;
        }

        WLOG4(fprintf(pLogFile4, "_ET9SaveWord, using storage pos %d length %d\n", wPos, wSaveLen);)

        /* clear words that already are using this storage */

        {
            const ET9U16        wLastPos = (ET9U16)(wPos + wSaveLen - 1);
            ET9U16              wCount;
            ET9U16              wWordLastPos;
            ET9SavedInputWord   *pWord;

            pWord = pSavedInputWords->pSavedWords;

            for (wCount = ET9MAXSAVEINPUTWORDS; wCount; --wCount, ++pWord) {

                if (pWord->wStorePos == UNDEFINED_STORE_INDEX) {
                    continue;
                }

                wWordLastPos = (ET9U16)(pWord->wStorePos + pWord->wInputLen - 1);

                if ((pWord->wStorePos >= wPos && pWord->wStorePos <= wLastPos) ||
                    (wWordLastPos     >= wPos && wWordLastPos     <= wLastPos)) {

                    pWord->wStorePos = UNDEFINED_STORE_INDEX;
                }
            }
        }

        /* assign pos */

        pSavedInputWords->pSavedWords[pSavedInputWords->wCurrInputSaveIndex].wStorePos = wPos;
    }

    /* save input */

    {
        ET9U16 wCount;
        ET9SymbInfo *pSymbInfo;
        ET9SavedInputInfo *pSavedInfo;
        ET9SavedInputWord * const pWord = &pSavedInputWords->pSavedWords[pSavedInputWords->wCurrInputSaveIndex];

        if (pWordSymbInfo->Private.bRequiredLocate) {
            pWord->eLastShiftState = pWordSymbInfo->Private.eRequiredLastShiftState;
        }
        else {
            pWord->eLastShiftState = pWordSymbInfo->Private.eLastShiftState;
        }

        pWord->dwStringHash = _ET9SymbStringCheckSum(pString, wLen);

        pWord->bSwitchLanguage = pWordSymbInfo->Private.bSwitchLanguage;

        pWord->bHasShiftOrCapsGesture = (pWordSymbInfo->Private.bHasShiftGesture || pWordSymbInfo->Private.bHasAllCapsGesture);

#ifdef ET9_DEBUG
        pWord->wStringLen = wLen;
        _ET9SymCopy(pWord->psString, pString, wLen);
#endif

        pWord->wInputLen = 0;

        pSymbInfo = pWordSymbInfo->SymbsInfo;
        pSavedInfo = &pSavedInputWords->sInputs[pWord->wStorePos];

        for (wCount = pWordSymbInfo->bNumSymbs; wCount; ) {

            WLOG4(fprintf(pLogFile4, "_ET9SaveWord, wCount %u, index %u, bTraceIndex %u\n", wCount, (pSymbInfo - pWordSymbInfo->SymbsInfo), pSymbInfo->bTraceIndex);)

            if (pSymbInfo->bTraceIndex) {

                __TransferSymbToSaveSymb(pSavedInfo, pSymbInfo);

                ++pSavedInfo;
                ++pWord->wInputLen;

                {
                    const ET9U16 wSymbsUsed = __TransferPointsToSaveSymb(pWordSymbInfo, pSavedInfo, pSymbInfo, pwPointCount[pSymbInfo - pWordSymbInfo->SymbsInfo]);

                    pSavedInfo += wSymbsUsed;
                    pWord->wInputLen = (ET9U16)(pWord->wInputLen + wSymbsUsed);

                    {
                        const ET9U8 bCurrTraceIndex = pSymbInfo->bTraceIndex;

                        while (wCount && pSymbInfo->bTraceIndex == bCurrTraceIndex) {
                            --wCount;
                            ++pSymbInfo;
                        }
                    }
                }
            }
            else {

                __TransferSymbToSaveSymb(pSavedInfo, pSymbInfo);

                --wCount;
                ++pSymbInfo;
                ++pSavedInfo;
                ++pWord->wInputLen;
            }
        }

        ET9Assert(pWord->wInputLen <= wEstimatedNumberOfSaveSymbs);
    }

    /* log */

    __LogSavedWords(pWordSymbInfo, pLogFile4);
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                               
 *
 *                                                                        
 *                                                  
 *                                                       
 *                                                                                             
 *
 *             
 */

ET9BOOL ET9FARCALL _ET9_IsLastSavedWord(ET9WordSymbInfo   const * const pWordSymbInfo,
                                        ET9SYMB           const * const pString,
                                        const ET9U16                    wLen,
                                        ET9BOOL                 * const pbHasShiftOrCapsGesture)
{
    ET9SavedInputWords  const * const pSavedInputWords = &pWordSymbInfo->Private.sSavedInputWords;

    ET9Assert(pbHasShiftOrCapsGesture);

    WLOG4(fprintf(pLogFile4, "_ET9_IsLastSavedWord, wLen %u\n", wLen);)

    *pbHasShiftOrCapsGesture = 0;

    if (!wLen) {

        WLOG4(fprintf(pLogFile4, "  -> NO - zero length word\n");)

        return 0;
    }

    if (pSavedInputWords->wCurrInputSaveIndex >= ET9MAXSAVEINPUTWORDS) {

        WLOG4(fprintf(pLogFile4, "  -> NO - no words saved\n");)

        return 0;
    }

    {
        ET9U32 dwStringHash = _ET9SymbStringCheckSum(pString, wLen);

        ET9SavedInputWord const * const pWord = &pSavedInputWords->pSavedWords[pSavedInputWords->wCurrInputSaveIndex];

        WLOG4(fprintf(pLogFile4, "  -> %s - dwStringHash %u, saved-dwStringHash %u\n", ((dwStringHash == pWord->dwStringHash) ? "YES" : "NO"), dwStringHash, pWord->dwStringHash);)

        if (dwStringHash == pWord->dwStringHash) {

            *pbHasShiftOrCapsGesture = pWord->bHasShiftOrCapsGesture;

            return 1;
        }
        else {

            return 0;
        }
    }
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                  
 *
 *                                                                        
 *                                                    
 *
 *             
 */

void ET9FARCALL _ET9ExplicifyWord(ET9WordSymbInfo   * const pWordSymbInfo,
                                  ET9SimpleWord     * const pWord)
{
    ET9STATUS   wStatus;
    ET9U16      wCount;
    ET9SYMB     *psSymb;

    ET9Assert(pWord);
    ET9Assert(pWordSymbInfo);

    WLOG4(fprintf(pLogFile4, "_ET9ExplicifyWord\n");)

    /* validate */

    if (!pWord->wLen) {
        ET9ClearAllSymbs(pWordSymbInfo);
        return;
    }

    ET9Assert(!pWord->wCompLen);

    /* enter using explicits */

    __ClearAllSymbs(pWordSymbInfo, 0);

    psSymb = pWord->sString;

    for (wCount = pWord->wLen; wCount; --wCount, ++psSymb) {

        wStatus = ET9AddExplicitSymb(pWordSymbInfo,
                                     *psSymb,
                                     0,
                                     (_ET9SymIsUpper(*psSymb, pWordSymbInfo->Private.dwLocale) ? ET9SHIFT : ET9NOSHIFT),
                                     ET9_NO_ACTIVE_INDEX);

        if (wStatus) {

            WLOG4(fprintf(pLogFile4, "_ET9ExplicifyWord, explicit symb failed\n");)

            ET9ClearAllSymbs(pWordSymbInfo);
            return;
        }
    }

    ET9Assert(pWordSymbInfo->bNumSymbs == pWord->wLen);

    /* potentially prevent catchup builds etc */

    _ET9ContentExplicified(pWordSymbInfo);
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Enables the next locking feature.
 *
 * @param[in]     pWordSymbInfo       Pointer to the Word Symbol Data Structure (ET9WordSymbInfo).
 *
 * @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.
 *
 * @remarks Next locking limits the results in the selection list to words that begin with specifed characters.<br>
 * By default, next locking is disabled.
 *
 * @see ET9ClearNextLocking()
 */

ET9STATUS ET9FARCALL ET9SetNextLocking(ET9WordSymbInfo * const pWordSymbInfo)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    pWordSymbInfo->dwStateBits |= ET9STATENEXTLOCKINGMASK;

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Disables the next locking feature.
 *
 * @param[in]     pWordSymbInfo       Pointer to the Word Symbol Data Structure (ET9WordSymbInfo).
 *
 * @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.
 *
 * @remarks Next locking limits the results in the selection list to words that begin with specifed characters.<br>
 * By default, next locking is disabled.
 *
 * @see ET9SetNextLocking()
 */

ET9STATUS ET9FARCALL ET9ClearNextLocking(ET9WordSymbInfo * const pWordSymbInfo)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return  ET9STATUS_INVALID_MEMORY;
    }

    pWordSymbInfo->dwStateBits &= ~(ET9STATENEXTLOCKINGMASK);

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Initializes the the Word Symbol Data Structure.
 *
 * @param[in]     pWordSymbInfo        Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the input the user has provided.
 * @param[out]    bResetWordSymbInfo   Reset indicator used to force resetting a previously initialized Word Symbol Data Structure.
 *
 * @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.
 */

ET9STATUS ET9FARCALL ET9WordSymbInit(ET9WordSymbInfo * const pWordSymbInfo,
                                     const ET9BOOL           bResetWordSymbInfo)
{
    /* validate parameters */

    if (!pWordSymbInfo) {
        return ET9STATUS_INVALID_MEMORY;
    }

    /* init needed? */

    if (pWordSymbInfo->wInitOK == ET9GOODSETUP && !bResetWordSymbInfo) {
        return ET9STATUS_NONE;
    }

    /* allowed to init? (can't break edition references by this init call) */

    if (pWordSymbInfo->wInitOK == ET9GOODSETUP) {

        ET9U8               bCount;
        ET9BaseLingInfo     **ppBase;

        ppBase = pWordSymbInfo->Private.ppEditionsList;
        for (bCount = ET9MAXEDITIONS; bCount; --bCount, ++ppBase) {

            if (*ppBase) {
                return ET9STATUS_ERROR;
            }
        }
    }

    /* actual init */

    pWordSymbInfo->wInitOK = 0;

    return _ET9WordSymbInit(pWordSymbInfo);
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Sets the Keyboard Input Module to an unshifted (or lowercase) shift state.
 *
 * @param[in]     pWordSymbInfo     Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the input the user has provided.
 *
 * @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_INVALID_KDB_PAGE   The page specified by wPageNum is not valid for the active keyboard.
 * @retval ET9STATUS_NO_KEY             No keys are associated with the keyboard page specified by wPageNum.
 * @retval ET9STATUS_READ_DB_FAIL       The XT9 core cannot read data from the keyboard. This typically indicates a problem with the ET9KDBREADCALLBACK function you have implemented.
 *
 * @see ET9SetCapsLock(), ET9SetShift()
 */

ET9STATUS ET9FARCALL ET9SetUnShift(ET9WordSymbInfo * const pWordSymbInfo)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    {
        const ET9U32 dwStateBitsOld = pWordSymbInfo->dwStateBits;

        /* Make sure autocap status is off at this point... user overridden */

        if (!pWordSymbInfo->bNumSymbs) {
           pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_OFF;
        }

        /* Make sure downshifting logic at this point... user overridden */

        if (pWordSymbInfo->Private.eLastShiftState != ET9NOSHIFT) {
            pWordSymbInfo->Private.bCompoundingDownshift = 0;
        }

        pWordSymbInfo->dwStateBits &= ~(ET9STATE_SHIFT_MASK | ET9STATE_CAPS_MASK);
        pWordSymbInfo->Private.eLastShiftState = ET9NOSHIFT;

        if (pWordSymbInfo->dwStateBits != dwStateBitsOld) {
            _ET9InvalidateSelList(pWordSymbInfo);
        }
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Sets the Keyboard Input Module to a shifted (or single-character shifted) shift state.
 *
 * @param[in]     pWordSymbInfo     Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the input the user has provided.
 *
 * @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_INVALID_KDB_PAGE   The page specified by wPageNum is not valid for the active keyboard.
 * @retval ET9STATUS_NO_KEY             No keys are associated with the keyboard page specified by wPageNum.
 * @retval ET9STATUS_READ_DB_FAIL       The XT9 core cannot read data from the keyboard. This typically indicates a problem with the ET9KDBREADCALLBACK function you have implemented.
 *
 * @see ET9SetCapsLock(), ET9SetUnShift()
 */

ET9STATUS ET9FARCALL ET9SetShift(ET9WordSymbInfo * const pWordSymbInfo)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    {
        const ET9U32 dwStateBitsOld = pWordSymbInfo->dwStateBits;

        /* don't reset the insert mask here! */
        /* if shift request received immediately after reporting autocap scenario */

        if (pWordSymbInfo->Private.eAutocapWord == ET9AUTOCAP_PENDING) {

           /* upgrade status for word */

           pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_APPLIED;
        }

        /* Make sure downshifting logic at this point... user overridden */

        pWordSymbInfo->Private.bCompoundingDownshift = 0;
        pWordSymbInfo->dwStateBits &= ~(ET9STATE_CAPS_MASK);
        pWordSymbInfo->dwStateBits |= ET9STATE_SHIFT_MASK;
        pWordSymbInfo->Private.eLastShiftState = ET9SHIFT;

        if (pWordSymbInfo->dwStateBits != dwStateBitsOld) {
            _ET9InvalidateSelList(pWordSymbInfo);
        }
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * @brief Sets the Keyboard Input Module to a caps lock (or uppercase) shift state.
 *
 * @param[in]     pWordSymbInfo         Pointer to the Word Symbol Data Structure (ET9WordSymbInfo), which contains information about the input the user has provided.
 *
 * @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_INVALID_KDB_PAGE   The page specified by wPageNum is not valid for the active keyboard.
 * @retval ET9STATUS_NO_KEY             No keys are associated with the keyboard page specified by wPageNum.
 * @retval ET9STATUS_READ_DB_FAIL       The XT9 core cannot read data from the keyboard. This typically indicates a problem with the ET9KDBREADCALLBACK function you have implemented.
 *
 * @see ET9SetShift(), ET9SetUnShift()
 */

ET9STATUS ET9FARCALL ET9SetCapsLock(ET9WordSymbInfo * const pWordSymbInfo)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    {
        const ET9U32 dwStateBitsOld = pWordSymbInfo->dwStateBits;

        /* don't reset the insert mask here! */
        /* Make sure autocap status is off at this point... user overridden */

        pWordSymbInfo->Private.eAutocapWord = ET9AUTOCAP_OFF;

        /* Make sure downshift logic disabled at this point... user overridden */

        pWordSymbInfo->Private.bCompoundingDownshift = 0;
        pWordSymbInfo->dwStateBits &= ~(ET9STATE_SHIFT_MASK);
        pWordSymbInfo->dwStateBits |= ET9STATE_CAPS_MASK;
        pWordSymbInfo->Private.eLastShiftState = ET9CAPSLOCK;

        if (pWordSymbInfo->dwStateBits != dwStateBitsOld) {
            _ET9InvalidateSelList(pWordSymbInfo);
        }
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                     
 *
 *                                                                                                                                                                    
 *
 *                                         
 */

ET9BOOL ET9FARCALL _ET9HasTraceInfo(ET9WordSymbInfo const * const pWordSymbInfo)
{
    ET9UINT nCount;
    ET9SymbInfo const *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return 0;
    }

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {
        if (pSymbInfo->bTraceIndex) {
            return 1;
        }
    }

    return 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                          
 *
 *                                                                                                                                                                    
 *
 *                                         
 */

ET9BOOL ET9FARCALL _ET9HasTraceOnlyInfo(ET9WordSymbInfo const * const pWordSymbInfo)
{
    ET9UINT nCount;
    ET9SymbInfo const *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return 0;
    }

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {
        if (!pSymbInfo->bTraceIndex) {
            return 0;
        }
    }

    return 1;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                        
 *
 *                                                                                                                                                                    
 *
 *                                         
 */

ET9BOOL ET9FARCALL _ET9HasRegionalInfo(ET9WordSymbInfo const * const pWordSymbInfo)
{
    ET9UINT nCount;
    ET9SymbInfo const *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return 0;
    }

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {

        switch (pSymbInfo->eInputType)
        {
            case ET9REGIONALKEY:
                return 1;
            default:
                break;
        }

        /* check custom set */

        if (pSymbInfo->bNumBaseSyms > 1) {

            ET9U8 bSymFreq;
            ET9UINT nBaseCount;
            ET9DataPerBaseSym const *pDPBS;

            nBaseCount = pSymbInfo->bNumBaseSyms;
            pDPBS = &pSymbInfo->DataPerBaseSym[0];

            bSymFreq = pDPBS->bSymFreq;

            --nBaseCount;
            ++pDPBS;

            for (; nBaseCount; --nBaseCount, ++pDPBS) {
                if (!pDPBS->bLimited && pDPBS->bSymFreq != bSymFreq) {
                    return 1;
                }
            }
        }
    }

    return 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                             
 *
 *                                                                                                                                                                    
 *
 *                                         
 */

ET9BOOL ET9FARCALL _ET9HasDiscreteOnlyInfo(ET9WordSymbInfo const * const pWordSymbInfo)
{
    ET9UINT nCount;
    ET9SymbInfo const *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return 0;
    }

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {

        if (pSymbInfo->bTraceIndex) {
            return 0;
        }

        switch (pSymbInfo->eInputType)
        {
            case ET9DISCRETEKEY:
                continue;
            case ET9CUSTOMSET:
                break;
            default:
                return 0;
        }

        /* check custom set */

        if (pSymbInfo->bNumBaseSyms) {

            ET9UINT nBaseCount;
            ET9DataPerBaseSym const *pDPBS;
            const ET9U8 bSymFreq = pSymbInfo->DataPerBaseSym[0].bSymFreq;

            pDPBS = &pSymbInfo->DataPerBaseSym[1];
            for (nBaseCount = pSymbInfo->bNumBaseSyms - 1; nBaseCount; --nBaseCount, ++pDPBS) {
                if (!pDPBS->bLimited && pDPBS->bSymFreq != bSymFreq) {
                    return 0;
                }
            }
        }
    }

    return 1;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                            
 *
 *                                                                                                                                                                    
 *
 *                                         
 */

ET9BOOL ET9FARCALL _ET9HasAllShiftedInfo(ET9WordSymbInfo const * const pWordSymbInfo)
{
    ET9UINT nCount;
    ET9UINT nShiftCount;
    ET9SymbInfo const *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return 0;
    }

    nShiftCount = 0;

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {

        switch (pSymbInfo->bSymbType)
        {
            case ET9KTPUNCTUATION:
            case ET9KTNUMBER:
            case ET9KTSTRING:
            case ET9KTSMARTPUNCT:
                continue;
        }

        if (!pSymbInfo->eShiftState) {
            return 0;
        }

        ++nShiftCount;
    }

    return (nShiftCount) ? 1 : 0;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                            
 *
 *                                                                                                                                                                    
 *
 *                                         
 */

ET9BOOL ET9FARCALL _ET9HasExplicitNumber(ET9WordSymbInfo const * const pWordSymbInfo)
{
    ET9UINT nCount;
    ET9SymbInfo const *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return 0;
    }

    if (!pWordSymbInfo->bNumSymbs) {
        return 0;
    }

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {

        ET9U8 bNumBaseSyms;

        for (bNumBaseSyms = pSymbInfo->bNumBaseSyms; bNumBaseSyms > 1; --bNumBaseSyms) {

            if (!pSymbInfo->DataPerBaseSym[bNumBaseSyms - 1].bLimited) {
                break;
            }
        }

        if (bNumBaseSyms != 1) {
            return 0;
        }

        if (pSymbInfo->DataPerBaseSym[0].bNumSymsToMatch == 1 &&
            _ET9_IsNumeric(pSymbInfo->DataPerBaseSym[0].sChar[0])) {

            continue;
        }

        if (pSymbInfo->DataPerBaseSym[0].bNumSymsToMatch == 2 &&
            pSymbInfo->DataPerBaseSym[0].sChar[0] == pSymbInfo->DataPerBaseSym[0].sChar[1] &&
            _ET9_IsNumeric((pSymbInfo)->DataPerBaseSym[0].sChar[0])) {

            continue;
        }

        return 0;
    }

    return 1;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                                                    
 *
 *                                                                                                                                                                    
 *
 *                                        
 */

ET9BOOL ET9FARCALL _ET9IsInhibitTapOverrideAfterTrace(ET9WordSymbInfo const * const pWordSymbInfo)
{
    const ET9UINT nDecrementCount = ET9_INPUT_TRACK_SIZE - 1;

    ET9UINT nCurrIndex = pWordSymbInfo->Private.bCurrInputTrackIndex;

    nCurrIndex = (nCurrIndex + nDecrementCount) % ET9_INPUT_TRACK_SIZE;

    if (pWordSymbInfo->Private.peInputTracks[nCurrIndex] != ET9InputTrack_trace) {
        return 0;
    }

    nCurrIndex = (nCurrIndex + nDecrementCount) % ET9_INPUT_TRACK_SIZE;

    if (pWordSymbInfo->Private.peInputTracks[nCurrIndex] != ET9InputTrack_trace) {
        return 0;
    }

    if (_ET9HasTraceInfo(pWordSymbInfo)) {
        return 0;
    }

    return 1;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                 
 *
 *                                                                                                                                                                    
 *
 *                                        
 */

ET9BOOL ET9FARCALL _ET9IsInhibitDelayedReorderAfterTrace(ET9WordSymbInfo const * const pWordSymbInfo)
{
#if 0

    ET9UINT nTraceCount = 0;

    ET9UINT nIndex;

    for (nIndex = 0; nIndex < ET9_INPUT_TRACK_SIZE; ++nIndex) {
        if (pWordSymbInfo->Private.peInputTracks[nIndex] == ET9InputTrack_trace) {
            ++nTraceCount;
        }
    }

    if (nTraceCount < 2) {
        return 0;
    }

    if (_ET9HasTraceInfo(pWordSymbInfo)) {
        return 0;
    }

    return 1;

#else

    ET9_UNUSED(pWordSymbInfo);

    return 0;

#endif
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                   
 *
 *                                                                                                                                                                    
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL _ET9SettingsInhibited(ET9WordSymbInfo const * const pWordSymbInfo)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (pWordSymbInfo->Private.bRequiredLocate && pWordSymbInfo->bNumSymbs) {

#ifdef ET9_VERIFY_INHIBITED_SETTINGS
        ET9Assert(0);
#endif

        return ET9STATUS_SETTINGS_INHIBITED;
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                             
 *
 *                                                                        
 *                                                
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL _ET9_SetAutoLocale(ET9WordSymbInfo   * const pWordSymbInfo,
                                        const ET9U32              dwLocale)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (!pWordSymbInfo->Private.bManualLocale) {
        pWordSymbInfo->Private.dwLocale = dwLocale;
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                        
 *
 *                                                                
 *                                                                
 *
 *                                                                    
 */

void ET9FARCALL _ET9_GetDefaultLocale(ET9U32 * const pdwLocale, ET9BOOL * const pbManual)
{
#if 0
#if defined(ET9_CHINESE_MODULE)
    *pdwLocale = ET9PLIDChineseTraditional;
    *pbManual = 1;
#elif defined(ET9_KOREAN_MODULE)
    *pdwLocale = ET9PLIDKorean;
    *pbManual = 1;
#else
    *pdwLocale = 0;
    *pbManual = 0;
#endif
#else
    *pdwLocale = 0;
    *pbManual = 0;
#endif
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                        
 *
 *                                                                        
 *                                                
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL ET9SetLocale(ET9WordSymbInfo   * const pWordSymbInfo,
                                  const ET9U32              dwLocale)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (pWordSymbInfo->Private.bManualLocale &&
        pWordSymbInfo->Private.dwLocale == dwLocale) {

        return ET9STATUS_NONE;
    }

    /* set info */

    pWordSymbInfo->Private.dwLocale = dwLocale;
    pWordSymbInfo->Private.bManualLocale = 1;

    /* done */

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                        
 *
 *                                                                        
 *                                                                            
 *
 *                                                                    
 */

ET9STATUS ET9FARCALL ET9GetLocale(ET9WordSymbInfo   * const pWordSymbInfo,
                                  ET9U32            * const pdwLocale)
{
    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return ET9STATUS_INVALID_MEMORY;
    }

    if (!pdwLocale) {
        return ET9STATUS_INVALID_MEMORY;
    }

    *pdwLocale = pWordSymbInfo->Private.dwLocale;

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                                    
 *
 *                                                                        
 *
 *             
 */

void ET9FARCALL _ET9ClearShiftInfo(ET9WordSymbInfo * const pWordSymbInfo)
{
    ET9UINT nCount;
    ET9SymbInfo *pSymbInfo;

    if (!pWordSymbInfo || pWordSymbInfo->wInitOK != ET9GOODSETUP) {
        return;
    }

    pSymbInfo = pWordSymbInfo->SymbsInfo;
    for (nCount = pWordSymbInfo->bNumSymbs; nCount; --nCount, ++pSymbInfo) {
        pSymbInfo->eShiftState = ET9NOSHIFT;
        pSymbInfo->bAutoDowncase = 0;
        pSymbInfo->bForcedLowercase = 0;
    }
}

/*---------------------------------------------------------------------------*/
/**
 * Set caps gesture allowed.
 *
 * @param pWordSymbInfo             Pointer to word symbol info structure.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */

ET9STATUS ET9FARCALL ET9SetCapsGesture(ET9WordSymbInfo  * const pWordSymbInfo)
{
    ET9STATUS eStatus;

    eStatus = _ET9SettingsInhibited(pWordSymbInfo);

    if (eStatus) {
        return eStatus;
    }

    if (!pWordSymbInfo->Private.bStateCapsGesture) {

        pWordSymbInfo->Private.bStateCapsGesture = 1;

        _ET9InvalidateSymbInfo(pWordSymbInfo);
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * Clear caps gesture allowed.
 *
 * @param pWordSymbInfo             Pointer to word symbol info structure.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */

ET9STATUS ET9FARCALL ET9ClearCapsGesture(ET9WordSymbInfo  * const pWordSymbInfo)
{
    ET9STATUS eStatus;

    eStatus = _ET9SettingsInhibited(pWordSymbInfo);

    if (eStatus) {
        return eStatus;
    }

    if (pWordSymbInfo->Private.bStateCapsGesture) {

        pWordSymbInfo->Private.bStateCapsGesture = 0;

        _ET9InvalidateSymbInfo(pWordSymbInfo);
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * Set shift gesture allowed.
 *
 * @param pWordSymbInfo             Pointer to word symbol info structure.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */

ET9STATUS ET9FARCALL ET9SetShiftGesture(ET9WordSymbInfo  * const pWordSymbInfo)
{
    ET9STATUS eStatus;

    eStatus = _ET9SettingsInhibited(pWordSymbInfo);

    if (eStatus) {
        return eStatus;
    }

    if (!pWordSymbInfo->Private.bStateShiftGesture) {

        pWordSymbInfo->Private.bStateShiftGesture = 1;

        _ET9InvalidateSymbInfo(pWordSymbInfo);
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * Clear shift gesture allowed.
 *
 * @param pWordSymbInfo             Pointer to word symbol info structure.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */

ET9STATUS ET9FARCALL ET9ClearShiftGesture(ET9WordSymbInfo  * const pWordSymbInfo)
{
    ET9STATUS eStatus;

    eStatus = _ET9SettingsInhibited(pWordSymbInfo);

    if (eStatus) {
        return eStatus;
    }

    if (pWordSymbInfo->Private.bStateShiftGesture) {

        pWordSymbInfo->Private.bStateShiftGesture = 0;

        _ET9InvalidateSymbInfo(pWordSymbInfo);
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * Set free symbols (apostrophes, periods etc).
 *
 * @param pWordSymbInfo             Pointer to word symbol info structure.
 * @param eValue                    Value to set.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */

ET9STATUS ET9FARCALL ET9SetFreeSymbols(ET9WordSymbInfo  * const pWordSymbInfo, const ET9_SettingValue eValue)
{
    ET9STATUS eStatus;

    eStatus = _ET9SettingsInhibited(pWordSymbInfo);

    if (eStatus) {
        return eStatus;
    }

    if (eValue <= ET9_SettingValue_First || eValue >= ET9_SettingValue_Last) {
        return ET9STATUS_BAD_PARAM;
    }

    if (pWordSymbInfo->Private.eStateFreeSymbols != eValue) {

        pWordSymbInfo->Private.eStateFreeSymbols = eValue;

        _ET9InvalidateSymbInfo(pWordSymbInfo);
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/**
 * Set free double letters.
 *
 * @param pWordSymbInfo             Pointer to word symbol info structure.
 * @param eValue                    Value to set.
 *
 * @return ET9STATUS_NONE on success, otherwise return ET9 error code.
 */

ET9STATUS ET9FARCALL ET9SetFreeDoubleLetters(ET9WordSymbInfo  * const pWordSymbInfo, const ET9_SettingValue eValue)
{
    ET9STATUS eStatus;

    eStatus = _ET9SettingsInhibited(pWordSymbInfo);

    if (eStatus) {
        return eStatus;
    }

    if (eValue <= ET9_SettingValue_First || eValue >= ET9_SettingValue_Last) {
        return ET9STATUS_BAD_PARAM;
    }

    if (pWordSymbInfo->Private.eStateFreeDoubleLetters != eValue) {

        pWordSymbInfo->Private.eStateFreeDoubleLetters = eValue;

        _ET9InvalidateSymbInfo(pWordSymbInfo);
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                                     
 *
 *                                                                                                      
 *                                                                  
 *                                                               
 *                                                           
 *
 *                                                  
 */

ET9STATUS ET9FARCALL _ET9_GetKeyPositionForSymbol(ET9WordSymbInfo   * const pWordSymbInfo,
                                                  const ET9SYMB             sSymbol,
                                                  ET9TracePoint     * const pPoint,
                                                  _ET9KeyLookupKind * const peLookupKind)
{
    ET9KdbLayoutInfo const * const pLayoutInfo = &pWordSymbInfo->Private.sCurrKeyboardLayout;

    ET9Assert(pWordSymbInfo);
    ET9Assert(pPoint);

    if (!pLayoutInfo->bOk) {
        return ET9STATUS_ERROR;
    }

    {
        const ET9SYMB sSymbolLC = _ET9SymToLower(sSymbol, pWordSymbInfo->Private.dwLocale);

        ET9UINT nCount;
        ET9KdbAreaInfo const * pArea;
        ET9KdbAreaInfo const * pBestArea = NULL;

        /* within cache? (performance issue) */

        if (sSymbolLC >= _ET9MAXKEYPOSCACHE) {
            return ET9STATUS_OUT_OF_RANGE;
        }

        /* in cache? */

        if (sSymbolLC < _ET9MAXKEYPOSCACHE &&
            pWordSymbInfo->Private.pKeyPositionLookupCache[sSymbolLC]) {

            pPoint->nX = pWordSymbInfo->Private.pKeyPositionLookupCache[sSymbolLC]->nCenterX;
            pPoint->nY = pWordSymbInfo->Private.pKeyPositionLookupCache[sSymbolLC]->nCenterY;

            *peLookupKind = (_ET9KeyLookupKind)pWordSymbInfo->Private.pbKeyKindLookupCache[sSymbolLC];

            ET9Assert(*peLookupKind != ET9KeyLookupKind_Undef);

            return ET9STATUS_NONE;
        }

        /* actual lookup */

        pArea = pLayoutInfo->pKeyAreas;
        for (nCount = pLayoutInfo->nKeyAreaCount; nCount; --nCount, ++pArea) {

            {
                ET9SYMB *psSymb;
                ET9UINT nCharCount;

                psSymb = pArea->psChars;                                                        /* ignore invalid Insure++ warnings */
                for (nCharCount = pArea->nCharCount; nCharCount; --nCharCount, ++psSymb) {      /* ignore invalid Insure++ warnings */
                    if (sSymbolLC == *psSymb) {                                                 /* ignore invalid Insure++ warnings */
                        pBestArea = pArea;
                        break;
                    }
                }

                /* initial? */

                if (nCharCount && nCharCount == pArea->nCharCount) {
                    break;
                }
            }
        }

        if (!pBestArea) {
            return ET9STATUS_ERROR;
        }

        *peLookupKind = _ET9_IsFree(sSymbolLC) ? ET9KeyLookupKind_Free : ET9KeyLookupKind_Std;

        pPoint->nX = pBestArea->nCenterX;
        pPoint->nY = pBestArea->nCenterY;

        if (sSymbolLC < _ET9MAXKEYPOSCACHE) {
            pWordSymbInfo->Private.pbKeyKindLookupCache[sSymbolLC] = (ET9U8)*peLookupKind;
            pWordSymbInfo->Private.pKeyPositionLookupCache[sSymbolLC] = pBestArea;
        }
    }

    return ET9STATUS_NONE;
}

/*---------------------------------------------------------------------------*/
/*  -IDR-    
 *                                                      
 *
 *                                                                                                                                                                    
 *                                                                              
 *
 *                                         
 */

ET9BOOL ET9FARCALL _ET9HasSpaceInRegion(ET9WordSymbInfo const * const pWordSymbInfo, const ET9UINT bSymbIndex)
{
    ET9UINT bBaseSymbIndex;
    ET9UINT bAltSymbIndex;
    ET9SymbInfo const * const pSymbInfo = &pWordSymbInfo->SymbsInfo[bSymbIndex];

    ET9Assert(pWordSymbInfo);
    ET9Assert(bSymbIndex < ET9MAXWORDSIZE);
    ET9Assert(pSymbInfo);

    if (bSymbIndex >= pWordSymbInfo->bNumSymbs) {
        return 0;
    }

    for (bBaseSymbIndex = 0; bBaseSymbIndex < pSymbInfo->bNumBaseSyms; ++bBaseSymbIndex) {

        ET9DataPerBaseSym const * const pDataPerBaseSym = &pSymbInfo->DataPerBaseSym[bBaseSymbIndex];

        ET9Assert(pDataPerBaseSym);

        for (bAltSymbIndex = 0; bAltSymbIndex < pDataPerBaseSym->bNumSymsToMatch; ++bAltSymbIndex) {

            if (pDataPerBaseSym->sChar[bAltSymbIndex] == 0x20) {
                return 1;
            }
        }
    }

    return 0;
}

/*! @} */
/* ----------------------------------< eof >--------------------------------- */
