//+---------------------------------------------------------------------
//
//  File:       stdenum.cxx
//
//  Contents:   Standard implementations of common enumerators
//
//----------------------------------------------------------------------

#include "headers.hxx"
#pragma hdrstop
#include <limits.h>         // for UINT_MAX below

//
//  forward declarations
//

class StdEnumOLEVERB;
typedef StdEnumOLEVERB FAR* LPSTDENUMOLEVERB;

class StdEnumFORMATETC;
typedef StdEnumFORMATETC FAR* LPSTDENUMFORMATETC;

#if 1
class StdStaticEnum;
typedef StdStaticEnum FAR* LPSTDSTATICENUM;
#endif  // 0


//+---------------------------------------------------------------
//
//  Class:      StdEnumOLEVERB
//
//  Purpose:    Standard enumerator of OLEVERB tables
//
//---------------------------------------------------------------

class StdEnumOLEVERB: public IEnumOLEVERB
{
    friend HRESULT CreateOLEVERBEnum(LPOLEVERB, ULONG, LPENUMOLEVERB FAR*);

public:
    DECLARE_STANDARD_IUNKNOWN(StdEnumOLEVERB);

    // *** IEnumOLEVERB methods ***
    STDMETHOD(Next) (ULONG celt, LPOLEVERB rgelt, ULONG FAR* pceltFetched);
    STDMETHOD(Skip) (ULONG celt);
    STDMETHOD(Reset) (void);
    STDMETHOD(Clone) (IEnumOLEVERB FAR* FAR* ppenm);

private:
    StdEnumOLEVERB(LPOLEVERB pStart, ULONG cCount);
    ~StdEnumOLEVERB(void);

    LPOLEVERB _pStart;
    ULONG _cCount;
    ULONG _cCurrent;
};

//+---------------------------------------------------------------
//
//  Member:     StdEnumOLEVERB::StdEnumOLEVERB, private
//
//  Synopsis:   Constructor for StdEnumOLEVERB objects
//
//  Arguments:  [pStart] -- pointer to the beginning of the OLEVERB array
//              [cCount] -- the number of elements in the array
//
//  Notes:      OLEVERB enumerators should be constructed using the
//              CreateOLEVERBEnum function.
//
//----------------------------------------------------------------

StdEnumOLEVERB::StdEnumOLEVERB(LPOLEVERB pStart, ULONG cCount)
{
    _ulRefs = 1;
    _pStart = pStart;
    _cCount = cCount;
    _cCurrent = 0;
    DOUT(TEXT("StdEnumOLEVERB constructed.\r\n"));
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumOLEVERB::~StdEnumOLEVERB, private
//
//  Synopsis:   Destructor for StdEnumOLEVERB objects
//
//  Notes:      Static enumerators should never be `deleted' but
//              instead IUnknown::Release'd.
//
//----------------------------------------------------------------

StdEnumOLEVERB::~StdEnumOLEVERB(void)
{
    DOUT(TEXT("StdEnumOLEVERB destructed.\r\n"));
}

IMPLEMENT_STANDARD_IUNKNOWN(StdEnumOLEVERB)

//+---------------------------------------------------------------
//
//  Member:     StdEnumOLEVERB::QueryInterface, public
//
//  Synopsis:   Method of IUnknown interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumOLEVERB::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
    if (IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,IID_IEnumOLEVERB))
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    DOUT(TEXT("StdEnumOLEVERB::QueryInterface E_NOINTERFACE\r\n"));
    *ppvObj = NULL;
    return E_NOINTERFACE;
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumOLEVERB::Next
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumOLEVERB::Next(ULONG celt,
        LPOLEVERB pArrayObjs,
        ULONG FAR* pceltFetched)
{
    /* skip ahead to primary verb */
    for (;_cCurrent <= _cCount; _cCurrent++)
    {
        LPOLEVERB pVerb = &_pStart[_cCurrent];
        if (pVerb->lVerb >= OLEIVERB_PRIMARY)
            break;
    }

    ULONG celtFetched = min(celt, _cCount-_cCurrent);
    for (ULONG i = 0; i < celtFetched; i++, _cCurrent++)
    {
        LPOLEVERB pVerb = &_pStart[_cCurrent];

        pArrayObjs[i] = *pVerb;
        if (pVerb->lpszVerbName!=NULL)
        {
            HRESULT r;
            r = TaskAllocString(pVerb->lpszVerbName,
                    &pArrayObjs[i].lpszVerbName);
            if (!OK(r))
                return r;
        }
    }

    if (pceltFetched != NULL)
    {
        *pceltFetched = celtFetched;
    }

    return ((celtFetched == celt) ? NOERROR : S_FALSE);
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumOLEVERB::Skip
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumOLEVERB::Skip(ULONG celt)
{
    _cCurrent += celt;
    if (_cCurrent >= _cCount)
    {
        _cCurrent = _cCount;
        return S_FALSE;
    }
    return NOERROR;
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumOLEVERB::Reset
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumOLEVERB::Reset(void)
{
    _cCurrent = 0;
    return NOERROR;
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumOLEVERB::Clone
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumOLEVERB::Clone(LPENUMOLEVERB FAR* ppenm)
{
    HRESULT r = E_OUTOFMEMORY;
    //LPSTDENUMOLEVERB penum = new (NullOnFail) StdEnumOLEVERB(_pStart, _cCount);
    LPSTDENUMOLEVERB penum = new StdEnumOLEVERB(_pStart, _cCount);
    if (penum != NULL)
    {
        r = NOERROR;
        penum->_cCurrent = _cCurrent;
        *ppenm = penum;
    }
    else
    {
        DOUT(TEXT("o2base/StdEnumOLEVERB::Clone failed\r\n"));
    }
    return r;
}

//+---------------------------------------------------------------
//
//  Function:   CreateOLEVERBEnum, public
//
//  Synopsis:   Creates a standard enumerator over OLEVERB arrays
//
//  Arguments:  [pVerbs] -- pointer to the beginning of the OLEVERB array
//              [cVerbs] -- the number of elements in the array
//              [ppenum] -- where the enumerator is returned
//
//  Returns:    Success if the enumerator could be successfully created
//
//  Notes:      This function is typically used in the IOleObject::EnumVerbs
//              method implementation.
//
//----------------------------------------------------------------

HRESULT
CreateOLEVERBEnum(LPOLEVERB pVerbs, ULONG cVerbs, LPENUMOLEVERB FAR* ppenum)
{
    HRESULT r = E_OUTOFMEMORY;
    //LPSTDENUMOLEVERB penum = new (NullOnFail) StdEnumOLEVERB(pVerbs, cVerbs);
    LPSTDENUMOLEVERB penum = new StdEnumOLEVERB(pVerbs, cVerbs);
    if (penum != NULL)
    {
        r = NOERROR;
        *ppenum = penum;
    }
    else
    {
        DOUT(TEXT("o2base/stdenum/CreateOLEVERBEnum failed\r\n"));
    }
    return r;
}

//+---------------------------------------------------------------
//
//  Class:      StdEnumFORMATETC
//
//  Purpose:    Standard enumerator of FORMATETC tables
//
//---------------------------------------------------------------

class StdEnumFORMATETC: public IEnumFORMATETC
{
    friend HRESULT CreateFORMATETCEnum(LPFORMATETC, ULONG, LPENUMFORMATETC FAR*);
public:
    DECLARE_STANDARD_IUNKNOWN(StdEnumFORMATETC);

    // *** IEnumFORMATETC methods ***
    STDMETHOD(Next) (ULONG celt, LPFORMATETC rgelt, ULONG FAR* pceltFetched);
    STDMETHOD(Skip) (ULONG celt);
    STDMETHOD(Reset) (void);
    STDMETHOD(Clone) (IEnumFORMATETC FAR* FAR* ppenm);

private:
    StdEnumFORMATETC(LPFORMATETC pStart, ULONG cCount);
    ~StdEnumFORMATETC(void);

    LPFORMATETC _pStart;
    ULONG _cCount;
    ULONG _cCurrent;
};

//+---------------------------------------------------------------
//
//  Member:     StdEnumFORMATETC::StdEnumFORMATETC, private
//
//  Synopsis:   Constructor for StdEnumFORMATETC objects
//
//  Arguments:  [pStart] -- pointer to the beginning of the FORMATETC array
//              [cCount] -- the number of elements in the array
//
//  Notes:      Static enumerators should be constructed using the
//              CreateFORMATETCEnum function.
//
//----------------------------------------------------------------

StdEnumFORMATETC::StdEnumFORMATETC(LPFORMATETC pStart, ULONG cCount)
{
    _ulRefs = 1;
    _pStart = pStart;
    _cCount = cCount;
    _cCurrent = 0;
    DOUT(TEXT("StdEnumFORMATETC constructed.\r\n"));
}

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::~StdStaticEnum, private
//
//  Synopsis:   Destructor for StdStaticEnum objects
//
//  Notes:      Static enumerators should never be `deleted' but
//              instead IUnknown::Release'd.
//
//----------------------------------------------------------------

StdEnumFORMATETC::~StdEnumFORMATETC(void)
{
    DOUT(TEXT("StdEnumFORMATETC destructed.\r\n"));
}

IMPLEMENT_STANDARD_IUNKNOWN(StdEnumFORMATETC)

//+---------------------------------------------------------------
//
//  Member:     StdEnumFORMATETC::QueryInterface, public
//
//  Synopsis:   Method of IUnknown interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumFORMATETC::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
#ifdef VERBOSE_DBG
    TCHAR achBuffer[256];
    wsprintf(achBuffer,
            TEXT("StdEnumFORMATETC::QueryInterface (%lx)\r\n"),
            riid.Data1);
    DOUT(achBuffer);
#endif //VERBOSE_DBG

    if (IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,IID_IEnumFORMATETC))
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }

#if VERBOSE_DBG
    wsprintf(achBuffer,
            TEXT("StdEnumFORMATETC::QueryInterface returning E_NOINTERFACE for %lx\r\n"),
            riid.Data1);
    DOUT(achBuffer);
#endif //VERBOSE_DBG
    *ppvObj = NULL;
    return E_NOINTERFACE;
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumFORMATETC::Next
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumFORMATETC::Next(ULONG celt,
                        LPFORMATETC pArrayObjs,
                        ULONG FAR* pceltFetched)
{
    ULONG celtFetched = 0;

    if (_pStart == NULL)
        return S_FALSE;

    if (pceltFetched != NULL)
        *pceltFetched = 0L;

    if (pArrayObjs == NULL || _cCurrent >= _cCount)
        return S_FALSE;
    
    while ( _cCurrent < _cCount && celt > 0 )
    {
        LPFORMATETC pFormat = &_pStart[_cCurrent];

        // deep copy the FORMATETC structure
        
        *pArrayObjs = *pFormat;
        if (pFormat->ptd == DVTARGETIGNORE )
            pArrayObjs->ptd = NULL;
        
        if (pArrayObjs->ptd != NULL)
        {
            HRESULT r = TaskAllocMem(sizeof(DVTARGETDEVICE),
                (LPVOID FAR*)&pArrayObjs->ptd);
            if (OK(r))
            {
                *(pArrayObjs->ptd) = *(pFormat->ptd);
            }
        }
        pArrayObjs++;
        _cCurrent++;
        celtFetched++;
        celt--;
    }

    if (pceltFetched != NULL)
    {
        *pceltFetched = celtFetched - celt;
    }
    return NOERROR;
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumFORMATETC::Skip
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumFORMATETC::Skip(ULONG celt)
{
    _cCurrent += celt;
    if (_cCurrent >= _cCount)
    {
        _cCurrent = _cCount;
        return S_FALSE;
    }
    return NOERROR;
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumFORMATETC::Reset
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumFORMATETC::Reset(void)
{
    _cCurrent = 0;
    return NOERROR;
}

//+---------------------------------------------------------------
//
//  Member:     StdEnumFORMATETC::Clone
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdEnumFORMATETC::Clone(LPENUMFORMATETC FAR* ppenm)
{
    HRESULT r = E_OUTOFMEMORY;;
    LPSTDENUMFORMATETC penum = new StdEnumFORMATETC(_pStart, _cCount);
    if (penum != NULL)
    {
        r = NOERROR;
        penum->_cCurrent = _cCurrent;
        *ppenm = penum;
    }
    else
    {
        DOUT(TEXT("o2base/StdEnumFORMATETC::Clone failed\r\n"));
    }
    return r;
}

//+---------------------------------------------------------------
//
//  Function:   CreateFORMATETCEnum, public
//
//  Synopsis:   Creates a standard enumerator over FORMATETC arrays
//
//  Arguments:  [pFormats] -- pointer to the beginning of the FORMATETC array
//              [cFormats] -- the number of elements in the array
//              [ppenum] -- where the enumerator is returned
//
//  Returns:    Success if the enumerator could be successfully created
//
//  Notes:      This function is typically used in the IDataObject::EnumFormatetc
//              method implementation.
//
//----------------------------------------------------------------

HRESULT
CreateFORMATETCEnum(LPFORMATETC pFormats,
                    ULONG cFormats,
                    LPENUMFORMATETC FAR* ppenum)
{
    HRESULT r;
    LPSTDENUMFORMATETC penum = new StdEnumFORMATETC(pFormats, cFormats);
    if (penum == NULL)
    {
        DOUT(TEXT("o2base/stdenum/CreateFORMATETCEnum E_OUTOFMEMORY\r\n"));
        r = E_OUTOFMEMORY;
    }
    else
    {
        *ppenum = penum;
        r = NOERROR;
    }
    return r;
}

#if 1   // this maybe useful later but is not currently used.

//+---------------------------------------------------------------
//
//  Class:      StdStaticEnum
//
//  Purpose:    Enumerates over a static array
//
//  Notes:      This may not be used to enumerate over structures
//              that are "deep".  For instance, it cannot be used
//              to enumerate over an array of FORMATETCs because such
//              an enumerator needs to deep copy the ptd field
//              and the enumerator client frees these allocated ptd.
//              Similarly for the OLEVERB structure where the verb
//              name string must be deep copied.
//
//---------------------------------------------------------------

class StdStaticEnum: public IUnknown
{
    friend HRESULT CreateStaticEnum(REFIID, LPVOID, ULONG, ULONG, LPVOID FAR*);

public:
    DECLARE_STANDARD_IUNKNOWN(StdStaticEnum);

    //*** IEnumerator methods ***
    STDMETHOD(Next) (ULONG celt, LPVOID pArrayObjs, ULONG FAR* pceltFetched);
    STDMETHOD(Skip) (ULONG celt);
    STDMETHOD(Reset) (void);
    STDMETHOD(Clone) (LPSTDSTATICENUM FAR* ppenm);

private:
    // constructor/destructor
    StdStaticEnum(REFIID riid, LPVOID pStart, ULONG cSize, ULONG cCount);
    ~StdStaticEnum(void);

    IID _iid;
    LPVOID _pStart;
    ULONG _cSize;
    ULONG _cCount;
    ULONG _cCurrent;
};

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::StdStaticEnum, private
//
//  Synopsis:   Constructor for StdStaticEnum objects
//
//  Arguments:  [riid] -- the enumerator interface that this class is
//                          "pretending" to be.
//              [pStart] -- pointer to the beginning of the static array
//              [cSize] -- the size of the elements of the array
//              [cCount] -- the number of elements in the array
//
//  Notes:      Static enumerators should be constructed using the
//              CreateStaticEnum function.
//
//----------------------------------------------------------------

StdStaticEnum::StdStaticEnum(REFIID riid,
        LPVOID pStart,
        ULONG cSize,
        ULONG cCount)
{
    _ulRefs = 1;
    _iid = riid;
    _pStart = pStart;
    _cSize = cSize;
    _cCount = cCount;
    _cCurrent = 0;
    DOUT(TEXT("StdStaticEnum constructed.\r\n"));
}

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::~StdStaticEnum, private
//
//  Synopsis:   Destructor for StdStaticEnum objects
//
//  Notes:      Static enumerators should never be `deleted' but
//              instead IUnknown::Release'd.
//
//----------------------------------------------------------------

StdStaticEnum::~StdStaticEnum(void)
{
    DOUT(TEXT("StdStaticEnum destructed.\r\n"));
}

IMPLEMENT_STANDARD_IUNKNOWN(StdStaticEnum);

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::QueryInterface, public
//
//  Synopsis:   Method of IUnknown interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdStaticEnum::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
    if (IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,_iid))
    {
        *ppvObj = this;
        AddRef();
        return NOERROR;
    }
    DOUT(TEXT("StdStaticEnum::QueryInterface E_NOINTERFACE\r\n"));
    *ppvObj = NULL;
    return E_NOINTERFACE;
}

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::Next
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdStaticEnum::Next(ULONG celt,
        LPVOID pArrayObjs,
        ULONG FAR* pceltFetched)
{
    ULONG celtFetched = min(celt, _cCount-_cCurrent);

    // calculate the number of bytes to copy
    if (celtFetched != 0 && _cSize > (UINT_MAX/celtFetched))
    {
        DOUT(TEXT("StdStaticEnum::Next E_FAIL\r\n"));
        return E_FAIL;         // overflow!
    }

    UINT count = (UINT) (celtFetched*_cSize);
    _fmemcpy(pArrayObjs, (LPBYTE)_pStart+_cCurrent*_cSize, count);
    _cCurrent += celtFetched;
    if (pceltFetched != NULL)
    {
        *pceltFetched = celtFetched;
    }
    return ((celtFetched == celt) ? NOERROR : S_FALSE);
}

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::Skip
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdStaticEnum::Skip(ULONG celt)
{
    _cCurrent += celt;
    if (_cCurrent >= _cCount)
    {
        _cCurrent = _cCount;
        return S_FALSE;
    }
    return NOERROR;
}

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::Reset
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdStaticEnum::Reset(void)
{
    _cCurrent = 0;
    return NOERROR;
}

//+---------------------------------------------------------------
//
//  Member:     StdStaticEnum::Clone
//
//  Synopsis:   Member of IEnumXXX interface
//
//----------------------------------------------------------------

STDMETHODIMP
StdStaticEnum::Clone(LPSTDSTATICENUM FAR* ppenm)
{
    HRESULT r;
    //LPSTDSTATICENUM penum = new (NullOnFail) StdStaticEnum(
    LPSTDSTATICENUM penum = new StdStaticEnum(
                                                    _iid,
                                                    _pStart,
                                                    _cSize,
                                                    _cCount);
    if (penum == NULL)
    {
        DOUT(TEXT("o2base/StdStaticEnum::Clone failed\r\n"));
        r = E_OUTOFMEMORY;
    }
    else
    {
        penum->_cCurrent = _cCurrent;
        *ppenm = penum;
        r = NOERROR;
    }
    return r;
}

//+---------------------------------------------------------------
//
//  Function:   CreateStaticEnum, public
//
//  Synopsis:   Creates a standard enumerator over static arrays
//
//  Arguments:  [riid] -- the enumerator interface that this class is
//                          "pretending" to be.
//              [pStart] -- pointer to the beginning of the static array
//              [cSize] -- the size of the elements of the array
//              [cCount] -- the number of elements in the array
//              [ppenum] -- where the enumerator is returned
//
//  Returns:    Success if the enumerator could be successfully created
//
//----------------------------------------------------------------

HRESULT
CreateStaticEnum(REFIID riid,
        LPVOID pStart,
        ULONG cSize,
        ULONG cCount,
        LPVOID FAR* ppenum)
{
    HRESULT r;
    //LPSTDSTATICENUM penum = new (NullOnFail) StdStaticEnum(
    LPSTDSTATICENUM penum = new StdStaticEnum(
                                                    riid,
                                                    pStart,
                                                    cSize,
                                                    cCount);
    if (penum == NULL)
    {
        DOUT(TEXT("o2base/stdenum/CreateStaticEnum failed\r\n"));
        r = E_OUTOFMEMORY;
    }
    else
    {
        *ppenum = penum;
        r = NOERROR;
    }
    return r;
}

#endif // 0
