//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright (C) Microsoft Corporation, 1992 - 2000
//
//  File:      enumerator.h
//
//  Contents:  Supports enumeration for collections of com objects
//
//  History:   14-Oct-99 VivekJ    Created (as comerror.h)
//             08-Mar-2000 AudriusZ separated some code into enumerator.h file
//
//--------------------------------------------------------------------------

#ifndef ENUMERATOR_H_INCLUDED
#define ENUMERATOR_H_INCLUDED
#pragma once

/***************************************************************************\
 *
 * CLASS:  CMMCNewEnumImpl<BaseClass, _Position, EnumImplementor>
 *
 * PURPOSE: Implements enumeration for collection class
 *          EnumImplementor class is responsible for implementing these methods:
 *          SC  ScEnumNext(_Position &pos, PDISPATCH & pDispatch); 
 *          SC  ScEnumSkip(unsigned long celt, unsigned long& celtSkipped, _Position &pos);
 *          SC  ScEnumReset(_Position &pos);
 *
\***************************************************************************/
/****** usage tips *******************************************************
 *   Generally you will provide just 2 params to template - this means
 *   the tied object of your base class needs to implement following methods:
 *           ScEnumNext, ScEnumSkip, ScEnumReset;
 *
 *   If you pass same class as the first and the third template parameter, your base class is
 *   required to implement mentioned methods (not the tied object).
 *   It's usefull when you want to have collection and enueration in one class.
 *
 *   You also may specify any other class implementing the methods as the 3rd parameter, 
 *   but then the BaseClass needs to implement method 'ScGetEnumImplementor' returning 
 *   the instance of that class
 *
 *   NOTE1: since it's template class, the BaseClass is not required to define/implement
 *          ScGetTiedObject, when it implements enum methods itself.
 *   NOTE2: But it always IS REQUIRED to define CMyTiedObject type to compile.
 *          (this template class needs it to be anything different than the BaseClass)
 *          It is suggested to typedef it as void : "typedef void CMyTiedObject;"
 *   NOTE3: Make sure CMyTiedObject type is public or protected
 ************************************************************************/
template <class BaseClass, class _Position, class EnumImplementor = BaseClass::CMyTiedObject>
class CMMCNewEnumImpl : public BaseClass
{
    // Methods to get proper implementor for Enum methods
    // simple one when implemented by the base class
    SC ScGetEnumImplementor(BaseClass * &pObj)                { pObj = this; return SC(S_OK); }
    // when implemented by the tied object (default) - also simple
    SC ScGetEnumImplementor(BaseClass::CMyTiedObject * &pObj) { return ScGetTiedObject(pObj); }
public:
    STDMETHOD(get__NewEnum)(IUnknown** ppUnk);
};




/*+-------------------------------------------------------------------------*
 * class CMMCEnumerator
 *
 *
 * PURPOSE: General purpose enumerator class. Keyed to a position object,
 *          which is templated.
 *
 *          The following three methods need to be implemented by the tied object:
 *
 *          SC  ScEnumNext(_Position &pos, PDISPATCH & pDispatch); // should return the next element.
 *          SC  ScEnumSkip(unsigned long celt, unsigned long& celtSkipped,
                            * _Position &pos);
 *          SC  ScEnumReset(_Position &pos);
 *
 *          Cloning the enumerator is taken care of automatically.
 *
 * NOTE:    The Position object must have a copy constructor and assignment
 *          operator.
 *+-------------------------------------------------------------------------*/

typedef IEnumVARIANT ** PPENUMVARIANT;
typedef VARIANT *       PVARIANT;

template<class TiedObj, class _Position>
class CMMCEnumerator : 
    public IEnumVARIANT,
    public IMMCSupportErrorInfoImpl<&IID_IEnumVARIANT,     &GUID_NULL>,    // rich error handling
    public CComObjectRoot,
    public CTiedComObject<TiedObj>
{
    typedef CMMCEnumerator<TiedObj, _Position> ThisClass;

    typedef TiedObj CMyTiedObject;

    friend  TiedObj;

public:
    BEGIN_COM_MAP(ThisClass)
        COM_INTERFACE_ENTRY(IEnumVARIANT)
    END_COM_MAP()

    DECLARE_NOT_AGGREGATABLE(ThisClass)

    // Returns the next celt items starting from the current position
    // in the array rgvar
    STDMETHODIMP Next(unsigned long celt, PVARIANT rgvar, unsigned long * pceltFetched);

    // Skips over the next celt elements in the enumeration sequence.
    STDMETHODIMP Skip(unsigned long celt);

    // Resets the enumeration sequence to the beginning
    STDMETHODIMP Reset();

    // Creates a copy of the current state of enumeration
    STDMETHODIMP Clone(PPENUMVARIANT ppenum);

public:
    // the position object that keeps track of the present location.
    _Position m_position;
};


/*+-------------------------------------------------------------------------*
 * class CMMCArrayEnumBase
 *
 * PURPOSE: General purpose array enumeration base class.
 *          Particularly useful when array of items to be enumerated is initially
 *          available, or when com object creation is not a big penalty and there 
 *          is no need to postpone item creation to when they are requested.
 *
 * USAGE:   typedef your enumerator as CMMCNewEnumImpl parameterized by this class
 *          - or even better - create the instance of CMMCArrayEnum class.
 *          Use Init method passing an array of pointers to items [first, last)
 *+-------------------------------------------------------------------------*/
template <class _CollectionInterface, class _ItemInterface>
class CMMCArrayEnumBase :
    public CMMCIDispatchImpl<_CollectionInterface>,
    public CTiedObject                     // enumerators are tied to it
{
protected:
    typedef void CMyTiedObject; // not tied

public:
    BEGIN_MMC_COM_MAP(CMMCArrayEnumBase)
    END_MMC_COM_MAP()

public:

    // Returns count of items in the collection
    STDMETHODIMP get_Count( PLONG pCount );

    // Returns specified item from collection
    STDMETHODIMP Item( long Index, _ItemInterface ** ppItem );

    // Resets position to the first item in the collection
    ::SC ScEnumReset (unsigned &pos);

    // Returns item from the collection, advances position
    ::SC ScEnumNext  (unsigned &pos, PDISPATCH & pDispatch);

    // Skips the amount of items in enumeration
    ::SC ScEnumSkip  (unsigned long celt, unsigned long& celtSkipped, unsigned &pos);

    // Initializes the array with given iterators
    template<typename InIt> 
    void Init(InIt first, InIt last)
    { 
        m_array.clear();
        m_array.reserve(last - first);
        while(first != last)
            m_array.push_back(*first), ++first;
    }

private:

    // data members
    std::vector< CComPtr<_ItemInterface> > m_array;
};

/*+-------------------------------------------------------------------------*
 * class CMMCArrayEnumBase
 *
 * PURPOSE: General purpose array enumeration class.
 *          Particularly useful when array of items to be enumerated is initially
 *          available, or when com object creation is not a big penalty and there 
 *          is no need to postpone item creation to when they are requested.
 *
 * USAGE:   create the instance of CMMCArrayEnum class whenever you need an 
 *          enumerator for the array of objects you have.
 *          Parameterized by collection type and element type;
 *          For instance CMMCArrayEnum< Nodes, Node >
 *          Use Init method passing an array of pointers to items [first, last)
 *
 * EXAMPLE: << skipping error checking for clarity >>
 *          void GetNodes(std::vector<PNODE>& InNodes, PPNODES ppOutNodes)
 *          {
 *              typedef CComObject< CMMCArrayEnum<Nodes, Node> > EnumNodes;
 *              EnumNodes *pNodes = NULL;
 *              EnumNodes::CreateInstance(&pNodes);             // create
 *              pNodes->Init(InNodes.begin(), InNodes.end());   // initialize with array
 *              pNodes->AddRef();                               // addref for caller
 *              *ppOutNodes = pNodes;                           // return
 *          }
 *
 *+-------------------------------------------------------------------------*/
template <class _CollT, class _ItemT>
class CMMCArrayEnum : 
public CMMCNewEnumImpl<CMMCArrayEnumBase<_CollT, _ItemT>, unsigned, CMMCArrayEnumBase<_CollT, _ItemT> >
{
};


// include inline definitions
#include "enumerator.inl"

#endif  // ENUMERATOR_H_INCLUDED