/*** 
*cenumpt.cpp
*
*  Copyright (C) 1992-1994, 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(IsEqualIID(riid, IID_IEnumVARIANT) || IsEqualIID(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;
}
