/***
*cenumpt.cpp
*
*  Copyright (C) 1992-93, Microsoft Corporation.  All Rights Reserved.
*
*Purpose:
*  This module implements the CEnumPoint class.
*
*
*Implementation Notes:
*
*****************************************************************************/

#include "hostenv.h"
#include "cenumpt.h"


CEnumPoint::CEnumPoint()
{
	m_refs = 0;

	m_psa = NULL;
	m_celts = 0;
	m_iCurrent = 0;
}


/***
*HRESULT CEnumPoint::Create(SAFEARRAY*, CEnumPoint**)
*Purpose:
*  This routine creates a CPoint enumerator from the given
*  (1 X N) SafeArray of CPoint IDispatch pointers.
*
*Entry:
*  psa = pointer to a SafeArray of VARIANTs
*
*Exit:
*  return value = HRESULT
*
*  *ppenum = pointer to a CPoint enumerator
*
***********************************************************************/
HRESULT
CEnumPoint::Create(SAFEARRAY FAR* psa, CEnumPoint FAR* FAR* ppenum)
{
	long lBound;
	HRESULT hresult;
	CEnumPoint FAR* penum;


	// Verify that the SafeArray is the proper shape.
	//
	if(SafeArrayGetDim(psa) != 1)
	  return ResultFromScode(E_INVALIDARG);

	hresult = SafeArrayGetLBound(psa, 1, &lBound);
	if(FAILED(hresult))
	  return hresult;

	if(lBound != 0)
	  return ResultFromScode(E_INVALIDARG);

	penum = new FAR CEnumPoint();
	if(penum == NULL)
	  return ResultFromScode(E_OUTOFMEMORY);
	penum->AddRef();

	hresult = SafeArrayGetUBound(psa, 1, &lBound);
	if(FAILED(hresult))
	  goto LError0;

	penum->m_psa = psa;
	penum->m_celts = lBound + 1;

	*ppenum = penum;

	return NOERROR;

LError0:;
	penum->Release();

	return hresult;
}


//---------------------------------------------------------------------
//                        IUnknown methods
//---------------------------------------------------------------------


STDMETHODIMP
CEnumPoint::QueryInterface(REFIID riid, void FAR* FAR* ppv)
{
	if(riid == IID_IEnumVARIANT || riid == IID_IUnknown){
	  *ppv = this;
	  AddRef();
	  return NOERROR;
	}
	*ppv = NULL;
	return ResultFromScode(E_NOINTERFACE);
}


STDMETHODIMP_(unsigned long)
CEnumPoint::AddRef()
{
	return ++m_refs;
}


STDMETHODIMP_(unsigned long)
CEnumPoint::Release()
{
	if(--m_refs == 0){
	  if(m_psa != NULL)
		SafeArrayDestroy(m_psa);
	  delete this;
	  return 0;
	}
	return m_refs;
}


//---------------------------------------------------------------------
//                        IEnumVARIANT methods
//---------------------------------------------------------------------


/***
*HRESULT CEnumPoint::Next(unsigned long, VARIANT*, unsigned long*)
*Purpose:
*  Attempt to get the next 'celt' items in the enumeration sequence.
*
*Entry:
*  celt = the number of elements to get
*
*Exit:
*  return value = HRESULT
*    S_OK
*    S_FALSE - the end of the sequence was reached
*
*  rgvar = array of the next 'celt' items
*  *pceltFetched = count of the elements actually fetched.
*
***********************************************************************/
STDMETHODIMP
CEnumPoint::Next(
	unsigned long celt,
	VARIANT FAR* rgvar,
	unsigned long FAR* pceltFetched)
{
	long ix;
	unsigned int i;
	HRESULT hresult;


	for(i = 0; i < celt; ++i)
	  VariantInit(&rgvar[i]);

	for(i = 0; i < celt; ++i){
	  if(m_iCurrent == m_celts){
		hresult = ResultFromScode(S_FALSE);
		goto LDone;
	  }

	  ix = m_iCurrent++;
	  hresult = SafeArrayGetElement(m_psa, &ix, &rgvar[i]);
	  if(FAILED(hresult))
		goto LError0;
	}

	hresult = NOERROR;

LDone:;
	if (pceltFetched != NULL)
	  *pceltFetched = i;

	return hresult;

LError0:;

	for(i = 0; i < celt; ++i)
	  VariantClear(&rgvar[i]);

	return hresult;
}


/***
*HRESULT CEnumPoint::Skip(unsigned long)
*Purpose:
*  Attempt to skip over the next 'celt' elements in the enumeration
*  sequence.
*
*Entry:
*  celt = the count of elements to skip
*
*Exit:
*  return value = HRESULT
*    S_OK
*    S_FALSE -  the end of the sequence was reached
*
***********************************************************************/
STDMETHODIMP
CEnumPoint::Skip(unsigned long celt)
{
	m_iCurrent += celt;

	if(m_iCurrent > m_celts)
	 m_iCurrent = m_celts;

	return (m_iCurrent == m_celts)
	  ? ResultFromScode(S_FALSE) : NOERROR;
}


/***
*HRESULT CEnumPoint::Reset(void)
*Purpose:
*  Reset the enumeration sequence back to the beginning.
*
*Entry:
*  None
*
*Exit:
*  return value = SHRESULT CODE
*    S_OK
*
***********************************************************************/
STDMETHODIMP
CEnumPoint::Reset()
{
	m_iCurrent = 0;

	return NOERROR;
}


/***
*HRESULT CEnumPoint::Clone(IEnumVARIANT**)
*Purpose:
*  Retrun a CPoint enumerator with exactly the same state as the
*  current one.
*
*Entry:
*  None
*
*Exit:
*  return value = HRESULT
*    S_OK
*    E_OUTOFMEMORY
*
***********************************************************************/
STDMETHODIMP
CEnumPoint::Clone(IEnumVARIANT FAR* FAR* ppenum)
{
	HRESULT hresult;
	SAFEARRAY FAR* psa;
	CEnumPoint FAR* penum;

	hresult = SafeArrayCopy(m_psa, &psa);
	if(FAILED(hresult))
	  return hresult;

	hresult = CEnumPoint::Create(psa, &penum);
	if(FAILED(hresult))
	  goto LError0;

	// Assert(penum->m_celts == m_celts);

	penum->m_iCurrent = m_iCurrent;

	*ppenum = penum;

	return NOERROR;

LError0:
	SafeArrayDestroy(psa);

	return hresult;
}
