#include "private.h"
#include "TextStore.h"
#include "TextEditor.h"
#include "initguid.h"
#include "InputScope.h"
#include "tsattrs.h"

//+---------------------------------------------------------------------------
//
// IUnknown
//
//----------------------------------------------------------------------------

STDAPI CTextStore::QueryInterface(REFIID riid, void **ppvObj)
{
    *ppvObj = NULL;

    if (IsEqualIID(riid, IID_IUnknown) ||
        IsEqualIID(riid, IID_ITextStoreACP))
    {
        *ppvObj = (ITextStoreACP *)this;
    }
    else if (IsEqualIID(riid, IID_ITfContextOwnerCompositionSink))
    {
        *ppvObj = (ITfContextOwnerCompositionSink *)this;
    }
    else if (IsEqualIID(riid, IID_ITfMouseTrackerACP))
    {
        *ppvObj = (ITfMouseTrackerACP *)this;
    }

    if (*ppvObj)
    {
        AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

STDAPI_(ULONG) CTextStore::AddRef()
{
    return ++_cRef;
}

STDAPI_(ULONG) CTextStore::Release()
{
    long cr;

    cr = --_cRef;

    if (cr == 0)
    {
        delete this;
    }

    return cr;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::AdviseSink(REFIID riid, IUnknown *punk, DWORD dwMask)
{

    if (!IsEqualGUID(riid, IID_ITextStoreACPSink))
    {
        return TS_E_NOOBJECT;
    }

    if (FAILED(punk->QueryInterface(IID_ITextStoreACPSink, (void **)&_pSink)))
    {
        return E_NOINTERFACE;
    }

    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::UnadviseSink(IUnknown *punk)
{
    // we're dealing with TSF. We don't have to check punk is same instance of _pSink.
    _pSink->Release();
    _pSink = NULL;

    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::RequestLock(DWORD dwLockFlags, HRESULT *phrSession)
{
    if (!_pSink)
    {
        return E_UNEXPECTED;
    }

    *phrSession = _pSink->OnLockGranted(dwLockFlags);
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetStatus(TS_STATUS *pdcs)
{
    pdcs->dwDynamicFlags = 0;
    pdcs->dwStaticFlags = 0;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::QueryInsert(LONG acpInsertStart, LONG acpInsertEnd, ULONG cch, LONG *pacpResultStart, LONG *pacpResultEnd)
{
    *pacpResultStart = acpInsertStart;
    *pacpResultEnd = acpInsertEnd;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetSelection(ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched)
{
    *pcFetched = 0;
    if ((ulCount > 0) && ((ulIndex == 0) || (ulIndex == TS_DEFAULT_SELECTION)))
    {
        pSelection[0].acpStart = _pEditor->GetSelectionStart();
        pSelection[0].acpEnd = _pEditor->GetSelectionEnd();
        pSelection[0].style.ase = TS_AE_END;
        pSelection[0].style.fInterimChar = FALSE;
        *pcFetched = 1;
    }

    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::SetSelection(ULONG ulCount, const TS_SELECTION_ACP *pSelection)
{
    if (ulCount > 0)
    { 
        _pEditor->MoveSelection(pSelection[0].acpStart, pSelection[0].acpEnd);
        _pEditor->InvalidateRect();
    }

    return S_OK;;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetText(LONG acpStart, LONG acpEnd, __out_ecount(cchPlainReq) WCHAR *pchPlain, ULONG cchPlainReq, ULONG *pcchPlainOut, TS_RUNINFO *prgRunInfo, ULONG ulRunInfoReq, ULONG *pulRunInfoOut, LONG *pacpNext)
{

    if ((cchPlainReq == 0) && (ulRunInfoReq == 0))
    {
        return S_OK;
    }

    if (acpEnd == -1)
        acpEnd = _pEditor->GetTextLength();

    acpEnd = min(acpEnd, acpStart + (int)cchPlainReq);

    if ((acpStart != acpEnd) &&
        !_pEditor->GetText(acpStart, pchPlain, acpEnd - acpStart))
    {
        return E_FAIL;
    }

    *pcchPlainOut = acpEnd - acpStart;
    if (ulRunInfoReq)
    {
        prgRunInfo[0].uCount = acpEnd - acpStart;
        prgRunInfo[0].type = TS_RT_PLAIN;
        *pulRunInfoOut = 1;
    }

    *pacpNext = acpEnd;

    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::SetText(DWORD dwFlags, LONG acpStart, LONG acpEnd, __in_ecount(cch) const WCHAR *pchText, ULONG cch, TS_TEXTCHANGE *pChange)
{
    LONG acpRemovingEnd;

    if (acpStart > (LONG)_pEditor->GetTextLength())
        return E_INVALIDARG;

    acpRemovingEnd = min(acpEnd, (LONG)_pEditor->GetTextLength() + 1);
    if (!_pEditor->RemoveText(acpStart, acpRemovingEnd - acpStart))
        return E_FAIL;

    if (!_pEditor->InsertText(acpStart, pchText, cch))
        return E_FAIL;

    pChange->acpStart = acpStart;
    pChange->acpOldEnd = acpEnd;
    pChange->acpNewEnd = acpStart + cch;

    _pEditor->InvalidateRect();
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetFormattedText(LONG acpStart, LONG acpEnd, IDataObject **ppDataObject)
{
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetEmbedded(LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk)
{
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::InsertEmbedded(DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject, TS_TEXTCHANGE *pChange)
{
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::RequestSupportedAttrs(DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
{
    PrepareAttributes(cFilterAttrs, paFilterAttrs);
    if (!_nAttrVals)
        return S_FALSE;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::RequestAttrsAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags)
{
    PrepareAttributes(cFilterAttrs, paFilterAttrs);
    if (!_nAttrVals)
        return S_FALSE;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::RequestAttrsTransitioningAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags)
{
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::FindNextAttrTransition(LONG acpStart, LONG acpHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset)
{
    *pacpNext = 0;
    *pfFound = FALSE;
    *plFoundOffset = 0;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::RetrieveRequestedAttrs(ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched)
{
    *pcFetched = 0;
    for (int i = 0; (i < (int)ulCount) && (i < _nAttrVals) ; i++)
    {
        paAttrVals[i] = _attrval[i];
        *pcFetched++;
    }

    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetEndACP(LONG *pacp)
{
    *pacp = _pEditor->GetTextLength();
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetActiveView(TsViewCookie *pvcView)
{
    *pvcView = 0;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetACPFromPoint(TsViewCookie vcView, const POINT *pt, DWORD dwFlags, LONG *pacp)
{
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetTextExt(TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc, BOOL *pfClipped)
{
    RECT rcStart;
    RECT rcEnd;

    if (_pEditor->GetLayout()->RectFromCharPos(acpStart, &rcStart) &&
        _pEditor->GetLayout()->RectFromCharPos(acpEnd, &rcEnd))
    {
        if (rcStart.top == rcEnd.top)
        {
            prc->left = min(rcStart.left, rcEnd.left);
            prc->right = max(rcStart.right, rcEnd.right);
        }
        else
        {
            RECT rcClient;
            GetClientRect(_pEditor->GetWnd(), &rcClient);
            prc->left = rcClient.left;
            prc->right = rcClient.right;
        }
        prc->top = min(rcStart.top, rcEnd.top);
        prc->bottom = max(rcStart.bottom, rcEnd.bottom);
    }
    else
    {
        prc->left = 0;
        prc->right = 0;
        prc->top = 0;
        prc->bottom = 0;
    }

    ClientToScreen(_pEditor->GetWnd(), (POINT *)&prc->left);
    ClientToScreen(_pEditor->GetWnd(), (POINT *)&prc->right);
    *pfClipped = FALSE;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetScreenExt(TsViewCookie vcView, RECT *prc)
{
    GetClientRect(_pEditor->GetWnd(), prc);
    ClientToScreen(_pEditor->GetWnd(), (POINT *)&prc->left);
    ClientToScreen(_pEditor->GetWnd(), (POINT *)&prc->right);
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::GetWnd(TsViewCookie vcView, HWND *phwnd)
{
    *phwnd = _pEditor->GetWnd();
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::QueryInsertEmbedded(const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable)
{
    return E_NOTIMPL;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::InsertTextAtSelection(DWORD dwFlags, __in_ecount(cch) const WCHAR *pchText, ULONG cch, LONG *pacpStart, LONG *pacpEnd, TS_TEXTCHANGE *pChange)
{
    LONG acpStart = _pEditor->GetSelectionStart();
    LONG acpEnd = _pEditor->GetSelectionEnd();

    if (dwFlags & TS_IAS_QUERYONLY)
    {
        *pacpStart = acpStart;
        *pacpEnd = acpStart + cch;
        return S_OK;
    }

    if (!_pEditor->RemoveText(acpStart, acpEnd - acpStart))
        return E_FAIL;

    if (pchText && !_pEditor->InsertText(acpStart, pchText, cch))
        return E_FAIL;

    
    if (pacpStart)
    {
        *pacpStart = acpStart;
    }

    if (pacpEnd)
    {
        *pacpEnd = acpStart + cch;
    }

    if (pChange)
    {
        pChange->acpStart = acpStart;
        pChange->acpOldEnd = acpEnd;
        pChange->acpNewEnd = acpStart + cch;
    }

    _pEditor->MoveSelection(acpStart, acpStart + cch);
    _pEditor->InvalidateRect();
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::InsertEmbeddedAtSelection(DWORD dwFlags, IDataObject *pDataObject, LONG *pacpStart, 
        LONG *pacpEnd, TS_TEXTCHANGE *pChange)
{
    return E_NOTIMPL;
}


//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::AdviseMouseSink(ITfRangeACP *range, ITfMouseSink *pSink, DWORD *pdwCookie)
{
    DWORD dwCookie;
    if (!_prgMouseSinks)
    {
        _prgMouseSinks = (MOUSESINK *)LocalAlloc(LPTR, sizeof(MOUSESINK));
        if (!_prgMouseSinks)
        {
            return E_OUTOFMEMORY;
        }
        _nMouseSinks = 1;
        dwCookie = 1;
    }
    else
    {
        UINT i;
        for (i = 0; i < _nMouseSinks;i ++)
        {
            if (_prgMouseSinks[i].pRange == NULL)
            {
                dwCookie = i + 1;
                break;
            }
        }
        if (i == _nMouseSinks)
        {
            void *pvNew = LocalReAlloc(_prgMouseSinks, 
                                       (_nMouseSinks + 1) * sizeof(MOUSESINK), 
                                       LMEM_MOVEABLE | LMEM_ZEROINIT);
            if (!pvNew)
            {
                return E_OUTOFMEMORY;
            }
            _prgMouseSinks = (MOUSESINK *)pvNew;
            _nMouseSinks++;
            dwCookie = _nMouseSinks;
        }
    }

    _prgMouseSinks[dwCookie - 1].pRange = range;
    _prgMouseSinks[dwCookie - 1].pMouseSink = pSink;
    _prgMouseSinks[dwCookie - 1].pRange->AddRef();
    _prgMouseSinks[dwCookie - 1].pMouseSink->AddRef();
    *pdwCookie = dwCookie;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::UnadviseMouseSink(DWORD dwCookie)
{
    _prgMouseSinks[dwCookie - 1].pRange->Release();
    _prgMouseSinks[dwCookie - 1].pRange = NULL;

    _prgMouseSinks[dwCookie - 1].pMouseSink->Release();
    _prgMouseSinks[dwCookie - 1].pMouseSink = NULL;

    return S_OK;
}


//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

#define IF_ATTR_INPUTSCOPE           1
#define IF_ATTR_FONT_STYLE_HEIGHT    2
#define IF_ATTR_FONT_FACENAME        3
#define IF_ATTR_FONT_SIZEPTS         4
#define IF_ATTR_TEXT_READONLY        5
#define IF_ATTR_TEXT_ORIENTATION     6
#define IF_ATTR_TEXT_VERTICALWRITING 7

const GUID *c_rgSupportedAttr[7] = {&GUID_PROP_INPUTSCOPE,
                                    &TSATTRID_Font_Style_Height,
                                    &TSATTRID_Font_FaceName,
                                    &TSATTRID_Font_SizePts,
                                    &TSATTRID_Text_ReadOnly,
                                    &TSATTRID_Text_Orientation,
                                    &TSATTRID_Text_VerticalWriting};

void CTextStore::PrepareAttributes(ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs)
{
    _nAttrVals= 0;
    memset(_attrval, 0, sizeof(_attrval));

    for (int i = 0; i < ARRAYSIZE(c_rgSupportedAttr); i++)
    {
        if (cFilterAttrs)
        {
            BOOL fFound = FALSE;
            for (ULONG j = 0; j < cFilterAttrs; j++)
            {
                if (IsEqualGUID(*c_rgSupportedAttr[i], paFilterAttrs[j]))
                {
                    fFound = TRUE;
                    break;
                }
            }

            if (!fFound)
            {
                continue;
            }
        }

        _attrval[_nAttrVals].idAttr = *c_rgSupportedAttr[i];
        _attrval[_nAttrVals].dwOverlapId = i + 1;

        switch (i + 1)
        {
            case IF_ATTR_INPUTSCOPE:
                _attrval[_nAttrVals].varValue.vt = VT_UNKNOWN;
                _attrval[_nAttrVals].varValue.punkVal = NULL;
                break;

            case IF_ATTR_FONT_STYLE_HEIGHT:
                _attrval[_nAttrVals].varValue.vt = VT_I4;
                _attrval[_nAttrVals].varValue.lVal = _pEditor->GetLineHeight();
                break;

            case IF_ATTR_FONT_FACENAME:
                _attrval[_nAttrVals].varValue.vt = VT_BSTR;
                _attrval[_nAttrVals].varValue.bstrVal = NULL;
                break;

            case IF_ATTR_FONT_SIZEPTS:
                _attrval[_nAttrVals].varValue.vt = VT_I4;
                _attrval[_nAttrVals].varValue.lVal = (int)((double)_pEditor->GetLineHeight() / 96.0 * 72.0);
                break;

            case IF_ATTR_TEXT_READONLY:
                _attrval[_nAttrVals].varValue.vt = VT_BOOL;
                _attrval[_nAttrVals].varValue.bVal = FALSE;
                break;

            case IF_ATTR_TEXT_ORIENTATION:
                _attrval[_nAttrVals].varValue.vt = VT_I4;
                _attrval[_nAttrVals].varValue.lVal = 0;
                break;

            case IF_ATTR_TEXT_VERTICALWRITING:
                _attrval[_nAttrVals].varValue.vt = VT_BOOL;
                _attrval[_nAttrVals].varValue.bVal = FALSE;
                break;
        }

        _nAttrVals++;
    }

}


//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::OnStartComposition(ITfCompositionView *pComposition, BOOL *pfOk)
{
    if (_pCurrentCompositionView)
    {
        _pCurrentCompositionView->Release();
        _pCurrentCompositionView = NULL;
    }

    _pCurrentCompositionView = pComposition;
    _pCurrentCompositionView->AddRef();

    *pfOk = TRUE;
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::OnUpdateComposition(ITfCompositionView *pComposition,
                                ITfRange *pRangeNew)
{
    if (_pCurrentCompositionView)
    {
        _pCurrentCompositionView->Release();
        _pCurrentCompositionView = NULL;
    }

    _pCurrentCompositionView = pComposition;
    _pCurrentCompositionView->AddRef();
    return S_OK;
}

//----------------------------------------------------------------------------
//
//
//
//----------------------------------------------------------------------------

STDAPI CTextStore::OnEndComposition(ITfCompositionView *pComposition)
{
    if (_pCurrentCompositionView)
    {
        _pCurrentCompositionView->Release();
        _pCurrentCompositionView = NULL;
    }

    return S_OK;
}
