//+----------------------------------------------------------------------------
//
//      File:
//              multi.cpp
//
//      Contents:
//              Cache node test which creates multiple nodes, then performs
//              various data tests on them.
//
//      History:
//
//              04-Sep-94       davepl  Created
//
//-----------------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop

//+----------------------------------------------------------------------------
//
//      Member:         TestInstance::MultiCache
//
//      Synopsis:	Caches N unique nodes, where N is large (>100).  Saves
//                      the cache, then reloads it to compare.
//
//      Arguments:	[dwCount]       Number of new nodes to cache
//
//      Returns:	HRESULT
//
//      Notes:
//
//      History:	24-Aug-94  Davepl	Created
//
//-----------------------------------------------------------------------------

HRESULT TestInstance::MultiCache(DWORD dwCount)
{
    HRESULT hr;

    DWORD iCFGEN = 0,
          iNODES = 0,
          iSDATA = 0;

    TraceLog Log(NULL, "TestInstance::MultiCache", GS_CACHE, VB_MINIMAL);
    Log.OnEntry (" ( %d )\n", dwCount);
    Log.OnExit  (" ( %X )\n", &hr);

    //
    // A temporary buffer for creating text clipformat names
    //

    char szFormatName[ MAX_BUF ];

    //
    // An array of UINTs to hold our private clipformats, and an
    // array of DWORDS to hold the connection IDs
    //

    CLIPFORMAT *acfArray = (CLIPFORMAT *) malloc(dwCount * sizeof(CLIPFORMAT));
    if (NULL == acfArray)
    {
        return E_OUTOFMEMORY;
    }

    DWORD *adwConnections = (DWORD *) malloc(dwCount * sizeof(DWORD));
    if (NULL == adwConnections)
    {
        free(acfArray);
        return E_OUTOFMEMORY;
    }

    //
    // Generate N private clipformats
    //

    for (iCFGEN=0; iCFGEN < dwCount; iCFGEN++)
    {
        sprintf(szFormatName, "LocalFormat%d", iCFGEN);

        acfArray[iCFGEN] = (WORD) RegisterClipboardFormat(szFormatName);
        if (0 == acfArray[iCFGEN])
        {
            free(acfArray);
            return HRESULT_FROM_WIN32(GetLastError());
        }
    }

    //
    // Cache N nodes based on those formats
    //

    FORMATETC fetc =
     		 {
  		     0,                 // Clipformat
		     NULL,		// DVTargetDevice
		     DVASPECT_CONTENT,	// Aspect
		     -1,		// Index
		     TYMED_HGLOBAL	// TYMED
		 };

    STGMEDIUM stgm;

    for (iNODES = 0; iNODES < dwCount; iNODES++)
    {
        fetc.cfFormat = acfArray[iNODES];
        hr = m_pOleCache->Cache(&fetc, ADVF_PRIMEFIRST, &adwConnections[iNODES]);

        // We are expecting the cache to return CACHE_S_FORMATETC_NOTSUPPORTED
        // for this data, since it cannot draw it.

        hr = MassageErrorCode(CACHE_S_FORMATETC_NOTSUPPORTED, hr);

        if (S_OK != hr)
        {
            break;
        }
    }

    //
    // If all went well adding the nodes, proceed to SetData into
    // each of the nodes with some unique data
    //

    if (S_OK == hr)
    {
        for (iSDATA = 0; iSDATA < dwCount; iSDATA++)
        {
            HGLOBAL hTmp = GlobalAlloc(GMEM_MOVEABLE, sizeof(DWORD));
            if (NULL == hTmp)
            {
                break;
            }
            DWORD * pdw = (DWORD *) GlobalLock(hTmp);
            if (NULL == pdw)
            {
                GlobalFree(hTmp);
                break;
            }

            //
            // Set the data in the HGLOBAL equal to the clipformat
            // for this node
            //

            *pdw = iSDATA;

            GlobalUnlock(hTmp);

            stgm.tymed = TYMED_HGLOBAL;
            stgm.hGlobal = hTmp;
            fetc.cfFormat = acfArray[iSDATA];

            hr = m_pOleCache->SetData(&fetc, &stgm, TRUE /* fRelease */);

            if (S_OK != hr)
            {
                break;
            }
        }
    }

    //
    // Save the cache and reload it
    //

    if (S_OK == hr)
    {
        hr = SaveAndReload();
    }

    //
    // Just to make things interesting, let's DiscardCache before we
    // start looking for data.  This will force the cache to demand-load
    // the data as we ask for it. Since we know the cache is not dirty,
    // there's no value (practical or from a test perspective) in asking
    // the DiscardCache to save along the way.
    //

    if (S_OK == hr)
    {
        hr = m_pOleCache2->DiscardCache(DISCARDCACHE_NOSAVE);
    }

    if (S_OK == hr)
    {
        for (iSDATA = 0; iSDATA < dwCount; iSDATA++)
        {
            //
            // For each of the cache nodes we added, try to
            // get the data that was saved in the cache under
            // that clipformat
            //

            fetc.cfFormat = acfArray[iSDATA];
            hr = m_pDataObject->GetData(&fetc, &stgm);
            if (S_OK != hr)
            {
                ReleaseStgMedium(&stgm);
                break;
            }

            //
            // Lock the HGLOBAL and compare what is in the cache
            // node to what we expect should be there (the index
            // into our clipboard format table
            //

            DWORD * pdw = (DWORD *) GlobalLock(stgm.hGlobal);
            if (NULL == pdw)
            {
                hr = E_OUTOFMEMORY;
                break;
            }

            if (*pdw != iSDATA)
            {
                hr = E_FAIL;
                GlobalUnlock(stgm.hGlobal);
                ReleaseStgMedium(&stgm);
                break;
            }

            GlobalUnlock(stgm.hGlobal);
            ReleaseStgMedium(&stgm);
        }
    }

    //
    // We want to remove all of the cache nodes we have added.
    // Unforunately, there is no easy way to do this; we have to
    // enumerate over the cache and toss nodes as we find them, even
    // though we _know_ everything about the nodes.  Sigh...
    //

    //
    // Get an enumerator on the cache
    //

    LPENUMSTATDATA pEsd;	
    if (S_OK == hr)
    {
    	hr = m_pOleCache->EnumCache(&pEsd);
    }

    //
    // Since we've got a large number of cache nodes in the cache,
    // now is a perfect time to run the generic enumerator tests on
    // the cache.
    //

    if (S_OK == hr)
    {
	hr = TestEnumerator((void *) pEsd, sizeof(STATDATA), iSDATA, NULL, NULL,NULL);
    }

    //
    // Reset the enumerator before beginning our UnCache loop.
    //

    if (S_OK == hr)
    {
    	hr = pEsd->Reset();
    }

    if (S_OK == hr)
    {
        //
        // Loop until a failure or until we have removed all of
        // the nodes that we thought should exist
        //

        STATDATA stat;

        while (S_OK == hr && iSDATA > 0)
        {
            hr = pEsd->Next(1, &stat, NULL);

            if (S_OK == hr)
            {
                hr = m_pOleCache->Uncache(stat.dwConnection);
                iSDATA--;
            }
        }
    }

    return hr;
}