/*
*********************************************************************
*
*	The Essence of OLE
*
*	Source code sample
*
*	Copyright (c) 1996, Rolling Thunder Computing, Ipswich MA
*
*	e-mail: essence@rollthunder.com
*	CompuServe: 73540,3430
*	Web: http://www.rollthunder.com
*
*	File Name: CENUM.CPP
*
*	Purpose: Generic enumerator object
*
*********************************************************************
*/

#include <windows.h>
#include "cenum.h"

CEnumerator::CEnumerator (
	LPUNKNOWN pUnkOuter,		//	outer IUnknown
	LPVOID pData,				// pointer to array of structures to enumerate
	DWORD Count,				// number of structures in array
	DWORD SizeEach,					// size of single structure in array
	IID iid) 					// IID od interface to support 
{

/*
Initialize member variables
*/

    m_pUnkOuter = pUnkOuter ;
	AddRef( ) ;

    m_nCurrentElement = 0;
    m_Count = Count ;
	m_iid = iid ;
	m_SizeEach = SizeEach ;

/*
Allocate and fill new array of whatever structures this object is enumerating
*/
    UINT        i ;

    m_pData = (char *) malloc (m_Count * SizeEach) ;

    if (m_pData != NULL)
    {
        for (i = 0; i < m_Count ; i++)
		{
            memmove (m_pData + (i * m_SizeEach), 
				(char *)pData + (i * m_SizeEach), 
				m_SizeEach);
		}
    }

    return;
}


CEnumerator::~CEnumerator(void)
{
    if (m_pData != NULL)
	{
		free (m_pData) ;
	}

    return;
}



STDMETHODIMP CEnumerator::QueryInterface(REFIID riid, LPVOID *ppv)
{

/*
This enumerator supports IUnknown and the IID we were passed in our constructor
*/
    if (riid == IID_IUnknown || riid == m_iid)
	{
        *ppv = (LPVOID)this;
		AddRef( ) ;
		return S_OK ;
	}
	else
	{
		*ppv = NULL ;
		return E_NOINTERFACE ;
	}
}

/*
AddRef( ) and release for the enumerator.  When these are calledCEnumerator also AddRef
the outer object to which the enumerator refersCEnumerator because the enumerator makes
no sense if the outer object is gone.
*/

STDMETHODIMP_(ULONG) CEnumerator::AddRef(void)
{
    m_RefCount ++ ;
    m_pUnkOuter->AddRef();
    return m_RefCount ;
}

STDMETHODIMP_(ULONG) CEnumerator::Release(void)
{
	m_RefCount -- ;
    m_pUnkOuter->Release();

    if (m_RefCount == 0)
	{
        delete this;
		return 0 ;
	}

    return m_RefCount ;
}



STDMETHODIMP CEnumerator::Next(ULONG DesiredCount, LPVOID pOutData, ULONG * pOutCount)
{

/*
Various error conditions
*/
    if (m_pData == NULL || pOutData == NULL || m_nCurrentElement >= m_Count)
	{
        return S_FALSE ;
	}


/*
Copy each requested structure
*/

    ULONG ReturnCount = 0 ;

    while (m_nCurrentElement < m_Count && DesiredCount > 0)
    {
        memmove ((char *)pOutData, m_pData + m_nCurrentElement * m_SizeEach, m_SizeEach) ;
		
		pOutData = (char *)pOutData + m_SizeEach ;        
		m_nCurrentElement ++ ;
		ReturnCount ++;
        DesiredCount -- ;
    }
 /*
 Return number of structures copied.
 */

    if (pOutCount != NULL) 
	{
		*pOutCount = ReturnCount ;
	}

    return NOERROR;
}


STDMETHODIMP CEnumerator::Skip(ULONG cSkip)
{
    if (((m_nCurrentElement+cSkip) >= m_Count) || m_pData == NULL)
	{
        return S_FALSE ;
	}

    m_nCurrentElement += cSkip;
    return NOERROR;
}



STDMETHODIMP CEnumerator::Reset(void)
{
    m_nCurrentElement=0;
    return NOERROR;
}




STDMETHODIMP CEnumerator::Clone(IEnum **ppEnum)
{
    CEnumerator    *pNew;

/*
Create new clone of this enumerator
*/
    pNew = new CEnumerator (m_pUnkOuter, m_pData, m_Count, 
		m_SizeEach, m_iid) ;	

/*
If unsuccessful, NULL output pointer and return error code.
*/
	
	if (pNew == NULL)
	{
	    *ppEnum = NULL;
        return E_OUTOFMEMORY ;
	}

/*
Set count in new enumrator to that in the old one
*/
	pNew->m_nCurrentElement = m_nCurrentElement;

/*
Fill output variable with pointer to new enumerator and return success code.
*/
    *ppEnum = pNew;
    return S_OK ;
}
