/*****************************************************************************
 *
 *  (C) COPYRIGHT MICROSOFT CORPORATION, 2000
 *
 *  TITLE:       comutils.h
 *
 *  VERSION:     1.0
 *
 *  AUTHOR:      LazarI
 *
 *  DATE:        23-Dec-2000
 *
 *  DESCRIPTION: COM templates & utilities
 *
 *****************************************************************************/

#ifndef _COMUTILS_H
#define _COMUTILS_H

// the generic smart pointers & handles
#include "gensph.h"

////////////////////////////////////////////////
// template class CUnknownMT<pQITable>
//
// milti-thread impl. of IUnknown
// with interlocking for the ref-counting.
//
template <const QITAB* pQITable>
class CUnknownMT
{
public:
    CUnknownMT(): m_cRefs(1) {}
    virtual ~CUnknownMT()   {}

    //////////////////
    // IUnknown
    //
    STDMETHODIMP Handle_QueryInterface(REFIID riid, void **ppv)
    {
        return QISearch(this, pQITable, riid, ppv);
    }
    STDMETHODIMP_(ULONG) Handle_AddRef()
    {
        return InterlockedIncrement(&m_cRefs);
    }
    STDMETHODIMP_(ULONG) Handle_Release()
    {
        ULONG cRefs = InterlockedDecrement(&m_cRefs);
        if( 0 == cRefs )
        {
            delete this;
        }
        return cRefs;
    }
private:
    LONG m_cRefs;
};

////////////////////////////////////////////////
// template class CUnknownST<pQITable>
//
// single-thread impl. of IUnknown
// no interlocking for the ref-counting.
//
template <const QITAB* pQITable>
class CUnknownST
{
public:
    CUnknownST(): m_cRefs(1) {}
    virtual ~CUnknownST()   {}

    //////////////////
    // IUnknown
    //
    STDMETHODIMP Handle_QueryInterface(REFIID riid, void **ppv)
    {
        return QISearch(this, pQITable, riid, ppv);
    }
    STDMETHODIMP_(ULONG) Handle_AddRef()
    {
        return m_cRefs++;
    }
    STDMETHODIMP_(ULONG) Handle_Release()
    {
        if( 0 == --m_cRefs )
        {
            delete this;
            return 0;
        }
        return m_cRefs;
    }
private:
    LONG m_cRefs;
};

#define QITABLE_DECLARE(className)  \
    class className##_QITable       \
    {                               \
    public:                         \
        static const QITAB qit[];   \
    };                              \


#define QITABLE_GET(className)      \
    className##_QITable::qit        \

#define QITABLE_BEGIN(className)                    \
    const QITAB className##_QITable::qit[] =        \
    {                                               \

#define QITABLE_END()                               \
        { 0 },                                      \
    };                                              \

#define IMPLEMENT_IUNKNOWN()                                \
    STDMETHODIMP QueryInterface(REFIID riid, void **ppv)    \
    { return Handle_QueryInterface(riid, ppv); }            \
    STDMETHODIMP_(ULONG) AddRef()                           \
    { return Handle_AddRef(); }                             \
    STDMETHODIMP_(ULONG) Release()                          \
    { return Handle_Release(); }                            \

#if FALSE
////////////////////////////////////////////////
// template class CEnumFormatEtc
//
// implementation for an IDataObject which
// supports SetData to different formats.
// - not implemented yet.
//
class CEnumFormatEtc: public IEnumFORMATETC
{
public:
    CEnumFormatEtc(IUnknown *pUnkOuter, const FORMATETC *pfetc, UINT uSize);
    ~CEnumFormatEtc();

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

    //////////////////
    // IEnumFORMATETC 
    //
    /* [local] */ 
    virtual HRESULT STDMETHODCALLTYPE Next( 
        /* [in] */ ULONG celt,
        /* [length_is][size_is][out] */ FORMATETC *rgelt,
        /* [out] */ ULONG *pceltFetched) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE Skip( 
        /* [in] */ ULONG celt) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE Reset( void) = 0;
    
    virtual HRESULT STDMETHODCALLTYPE Clone( 
        /* [out] */ IEnumFORMATETC **ppenum) = 0;

private:
    ULONG           m_cRefs;
    IUnknown       *m_pUnkOuter;
    LPFORMATETC     m_prgfe;
    ULONG           m_iCur;
    ULONG           m_cItems;
};
#endif // #if FALSE

////////////////////////////////////////////////
// template class CDataObj<MAX_FORMATS>
//
// implementation for an IDataObject which
// supports SetData to different formats.
//
template <int MAX_FORMATS = 32>
class CDataObj: public IDataObject
{
public:
    // construction/destruction
    CDataObj();
    ~CDataObj();

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

    //////////////////
    // IDataObject
    //
    /* [local] */ 
    virtual HRESULT STDMETHODCALLTYPE GetData(
        /* [unique][in] */ FORMATETC *pformatetcIn,
        /* [out] */ STGMEDIUM *pmedium);

    /* [local] */ 
    virtual HRESULT STDMETHODCALLTYPE GetDataHere(
        /* [unique][in] */ FORMATETC *pformatetc,
        /* [out][in] */ STGMEDIUM *pmedium);

    virtual HRESULT STDMETHODCALLTYPE QueryGetData(
        /* [unique][in] */ FORMATETC *pformatetc);

    virtual HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(
        /* [unique][in] */ FORMATETC *pformatectIn,
        /* [out] */ FORMATETC *pformatetcOut);

    /* [local] */ 
    virtual HRESULT STDMETHODCALLTYPE SetData(
        /* [unique][in] */ FORMATETC *pformatetc,
        /* [unique][in] */ STGMEDIUM *pmedium,
        /* [in] */ BOOL fRelease);

    virtual HRESULT STDMETHODCALLTYPE EnumFormatEtc(
        /* [in] */ DWORD dwDirection,
        /* [out] */ IEnumFORMATETC **ppenumFormatEtc);

    virtual HRESULT STDMETHODCALLTYPE DAdvise(
        /* [in] */ FORMATETC *pformatetc,
        /* [in] */ DWORD advf,
        /* [unique][in] */ IAdviseSink *pAdvSink,
        /* [out] */ DWORD *pdwConnection);

    virtual HRESULT STDMETHODCALLTYPE DUnadvise(
        /* [in] */ DWORD dwConnection);

    virtual HRESULT STDMETHODCALLTYPE EnumDAdvise(
        /* [out] */ IEnumSTATDATA **ppenumAdvise);

private:
    LONG m_cRefs;
    FORMATETC m_fmte[MAX_FORMATS];
    STGMEDIUM m_medium[MAX_FORMATS];
};


////////////////////////////////////////////////
// template class CSimpleDataObjImpl<T>
//
// simple implementation for an IDataObject
// and IDropSource which lives in memory.
//
template <class T>
class CSimpleDataObjImpl: public IDataObject,
                          public IDropSource
{
public:
    // construction/destruction
    CSimpleDataObjImpl(const T &data, CLIPFORMAT cfDataType, IDataObject *pDataObj = NULL);
    ~CSimpleDataObjImpl();

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

    //////////////////
    // IDataObject
    //
    /* [local] */ 
    virtual HRESULT STDMETHODCALLTYPE GetData(
        /* [unique][in] */ FORMATETC *pformatetcIn,
        /* [out] */ STGMEDIUM *pmedium);

    /* [local] */ 
    virtual HRESULT STDMETHODCALLTYPE GetDataHere(
        /* [unique][in] */ FORMATETC *pformatetc,
        /* [out][in] */ STGMEDIUM *pmedium);

    virtual HRESULT STDMETHODCALLTYPE QueryGetData(
        /* [unique][in] */ FORMATETC *pformatetc);

    virtual HRESULT STDMETHODCALLTYPE GetCanonicalFormatEtc(
        /* [unique][in] */ FORMATETC *pformatectIn,
        /* [out] */ FORMATETC *pformatetcOut);

    /* [local] */ 
    virtual HRESULT STDMETHODCALLTYPE SetData(
        /* [unique][in] */ FORMATETC *pformatetc,
        /* [unique][in] */ STGMEDIUM *pmedium,
        /* [in] */ BOOL fRelease);

    virtual HRESULT STDMETHODCALLTYPE EnumFormatEtc(
        /* [in] */ DWORD dwDirection,
        /* [out] */ IEnumFORMATETC **ppenumFormatEtc);

    virtual HRESULT STDMETHODCALLTYPE DAdvise(
        /* [in] */ FORMATETC *pformatetc,
        /* [in] */ DWORD advf,
        /* [unique][in] */ IAdviseSink *pAdvSink,
        /* [out] */ DWORD *pdwConnection);

    virtual HRESULT STDMETHODCALLTYPE DUnadvise(
        /* [in] */ DWORD dwConnection);

    virtual HRESULT STDMETHODCALLTYPE EnumDAdvise(
        /* [out] */ IEnumSTATDATA **ppenumAdvise);

    //////////////////
    // IDropSource
    //
    virtual HRESULT STDMETHODCALLTYPE QueryContinueDrag(
        /* [in] */ BOOL fEscapePressed,
        /* [in] */ DWORD grfKeyState);

    virtual HRESULT STDMETHODCALLTYPE GiveFeedback(
        /* [in] */ DWORD dwEffect);

private:
    LONG        m_cRefs;
    T           m_data;
    CLIPFORMAT  m_cfDataType;
    CRefPtrCOM<IDataObject> m_spDataObj;
};

// this namespace is a placeholder to put COM related helpers here
namespace comhelpers
{

BOOL AreObjectsIdentical(IUnknown *punk1, IUnknown *punk2);

}

// include the implementation of the template classes here
#include "comutils.inl"

#endif // endif _COMUTILS_H