//+---------------------------------------------------------------------------
//
//  File:       candlst.cpp
//
//  Contents:   candidate list classes
//
//----------------------------------------------------------------------------

#include "private.h"
#include "candlstx.h"
#include "hanja.h"

//
// CCandidateStringEx
//

/*   C  C A N D I D A T E  S T R I N G  E X   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
CCandidateStringEx::CCandidateStringEx(int nIndex, LPCWSTR psz, LANGID langid, void *pv, IUnknown *punk)
{
    size_t cch = 0;
    StringCchLengthW((LPWSTR)psz, CIC_KOR_CANDSTR_MAX, &cch);

    m_psz = new WCHAR[cch+1];
    if (m_psz)
        StringCchCopyW(m_psz, cch+1, psz);
    m_langid = langid;
    m_pv = pv;

    m_punk = punk;
    if (m_punk)
       m_punk->AddRef();
    m_pszRead = NULL;
    m_pszInlineComment = NULL;
#if 0
    m_pszPopupComment = NULL;
    m_dwPopupCommentGroupID = 0;
    m_pszPrefix = NULL;
    m_pszSuffix = NULL;
#endif
    m_nIndex = nIndex;
    m_cRef = 1;
}


/*   ~  C  C A N D I D A T E  S T R I N G  E X   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
CCandidateStringEx::~CCandidateStringEx()
{
    if (m_punk)
       m_punk->Release();
    delete m_psz;
    delete m_pszRead;
    if (m_pszInlineComment != NULL) {
        delete m_pszInlineComment;
    }
    
#if 0
    if (m_pszPopupComment != NULL) {
        delete m_pszPopupComment;
    }

    if (m_pszPrefix != NULL) {
        delete m_pszPrefix;
    }

    if (m_pszSuffix != NULL) {
        delete m_pszSuffix;
    }
#endif
}


/*   Q U E R Y  I N T E R F A C E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI CCandidateStringEx::QueryInterface(REFIID riid, void **ppvObj)
{
    if (ppvObj == NULL)
        return E_POINTER;

    *ppvObj = NULL;

    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCandidateString))
        *ppvObj = SAFECAST(this, ITfCandidateString*);
    else 
        if (IsEqualGUID(riid, IID_ITfCandidateStringInlineComment))
            *ppvObj = SAFECAST(this, ITfCandidateStringInlineComment*);
    else 
        if (IsEqualGUID( riid, IID_ITfCandidateStringColor))
            *ppvObj = SAFECAST(this, ITfCandidateStringColor*);
#if 0
    else 
        if (IsEqualGUID( riid, IID_ITfCandidateStringPopupComment))
            *ppvObj = SAFECAST(this, ITfCandidateStringPopupComment*);
    else 
        if (IsEqualGUID( riid, IID_ITfCandidateStringFixture))
            *ppvObj = SAFECAST( this, ITfCandidateStringFixture*);
    else 
        if (IsEqualGUID( riid, IID_ITfCandidateStringIcon))
            *ppvObj = SAFECAST( this, ITfCandidateStringIcon*);
#endif

    if (*ppvObj == NULL)
        return E_NOINTERFACE;

    AddRef();
    return S_OK;
}


/*   A D D  R E F   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI_(ULONG) CCandidateStringEx::AddRef()
{
    m_cRef++;
    return m_cRef;
}


/*   R E L E A S E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI_(ULONG) CCandidateStringEx::Release()
{
    m_cRef--;
    if (0 < m_cRef) {
        return m_cRef;
    }

    delete this;
    return 0;    
}


/*   G E T  S T R I N G   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetString(BSTR *pbstr)
{
    *pbstr = SysAllocString(m_psz);
    return S_OK;
}


/*   G E T  I N D E X   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetIndex(ULONG *pnIndex)
{
    *pnIndex = m_nIndex;
    return S_OK;
}


/*   G E T  I N L I N E  C O M M E N T  S T R I N G  */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetInlineCommentString(BSTR *pbstr)
{
    if (m_pszInlineComment == NULL) {
        return S_FALSE;
    }

    *pbstr = SysAllocString(m_pszInlineComment);
    return S_OK;
}

#if 0
/*   G E T  P O P U P  C O M M E N T   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetPopupCommentString(BSTR *pbstr)
{
    if (m_pszPopupComment == NULL) {
        return S_FALSE;
    }

    *pbstr = SysAllocString(m_pszPopupComment);
    return S_OK;
}


/*   G E T  P O P U P  C O M M E N T  G R O U P  I  D   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetPopupCommentGroupID(DWORD *pdwGroupID)
{
    *pdwGroupID = m_dwPopupCommentGroupID;
    return S_OK;
}

#endif

/*   G E T  C O L O R   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetColor(CANDUICOLOR *pcol)
{
    // TODO: Set diferent color according to the Hanja category
    if (m_bHanjaCat == HANJA_K0)
        {
        pcol->type = CANDUICOL_SYSTEM;
        pcol->cr = COLOR_MENUTEXT;
        }
    else
        {
        pcol->type = CANDUICOL_COLORREF;

        // If button face is black
        if (GetSysColor(COLOR_3DFACE) == RGB(0,0,0)) 
            pcol->cr = RGB(0, 128, 255);
        else
            pcol->cr = RGB(0, 0, 255);
        }
    return S_OK;
}

#if 0
/*   G E T  P R E F I X  S T R I N G   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetPrefixString(BSTR *pbstr)
{
    if (m_pszPrefix == NULL) {
        return S_FALSE;
    }

    *pbstr = SysAllocString(m_pszPrefix);
    return S_OK;
}


/*   G E T  S U F F I X  S T R I N G   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetSuffixString(BSTR *pbstr)
{
    if (m_pszSuffix == NULL) {
        return S_FALSE;
    }

    *pbstr = SysAllocString(m_pszSuffix);
    return S_OK;
}

/*   G E T  I C O N   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::GetIcon( HICON *phIcon )
{
    if (m_hIcon == NULL) {
        return S_FALSE;
    }

    *phIcon = m_hIcon;
    return S_OK;
}

#endif

/*   S E T  R E A D I N G  S T R I N G   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::SetReadingString(LPCWSTR psz)
{
    if (psz == NULL)
        return S_FALSE;
    
    if (m_pszRead != NULL) {
        delete m_pszRead;
    }

    size_t cch = 0;
    StringCchLengthW((LPWSTR)psz, CIC_KOR_CANDSTR_MAX, &cch);
    m_pszRead = new WCHAR[cch+1];
    if (m_pszRead == NULL)
        return E_OUTOFMEMORY;
    StringCchCopyW(m_pszRead, cch+1, psz);

    return S_OK;
}

/*   S E T  I N L I N E  C O M M E N T   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::SetInlineComment(LPCWSTR psz)
{
    if (m_pszInlineComment != NULL)
        delete m_pszInlineComment;

    if (psz != NULL)
        {
        size_t cch = 0;
        StringCchLengthW((LPWSTR)psz, CIC_KOR_CANDSTR_MAX, &cch);
        m_pszInlineComment = new WCHAR[cch+1];
        if (m_pszInlineComment == NULL)
            return E_OUTOFMEMORY;
        StringCchCopyW(m_pszInlineComment, cch+1, psz);
        } 
    else 
        m_pszInlineComment = NULL;

    return S_OK;
}

#if 0
/*   S E T  P O P U P  C O M M E N T   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::SetPopupComment( LPCWSTR psz, DWORD dwGroupID )
{
    if (m_pszPopupComment != NULL) {
        delete m_pszPopupComment;
    }

    m_dwPopupCommentGroupID = 0;
    if (psz != NULL) {
        UINT cch = 0;
        StringCchLengthW((LPWSTR)psz, CIC_KOR_CANDSTR_MAX, &cch);
        m_pszPopupComment = new WCHAR[cch+1];
        if (m_pszPopupComment == NULL)
            return E_OUTOFMEMORY;
        StringCchCopyW(m_pszPopupComment, cch+1, psz);
        
        m_dwPopupCommentGroupID = dwGroupID;
    } 
    else {
        m_pszPopupComment = NULL;
    }
    return S_OK;
}


/*   S E T  P R E F I X  S T R I N G   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::SetPrefixString( LPCWSTR psz )
{
    if (m_pszPrefix != NULL) {
        delete m_pszPrefix;
    }

    if (psz != NULL) {
        UINT cch = 0;
        StringCchLengthW((LPWSTR)psz, CIC_KOR_CANDSTR_MAX, &cch);
        m_pszPrefix = new WCHAR[cch+1];
        if (m_pszPrefix == NULL)
            return E_OUTOFMEMORY;
        StringCchCopyW(m_pszPrefix, cch+1, psz);
    } 
    else {
        m_pszPrefix = NULL;
    }
    return S_OK;
}


/*   S E T  S U F F I X  S T R I N G   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::SetSuffixString( LPCWSTR psz )
{
    if (m_pszSuffix != NULL) {
        delete m_pszSuffix;
    }

    if (psz != NULL) {
        UINT cch = 0;
        StringCchLengthW((LPWSTR)psz, CIC_KOR_CANDSTR_MAX, &cch);
        m_pszSuffix = new WCHAR[cch+1];
        if (m_pszSuffix == NULL)
            return E_OUTOFMEMORY;
        StringCchCopyW(m_pszSuffix, cch+1, psz);
    } 
    else {
        m_pszSuffix = NULL;
    }
    return S_OK;
}

/*   S E T  I C O N   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateStringEx::SetIcon( HICON hIcon )
{
    m_hIcon = hIcon;
    return S_OK;
}

#endif

//
// CCandidateListEx
//

/*   C  C A N D I D A T E  L I S T  E X   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
CCandidateListEx::CCandidateListEx(CANDLISTCALLBACKEX pfnCallback, ITfContext *pic, ITfRange *pRange)
{
    m_pfnCallback = pfnCallback;
    m_pic = pic;
    m_pic->AddRef();
    m_pRange = pRange;
    m_pRange->AddRef();
    m_cRef = 1;
    m_iInitialSelection = 0;
    m_pExtraCand = NULL;
}


/*   ~  C  C A N D I D A T E  L I S T  E X   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
CCandidateListEx::~CCandidateListEx()
{
    m_pic->Release();
    m_pRange->Release();

    while(m_rgCandStr.Count())
        {
        CCandidateStringEx *pCandStringEx;
        
        pCandStringEx = m_rgCandStr.Get(0);
        pCandStringEx->Release();
        
        m_rgCandStr.Remove(0, 1);
        }

    if (m_pExtraCand != NULL)
        m_pExtraCand->Release();
}


/*   Q U E R Y  I N T E R F A C E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI CCandidateListEx::QueryInterface(REFIID riid, void **ppvObj)
{
    if (ppvObj == NULL)
        return E_POINTER;

    *ppvObj = NULL;

    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_ITfCandidateList))
        *ppvObj = SAFECAST(this, ITfCandidateList *);
    else 
    if (IsEqualIID(riid, IID_ITfCandidateListExtraCandidate))
        *ppvObj = SAFECAST(this, ITfCandidateListExtraCandidate *);


    if (*ppvObj == NULL) {
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
}


/*   A D D  R E F   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI_(ULONG) CCandidateListEx::AddRef()
{
    m_cRef++;
    return m_cRef;
}


/*   R E L E A S E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI_(ULONG) CCandidateListEx::Release()
{
    m_cRef--;
    if (0 < m_cRef) {
        return m_cRef;
    }

    delete this;
    return 0;    
}


/*   E N U M  C A N D I D A T E S   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::EnumCandidates(IEnumTfCandidates **ppEnum)
{
    HRESULT hr = S_OK;

    if (!(*ppEnum = new CEnumCandidatesEx(this))) {
        hr = E_OUTOFMEMORY;
    }
    return hr;
}


/*   G E T  C A N D I D A T E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::GetCandidate(ULONG nIndex, ITfCandidateString **ppCand)
{
    CCandidateStringEx *pCandString;
    UINT nCnt = m_rgCandStr.Count();
    if (nIndex >= nCnt)
        return E_FAIL;

    pCandString = m_rgCandStr.Get(nIndex);
    return pCandString->QueryInterface(IID_ITfCandidateString, (void **)ppCand
);
}


/*   G E T  C A N D I D A T E  N U M   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::GetCandidateNum(ULONG *pnCnt)
{
    *pnCnt = m_rgCandStr.Count();
    return S_OK;
}


/*   S E T  R E S U L T   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::SetResult(ULONG nIndex, TfCandidateResult imcr)
{
    if (m_pExtraCand && (nIndex == IEXTRACANDIDATE))
        {
        if (m_pfnCallback == NULL)
            return S_OK;

        return (m_pfnCallback)(m_pic, m_pRange, this, m_pExtraCand, imcr);
        }

    if (nIndex >= (UINT)m_rgCandStr.Count())
        return E_FAIL;

    if (m_pfnCallback == NULL)
        return S_OK;

    return (m_pfnCallback)(m_pic, m_pRange, this, m_rgCandStr.Get(nIndex), imcr);
}

/*   G E T  E X T R A  C A N D I D A T E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::GetExtraCandidate(ITfCandidateString **ppCand)
{
    if (ppCand == NULL)
        return E_POINTER;

    if (m_pExtraCand != NULL)
        return m_pExtraCand->QueryInterface(IID_ITfCandidateString, (void **)ppCand);

    return S_FALSE;
}


/*   A D D  S T R I N G   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::AddString( LPCWSTR psz, LANGID langid, void *pv, IUnknown *punk, CCandidateStringEx **ppCandStr )
{
    int nCnt = m_rgCandStr.Count();
    CCandidateStringEx *pCand = new CCandidateStringEx(nCnt, psz, langid, pv, punk);

    if (!pCand)
        return E_OUTOFMEMORY;

    m_rgCandStr.Insert(nCnt, 1);
    m_rgCandStr.Set(nCnt, pCand);

    if (ppCandStr) {
        *ppCandStr = pCand;
        (*ppCandStr)->AddRef();
    }
    return S_OK;
}



/*   S E T  I N I T I A L  S E L E C T I O N   */
/*------------------------------------------------------------------------------

    Set initial selection to open candidate UI
    (internal use method)

------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::SetInitialSelection(ULONG iSelection)
{
    m_iInitialSelection = iSelection;
    return S_OK;
}



/*   G E T  I N I T I A L  S E L E C T I O N   */
/*------------------------------------------------------------------------------

    Get initial selection to open candidate UI
    (internal use method)

------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::GetInitialSelection(ULONG *piSelection)
{
    if (piSelection == NULL) {
        return E_POINTER;
    }

    *piSelection = m_iInitialSelection;
    return S_OK;
}

/*   A D D  E X T R A  S T R I N G   */
/*------------------------------------------------------------------------------

    Create extra candidate string ("0-Ban Kouho")
    (internal use method)

------------------------------------------------------------------------------*/
HRESULT CCandidateListEx::AddExtraString(LPCWSTR psz, LANGID langid, void *pv, IUnknown *punk, CCandidateStringEx **ppCandStr)
{
    if (m_pExtraCand != NULL)
        return E_FAIL;

    m_pExtraCand = new CCandidateStringEx(IEXTRACANDIDATE, psz, langid, pv, punk);

    if (!m_pExtraCand)
        return E_OUTOFMEMORY;

    if (ppCandStr)
        {
        *ppCandStr = m_pExtraCand;
        (*ppCandStr)->AddRef();
        }
        
    return S_OK;
}

//
// CEnumCandidateEx
//

/*   C  E N U M  C A N D I D A T E S  E X   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
CEnumCandidatesEx::CEnumCandidatesEx(CCandidateListEx *pList)
{
    m_pList = pList;
    m_pList->AddRef();
    m_nCur = 0;

    m_cRef = 1;
}


/*   ~  C  E N U M  C A N D I D A T E S  E X   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
CEnumCandidatesEx::~CEnumCandidatesEx()
{
    m_pList->Release();
}


/*   Q U E R Y  I N T E R F A C E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI CEnumCandidatesEx::QueryInterface(REFIID riid, void **ppvObj)
{
    if (ppvObj == NULL)
        return E_POINTER;

    *ppvObj = NULL;

    if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IEnumTfCandidates)) {
        *ppvObj = SAFECAST(this, IEnumTfCandidates *);
    }

    if (*ppvObj == NULL) {
        return E_NOINTERFACE;
    }

    AddRef();
    return S_OK;
}

/*   A D D  R E F   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI_(ULONG) CEnumCandidatesEx::AddRef()
{
    m_cRef++;
    return m_cRef;
}


/*   R E L E A S E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
STDAPI_(ULONG) CEnumCandidatesEx::Release()
{
    m_cRef--;
    if (0 < m_cRef) {
        return m_cRef;
    }

    delete this;
    return 0;    
}


/*   C L O N E   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CEnumCandidatesEx::Clone(IEnumTfCandidates **ppEnum)
{
    return E_NOTIMPL;
}


/*   N E X T   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CEnumCandidatesEx::Next(ULONG ulCount, ITfCandidateString **ppCand, ULONG *pcFetched)
{
    ULONG cFetched = 0;

    while (cFetched < ulCount) {
        CCandidateStringEx *pCand;

        if (m_nCur >= m_pList->m_rgCandStr.Count())
            break;

        pCand = m_pList->m_rgCandStr.Get(m_nCur);
        if (FAILED(pCand->QueryInterface(IID_ITfCandidateString, (void **)ppCand)))
            break;

        ppCand++;
        cFetched++;
        m_nCur++;
    }

    if (pcFetched)
        *pcFetched = cFetched;

    return (cFetched == ulCount) ? S_OK : S_FALSE;
}


/*   R E S E T   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CEnumCandidatesEx::Reset()
{
    m_nCur = 0;
    return S_OK;
}


/*   S K I P   */
/*------------------------------------------------------------------------------



------------------------------------------------------------------------------*/
HRESULT CEnumCandidatesEx::Skip(ULONG ulCount)
{
    while (ulCount) {
        if (m_nCur >= m_pList->m_rgCandStr.Count())
            break;

        m_nCur++;
        ulCount--;
    }

    return ulCount ? S_FALSE : S_OK;
}