#pragma once


class CTextEditor;

typedef struct {
    ITfRange *pRange;
    ITfMouseSink *pMouseSink;
} MOUSESINK;
    

class CTextStore : public ITextStoreACP,
                   public ITfContextOwnerCompositionSink,
                   public ITfMouseTrackerACP
{
public:
     CTextStore(CTextEditor *pEditor) 
     {
         _prgMouseSinks = NULL;
         _nMouseSinks = 0;
         _pEditor = pEditor;
         _pCurrentCompositionView = NULL;
         _cRef = 1;
     }
     ~CTextStore() {}


    //
    // IUnknown methods
    //
    STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);

    //
    // ITextStoreACP
    //
    STDMETHODIMP    AdviseSink(REFIID riid, IUnknown *punk, DWORD dwMask);
    STDMETHODIMP    UnadviseSink(IUnknown *punk);
    STDMETHODIMP    RequestLock(DWORD dwLockFlags, HRESULT *phrSession);
    STDMETHODIMP    GetStatus(TS_STATUS *pdcs);
    STDMETHODIMP    QueryInsert(LONG acpInsertStart, LONG acpInsertEnd, ULONG cch, LONG *pacpResultStart, LONG *pacpResultEnd);
    STDMETHODIMP    GetSelection(ULONG ulIndex, ULONG ulCount, TS_SELECTION_ACP *pSelection, ULONG *pcFetched);
    STDMETHODIMP    SetSelection(ULONG ulCount, const TS_SELECTION_ACP *pSelection);
    STDMETHODIMP    GetText(LONG acpStart, LONG acpEnd, __out_ecount(cchPlainReq) WCHAR *pchPlain, ULONG cchPlainReq, ULONG *pcchPlainOut, TS_RUNINFO *prgRunInfo,
        ULONG ulRunInfoReq, ULONG *pulRunInfoOut, LONG *pacpNext);
    STDMETHODIMP    SetText(DWORD dwFlags, LONG acpStart, LONG acpEnd, __in_ecount(cch) const WCHAR *pchText, ULONG cch, TS_TEXTCHANGE *pChange);
    STDMETHODIMP    GetFormattedText(LONG acpStart, LONG acpEnd, IDataObject **ppDataObject);
    STDMETHODIMP    GetEmbedded(LONG acpPos, REFGUID rguidService, REFIID riid, IUnknown **ppunk);
    STDMETHODIMP    InsertEmbedded(DWORD dwFlags, LONG acpStart, LONG acpEnd, IDataObject *pDataObject, TS_TEXTCHANGE *pChange);
    STDMETHODIMP    RequestSupportedAttrs(DWORD dwFlags, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs);
    STDMETHODIMP    RequestAttrsAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags);
    STDMETHODIMP    RequestAttrsTransitioningAtPosition(LONG acpPos, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs, DWORD dwFlags);
    STDMETHODIMP    FindNextAttrTransition(LONG acpStart, LONG acpHalt, ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs,
        DWORD dwFlags, LONG *pacpNext, BOOL *pfFound, LONG *plFoundOffset);
    STDMETHODIMP    RetrieveRequestedAttrs(ULONG ulCount, TS_ATTRVAL *paAttrVals, ULONG *pcFetched);
    STDMETHODIMP    GetEndACP(LONG *pacp);
    STDMETHODIMP    GetActiveView(TsViewCookie *pvcView);
    STDMETHODIMP    GetACPFromPoint(TsViewCookie vcView, const POINT *pt, DWORD dwFlags, LONG *pacp);
    STDMETHODIMP    GetTextExt(TsViewCookie vcView, LONG acpStart, LONG acpEnd, RECT *prc, BOOL *pfClipped);
    STDMETHODIMP    GetScreenExt(TsViewCookie vcView, RECT *prc);
    STDMETHODIMP    GetWnd(TsViewCookie vcView, HWND *phwnd);
    STDMETHODIMP    QueryInsertEmbedded(const GUID *pguidService, const FORMATETC *pFormatEtc, BOOL *pfInsertable);
    STDMETHODIMP    InsertTextAtSelection(DWORD dwFlags, __in_ecount(cch) const WCHAR *pchText, ULONG cch, LONG *pacpStart,
        LONG *pacpEnd, TS_TEXTCHANGE *pChange);
    STDMETHODIMP    InsertEmbeddedAtSelection(DWORD dwFlags, IDataObject *pDataObject, LONG *pacpStart, 
        LONG *pacpEnd, TS_TEXTCHANGE *pChange);

    //
    // ITfContextOwnerCompositionSink
    //
    STDMETHODIMP OnStartComposition(ITfCompositionView *pComposition,
                                    BOOL *pfOk);

    STDMETHODIMP OnUpdateComposition(ITfCompositionView *pComposition,
                                     ITfRange *pRangeNew);

    STDMETHODIMP OnEndComposition(ITfCompositionView *pComposition);

    //
    // ITfMouseTrackerACP
    //
    STDMETHODIMP AdviseMouseSink(ITfRangeACP *range, ITfMouseSink *pSink, DWORD *pdwCookie);
    STDMETHODIMP UnadviseMouseSink(DWORD dwCookie);

    void OnSelectionChange()
    {
        if (_pSink)
            _pSink->OnSelectionChange();
    }

    void OnTextChange(LONG acpStart, LONG acpOldEnd, LONG acpNewEnd)
    {
        if (_pSink)
        {
            TS_TEXTCHANGE textChange;
            textChange.acpStart = acpStart;
            textChange.acpOldEnd = acpOldEnd;
            textChange.acpNewEnd = acpNewEnd;
            _pSink->OnTextChange(0, &textChange);
        }
    }

    void OnLayoutChange()
    {
        if (_pSink)
        {
            _pSink->OnLayoutChange(TS_LC_CHANGE, 0x01);
        }
    }

    ITfCompositionView *GetCurrentCompositionView() {return _pCurrentCompositionView;}

    void OnMouseEvent(UINT uEdge, UINT uQuadrant, DWORD dwBtnState, BOOL *pbEaten)
    {
        *pbEaten = FALSE;
        for (UINT i = 1; i < _nMouseSinks;  i++)
        {
            if (_prgMouseSinks->pMouseSink)
            {
                _prgMouseSinks->pMouseSink->OnMouseEvent(uEdge, uQuadrant, dwBtnState, pbEaten);
                if (*pbEaten)
                    break;
            }
        }
    }

private:
    void PrepareAttributes(ULONG cFilterAttrs, const TS_ATTRID *paFilterAttrs);
   
    CTextEditor *_pEditor;

    TS_ATTRVAL _attrval[8];
    int _nAttrVals;

    ITextStoreACPSink *_pSink;

    ITfCompositionView *_pCurrentCompositionView;

    MOUSESINK *_prgMouseSinks;
    UINT _nMouseSinks;

    long _cRef;
};

