/*
 *      dataobject.cxx
 *
 *
 *      Copyright (c) 1998-1999 Microsoft Corporation
 *
 *      PURPOSE:        Implements the CDataObject class
 *
 *
 *      OWNER:          ptousig
 */

#include <headers.hxx>

// -----------------------------------------------------------------------------
// static variables
UINT CBaseDataObject::s_cfAdminHscopeitem               = RegisterClipboardFormat(CF_EXCHANGE_ADMIN_HSCOPEITEM);        // The HSCOPEITEM of this node
UINT CBaseDataObject::s_cfMMCSnapinMachineName          = RegisterClipboardFormat(CF_MMC_SNAPIN_MACHINE_NAME);          // Format supplied by the Computer manager snapin. Passes in the name of the server.
UINT CBaseDataObject::s_cfDisplayName                   = RegisterClipboardFormat(CCF_DISPLAY_NAME);
UINT CBaseDataObject::s_cfNodeType                      = RegisterClipboardFormat(CCF_NODETYPE);
UINT CBaseDataObject::s_cfSzNodeType                    = RegisterClipboardFormat(CCF_SZNODETYPE);
UINT CBaseDataObject::s_cfSnapinClsid                   = RegisterClipboardFormat(CCF_SNAPIN_CLASSID);
UINT CBaseDataObject::s_cfNodeID                        = RegisterClipboardFormat(CCF_NODEID);
UINT CBaseDataObject::s_cfColumnSetId                   = RegisterClipboardFormat(CCF_COLUMN_SET_ID);
UINT CBaseDataObject::s_cfMultiSelectionItemTypes       = RegisterClipboardFormat(CCF_OBJECT_TYPES_IN_MULTI_SELECT);    // Multiselect - list of types for the selected nodes

// -----------------------------------------------------------------------------
HRESULT CBaseDataObject::GetDataHere(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
    DECLARE_SC(sc,_T(""));
    Trace(tagBaseSnapinIDataObject, _T("--> %S::IDataObject::GetDataHere(pformatetc->cfFormat=%s)"), SzGetSnapinItemClassName(), SzDebugNameFromFormatEtc(pformatetc->cfFormat));
    ADMIN_TRY;
    sc=ScGetDataHere(pformatetc, pmedium);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIDataObject, _T("<-- %S::IDataObject::GetDataHere is returning hr=%s"), SzGetSnapinItemClassName(), SzGetDebugNameOfHr(sc.ToHr()));

	if (sc == SC(DV_E_FORMATETC) )
	{
		sc.Clear();
        return DV_E_FORMATETC;
	}

    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CBaseDataObject::GetData(FORMATETC *pformatetc, STGMEDIUM *pmedium)
{
    DECLARE_SC(sc,_T(""));
    Trace(tagBaseSnapinIDataObject, _T("--> %S::IDataObject::GetData(pformatetc->cfFormat=%s)"), SzGetSnapinItemClassName(), SzDebugNameFromFormatEtc(pformatetc->cfFormat));
    ADMIN_TRY;
    sc=ScGetData(pformatetc, pmedium);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIDataObject, _T("<-- %S::IDataObject::GetData is returning hr=%s"), SzGetSnapinItemClassName(), SzGetDebugNameOfHr(sc.ToHr()));

	if (sc == SC(DV_E_FORMATETC) )
	{
		sc.Clear();
        return DV_E_FORMATETC;
	}

    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CBaseDataObject::QueryGetData(FORMATETC *pformatetc)
{
    DECLARE_SC(sc,_T(""));
    Trace(tagBaseSnapinIDataObject, _T("--> %S::IDataObject::QueryGetData(pformatetc->cfFormat=%s)"), SzGetSnapinItemClassName(), SzDebugNameFromFormatEtc(pformatetc->cfFormat));
    ADMIN_TRY;
    sc=ScQueryGetData(pformatetc);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIDataObject, _T("<-- %S::IDataObject::QueryGetData is returning hr=%s"), SzGetSnapinItemClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
HRESULT CBaseDataObject::EnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC* ppEnumFormatEtc)
{
    DECLARE_SC(sc,_T(""));
    Trace(tagBaseSnapinIDataObject, _T("--> %S::IDataObject::EnumFormatEtc(dwDirection=%d)"), SzGetSnapinItemClassName(), dwDirection);
    ADMIN_TRY;
    sc=ScEnumFormatEtc(dwDirection, ppEnumFormatEtc);
    ADMIN_CATCH_HR
    Trace(tagBaseSnapinIDataObject, _T("<-- %S::IDataObject::EnumFormatEtc is returning hr=%s"), SzGetSnapinItemClassName(), SzGetDebugNameOfHr(sc.ToHr()));
    return(sc.ToHr());
}

// -----------------------------------------------------------------------------
// Renders the data in a preallocated medium.
//
SC CBaseDataObject::ScGetDataHere(FORMATETC *pFormatEtc, STGMEDIUM *pMedium)
{
    SC sc = S_OK;

    // check parameters
    if (pFormatEtc == NULL || pMedium == NULL)
        return sc = E_INVALIDARG;

    const   CLIPFORMAT cf = pFormatEtc->cfFormat;
    CComPtr<IStream> spStream;
    HGLOBAL hGlobal = NULL;

    // see what kind of medium we have
    if (pFormatEtc->tymed == TYMED_ISTREAM)
    {
        // it's a stream
        spStream = pMedium->pstm;
        if (spStream == NULL)
        {
            sc = E_UNEXPECTED;
            goto Error;
        }
    }
    else if (pFormatEtc->tymed == TYMED_HGLOBAL)
    {
        // it's hGlobal
        hGlobal = pMedium->hGlobal;

        sc = CreateStreamOnHGlobal( hGlobal, FALSE, &spStream );
        if ( sc )
            goto Error;                                              // Minimal error checking
    }
    else // got the media we do not support
    {
        sc = DV_E_TYMED;
        goto Error;
    }

    pMedium->tymed = pFormatEtc->tymed;
    pMedium->pUnkForRelease = NULL;          // by OLE spec

    if (cf == s_cfDisplayName )
        sc = ScWriteDisplayName( spStream );

    else if ( cf == s_cfAdminHscopeitem )
        sc = ScWriteAdminHscopeitem( spStream );

    else if ( cf == s_cfNodeType )
        sc = ScWriteNodeType( spStream );

    else if ( cf == s_cfSzNodeType )
        sc = ScWriteSzNodeType( spStream );

    else if ( cf == s_cfSnapinClsid )
        sc = ScWriteClsid( spStream );

    else if ( cf == s_cfNodeID )
        sc = ScWriteNodeID( spStream );
    else if (cf == s_cfColumnSetId )
        sc = ScWriteColumnSetId( spStream );

    else if ( (cf == s_cfMultiSelectionItemTypes) && FIsMultiSelectDataObject())            // the clipboard format is enabled only for multiselect data objects
        sc = ScWriteMultiSelectionItemTypes( spStream );

	else if ( cf == CF_TEXT)
		sc = ScWriteAnsiName( spStream );

    else // Unknown format
    {
        // we will pretend to suport it for IStream based media (it probably comes from object model)
        if (pFormatEtc->tymed == TYMED_ISTREAM)
        {
            WCHAR szDescription[] = L"Sample Value For Requested Format Of: ";
            spStream->Write(szDescription, wcslen(szDescription) * sizeof(WCHAR), NULL);

            TCHAR szFormatName[512];
            int nChars = GetClipboardFormatName(cf, szFormatName, sizeof(szFormatName) / sizeof(szFormatName[0]));

            USES_CONVERSION;
            spStream->Write(T2W(szFormatName), nChars * sizeof(WCHAR), NULL);
        }
        else
        {
            sc = DV_E_FORMATETC;
            goto Cleanup;
        }
    }

    if (sc)
        goto Error;

    if (pFormatEtc->tymed == TYMED_HGLOBAL)
    {
        sc = GetHGlobalFromStream(spStream, &hGlobal);
        if (sc)
            goto Error;

        ASSERT(pMedium->hGlobal == NULL || pMedium->hGlobal == hGlobal);
        pMedium->hGlobal = hGlobal;
    }

Cleanup:
    return sc;
Error:
    if (sc == E_NOTIMPL)
    {
        sc = DV_E_FORMATETC; // Format not supported by this node
        goto Cleanup;
    }
    TraceError(_T("CBaseDataObject::GetDataHere"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Renders the data in a newly allocated medium.
//
SC CBaseDataObject::ScGetData(FORMATETC *pFormatEtc, STGMEDIUM *pmedium)
{
    SC sc = S_OK;

    pmedium->tymed = TYMED_HGLOBAL;
    pmedium->pUnkForRelease = NULL;
    pmedium->hGlobal = NULL;

    sc = ScGetDataHere(pFormatEtc, pmedium);

	if (sc == SC(DV_E_FORMATETC) )
	{
		sc.Clear();
        return DV_E_FORMATETC;
	}

    if (sc)
        goto Error;

Cleanup:
    return sc;
Error:
    TraceError(_T("CBaseDataObject::ScGetData"), sc);
    if (pmedium->hGlobal)
        GlobalFree(pmedium->hGlobal);
    pmedium->hGlobal = NULL;
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// Asks whether a given format is supported by this data object.
//
SC CBaseDataObject::ScQueryGetData(FORMATETC *pFormatEtc)
{
    SC                                      sc              = S_OK;
    const   CLIPFORMAT      cf              = pFormatEtc->cfFormat;


    if ( ( cf == s_cfDisplayName )               ||
         ( cf == s_cfNodeType )                  ||
         ( cf == s_cfSzNodeType )                ||
         ( cf == s_cfSnapinClsid )               ||
         ( cf == s_cfNodeID )                    ||
		 ( cf == CF_TEXT)                        ||
         ( (cf == s_cfMultiSelectionItemTypes) && FIsMultiSelectDataObject() )           // the clipboard format is enabled only for multiselect data objects
       )
    {
        sc = S_OK;                                                      // known and acceptable format
    }
    else
    {
        sc = S_FALSE;                                           // unknown or unacceptable format
    }

    return sc;
}

// -----------------------------------------------------------------------------
// Enumerates available clipboard format supported by this data object.
// Only implemented in DEBUG.
//
SC CBaseDataObject::ScEnumFormatEtc(DWORD dwDirection, LPENUMFORMATETC* ppEnumFormatEtc)
#ifdef DBG
{
    SC                                      sc              = S_OK;
    CComObject<CEnumFormatEtc>      *pEnum = NULL;

    ASSERT(ppEnumFormatEtc);

    sc = CComObject<CEnumFormatEtc>::CreateInstance(&pEnum);
    if (!pEnum)
        goto MemoryError;

    sc = pEnum->QueryInterface(__uuidof(IEnumFORMATETC),(void **) ppEnumFormatEtc );
    pEnum = NULL;
    if (sc)
        goto Error;

Cleanup:
    return sc;
MemoryError:
    if (pEnum)
        delete pEnum;
    pEnum = NULL;
Error:
    TraceError(_T("CBaseDataObject::ScEnumFormatEtc"), sc);
    goto Cleanup;

}
#else
{
    return E_NOTIMPL;
}
#endif


// -----------------------------------------------------------------------------
// A convenience function to extract a GUID of the specified clipboard format
// from a dataobject.
//
SC CBaseDataObject::ScGetGUID(UINT cf, LPDATAOBJECT lpDataObject, GUID *pguid)
{
    SC              sc              = S_OK;
    STGMEDIUM       stgmedium       = {TYMED_HGLOBAL,  NULL};
    FORMATETC       formatetc       = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    BYTE*           pb              = NULL;

    // validate parameters
    ASSERT(lpDataObject);
    ASSERT(pguid);

    // Allocate memory for the stream
    stgmedium.hGlobal = GlobalAlloc(GMEM_SHARE, sizeof(GUID));
    if (!stgmedium.hGlobal)
        goto MemoryError;

    // Attempt to get data from the object
    sc = lpDataObject->GetDataHere(&formatetc, &stgmedium);
	if (sc == SC(DV_E_FORMATETC) )
	{
		SC scNoTrace = sc;
		sc.Clear();
		return scNoTrace;
	}

    if (sc)
        goto Error;

    // Copy the GUID into the return buffer
    pb = (BYTE*) GlobalLock(stgmedium.hGlobal);
    CopyMemory(pguid, pb, sizeof(GUID));

Cleanup:
    if (pb)
        GlobalUnlock(stgmedium.hGlobal);
    if (stgmedium.hGlobal)
    {
        ASSERT(GlobalFree(stgmedium.hGlobal) == NULL);
    }
    stgmedium.hGlobal = NULL;

    return sc;
MemoryError:
    sc = E_OUTOFMEMORY;
Error:
    TraceError(_T("CBaseDataObject::ScGetGUID"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// A convenience function to extract a string of the specified clipboard format
// from a dataobject.
//
SC CBaseDataObject::ScGetString(UINT cf, LPDATAOBJECT lpDataObject, tstring& str)
{
    SC                      sc                      = S_OK;
    STGMEDIUM       stgmedium       = {TYMED_HGLOBAL,  NULL};
    FORMATETC       formatetc       = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    BYTE*                      pb                      = NULL;

    // validate parameters
    ASSERT(lpDataObject);

    // Allocate memory for the stream
    stgmedium.hGlobal = GlobalAlloc(GMEM_SHARE, str.length());
    if (!stgmedium.hGlobal)
        goto MemoryError;

    // Attempt to get data from the object
    sc = lpDataObject->GetData(&formatetc, &stgmedium);
    if (sc)
        goto Error;

    // copy the string into the return buffer
    pb = (BYTE*) GlobalLock(stgmedium.hGlobal);
	str = (LPTSTR)pb;

Cleanup:
    if (pb)
        GlobalUnlock(stgmedium.hGlobal);
    if (stgmedium.hGlobal)
    {
        ASSERT(GlobalFree(stgmedium.hGlobal) == NULL);
    }
    stgmedium.hGlobal = NULL;

    return sc;
MemoryError:
    sc = E_OUTOFMEMORY;
Error:
    TraceError(_T("CBaseDataObject::ScGetString"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// A convenience function to extract a bool of the specified clipboard format
// from a dataobject.
//
SC CBaseDataObject::ScGetBool(UINT cf, LPDATAOBJECT lpDataObject, BOOL *pf)
{
    SC                      sc                      = S_OK;
    STGMEDIUM       stgmedium       = {TYMED_HGLOBAL,  NULL};
    FORMATETC       formatetc       = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    BYTE*                      pb                      = NULL;

    // validate parameters
    ASSERT(lpDataObject);
    ASSERT(pf);

    // Allocate memory for the stream
    stgmedium.hGlobal = GlobalAlloc(GMEM_SHARE, sizeof(BOOL));
    if (!stgmedium.hGlobal)
        goto MemoryError;

    // Attempt to get data from the object
    sc = lpDataObject->GetDataHere(&formatetc, &stgmedium);
    if (sc)
        goto Error;

    // copy the BOOL into the return buffer
    pb = (BYTE*) GlobalLock(stgmedium.hGlobal);
    CopyMemory(pf, pb, sizeof(BOOL));

Cleanup:
    if (pb)
        GlobalUnlock(stgmedium.hGlobal);
    if (stgmedium.hGlobal)
    {
        ASSERT(GlobalFree(stgmedium.hGlobal) == NULL);
    }
    stgmedium.hGlobal = NULL;

    return sc;
MemoryError:
    sc = E_OUTOFMEMORY;
Error:
    TraceError(_T("CBaseDataObject::ScGetBool"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// A convenience function to extract a dword of the specified clipboard format
// from a dataobject.
//
SC CBaseDataObject::ScGetDword(UINT cf, LPDATAOBJECT lpDataObject, DWORD *pdw)
{
    SC                      sc                      = S_OK;
    STGMEDIUM       stgmedium       = {TYMED_HGLOBAL,  NULL};
    FORMATETC       formatetc       = {(CLIPFORMAT)cf, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    BYTE*                      pb                      = NULL;

    // validate parameters
    ASSERT(lpDataObject);
    ASSERT(pdw);

    // Allocate memory for the stream
    stgmedium.hGlobal = GlobalAlloc(GMEM_SHARE, sizeof(DWORD));
    if (!stgmedium.hGlobal)
        goto MemoryError;

    // Attempt to get data from the object
    sc = lpDataObject->GetDataHere(&formatetc, &stgmedium);
    if (sc)
        goto Error;

    // copy the DWORD into the return buffer
    pb = (BYTE*) GlobalLock(stgmedium.hGlobal);
    CopyMemory(pdw, pb, sizeof(DWORD));

Cleanup:
    if (pb)
        GlobalUnlock(stgmedium.hGlobal);
    if (stgmedium.hGlobal)
    {
        ASSERT(GlobalFree(stgmedium.hGlobal) == NULL);
    }
    stgmedium.hGlobal = NULL;

    return sc;
MemoryError:
    sc = E_OUTOFMEMORY;
Error:
    TraceError(_T("CBaseDataObject::ScGetDword"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// A convenience function to extract the SNodeID from a dataobject.
// The SNodeID will be allocated with PvAlloc() and needs to be freed by
// the caller.
//
SC CBaseDataObject::ScGetNodeID(LPDATAOBJECT lpDataObject, SNodeID **ppsnodeid)
{
    SC                      sc                      = S_OK;
    STGMEDIUM       stgmedium       = {TYMED_HGLOBAL, NULL, NULL};
    FORMATETC       formatetc       = {(CLIPFORMAT)s_cfNodeID, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    BYTE*           pb              = NULL;
    int             cb              = 0;
    SNodeID *       psnodeid        = NULL;

    // validate parameters
    ASSERT(lpDataObject);
    ASSERT(ppsnodeid);
    ASSERT(*ppsnodeid == NULL);

    // Attempt to get data from the object
    sc = lpDataObject->GetData(&formatetc, &stgmedium);
    if (sc)
        goto Error;

    // Get a pointer to the blob
    pb = (BYTE*) GlobalLock(stgmedium.hGlobal);
    psnodeid = (SNodeID *) pb;
    cb = sizeof(DWORD) + psnodeid->cBytes;

    // Allocate a new buffer with PvAlloc
    psnodeid = (SNodeID *) GlobalAlloc(GMEM_FIXED, cb);
    if (psnodeid == NULL)
        goto MemoryError;

    CopyMemory(psnodeid, pb, cb);

    // Transfer ownership to our caller
    *ppsnodeid = psnodeid;
    psnodeid = NULL;

Cleanup:
    if (pb)
        GlobalUnlock(stgmedium.hGlobal);
    if (stgmedium.hGlobal)
        GlobalFree(stgmedium.hGlobal);
    if (psnodeid)
        GlobalFree(psnodeid);
    return sc;
MemoryError:
    sc = E_OUTOFMEMORY;
Error:
    TraceError(_T("CBaseDataObject::ScGetNodeID"), sc);
    goto Cleanup;
}

// -----------------------------------------------------------------------------
// A convenience function to extract an MMC Column Set ID from a dataobject.
//
SC CBaseDataObject::ScGetColumnSetID(LPDATAOBJECT lpDataObject, SColumnSetID ** ppColumnSetID)
{
    SC                      sc          = S_OK;
    STGMEDIUM               stgmedium   = {TYMED_HGLOBAL,  NULL};
    FORMATETC               formatetc   = {(CLIPFORMAT)s_cfColumnSetId, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
    BYTE*                   pb          = NULL;
    SColumnSetID *  pColumnSetID        = NULL;
    int                     cb          = 0;

    // validate parameters
    ASSERT(lpDataObject);
    ASSERT(ppColumnSetID);
    ASSERT(!*ppColumnSetID);

    // Attempt to get data from the object
    sc = lpDataObject->GetData(&formatetc, &stgmedium);
    if (sc)
        goto Error;

    pb = (BYTE*)GlobalLock(stgmedium.hGlobal);
    pColumnSetID = (SColumnSetID *) pb;
    cb = sizeof(SColumnSetID) + pColumnSetID->cBytes;

    // Allocate a new buffer with PvAlloc
    *ppColumnSetID = (SColumnSetID *)GlobalAlloc(GMEM_FIXED, cb);
    if (*ppColumnSetID == NULL)
        goto MemoryError;

    CopyMemory(*ppColumnSetID, pColumnSetID, cb);

Cleanup:
    if (pColumnSetID)
        GlobalUnlock(stgmedium.hGlobal);
    if (stgmedium.hGlobal)
    {
        ASSERT(GlobalFree(stgmedium.hGlobal) == NULL);
    }
    stgmedium.hGlobal = NULL;

    return sc;
MemoryError:
    sc = E_OUTOFMEMORY;
Error:
    if(*ppColumnSetID)
        delete (*ppColumnSetID);
    (*ppColumnSetID) = NULL;
    TraceError(_T("CBaseDataObject::ScGetColumnSetID"), sc);
    goto Cleanup;
}


// -----------------------------------------------------------------------------
// Returns the name of the clipboard format (debug only)
//
#ifdef DBG
LPTSTR CBaseDataObject::SzDebugNameFromFormatEtc(UINT format)
{
    const int cchMaxLine = 256;
    static TCHAR s_szName[cchMaxLine];
    int ret = 0;

    ret = GetClipboardFormatName(format, s_szName, cchMaxLine);
    if (ret == 0)
        _tcscpy(s_szName, _T("Unknown Clipboard Format"));

    return s_szName;
}
#endif

// -----------------------------------------------------------------------------
// Moves to the next available clipboard format (debug only)
//
#ifdef DBG
STDMETHODIMP CEnumFormatEtc::Next(
                                 /* [in] */                                              ULONG           celt,
                                 /* [length_is][size_is][out] */ FORMATETC       *rgelt,
                                 /* [out] */                                     ULONG           *pceltFetched)
{
    ASSERT(rgelt);

    if (celt != 1)
        return E_FAIL;

    if (m_dwIndex > 0)
        return S_FALSE;

    if (pceltFetched)
        *pceltFetched = 1;

    if (rgelt)
    {
        rgelt->cfFormat = CF_UNICODETEXT;
        rgelt->dwAspect = DVASPECT_CONTENT;
        rgelt->tymed = TYMED_HGLOBAL;
    }
    else
        return E_INVALIDARG;

    m_dwIndex++;

    return S_OK;
}

#endif