/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 *
 * The contents of this file are subject to the Netscape Public License
 * Version 1.0 (the "NPL"); you may not use this file except in
 * compliance with the NPL.  You may obtain a copy of the NPL at
 * http://www.mozilla.org/NPL/
 *
 * Software distributed under the NPL is distributed on an "AS IS" basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the NPL
 * for the specific language governing rights and limitations under the
 * NPL.
 *
 * The Initial Developer of this code under the NPL is Netscape
 * Communications Corporation.  Portions created by Netscape are
 * Copyright (C) 1998 Netscape Communications Corporation.  All Rights
 * Reserved.
 */

#include "ienumfe.h"
#include <stdio.h>


/*
 * CEnumFormatEtc::CEnumFormatEtc
 * CEnumFormatEtc::~CEnumFormatEtc
 *
 * Parameters (Constructor):
 *  cFE             ULONG number of FORMATETCs in pFE
 *  prgFE           LPFORMATETC to the array to enumerate.
 */

CEnumFormatEtc::CEnumFormatEtc(ULONG aNumFEs, LPFORMATETC aFEList)
{
  UINT        i;

  mRefCnt     = 0;

  mCurrentInx = 0;
  mNumFEs     = aNumFEs;
  mMaxNumFEs  = aNumFEs;
  mFEList     = new FORMATETC[(UINT)aNumFEs];

  if (NULL!=mFEList) {
    for (i=0; i < aNumFEs; i++) {
      mFEList[i] = aFEList[i];
    }
  }

}

/*
 * CEnumFormatEtc::CEnumFormatEtc
 * CEnumFormatEtc::~CEnumFormatEtc
 *
 * Parameters (Constructor):
 *  pUnkRef         LPUNKNOWN to use for reference counting.
 *  cFE             ULONG number of FORMATETCs in pFE
 */

CEnumFormatEtc::CEnumFormatEtc(ULONG aMaxFE)
{
  mRefCnt     = 0;

  mCurrentInx = 0;
  mNumFEs     = 0;
  mMaxNumFEs  = aMaxFE;
  mFEList     = new FORMATETC[(UINT)aMaxFE];
}


//----------------------------------------------------
CEnumFormatEtc::~CEnumFormatEtc(void)
{
  if (NULL != mFEList) {
    delete [] mFEList;
  }

}

//----------------------------------------------------
void CEnumFormatEtc::AddFE(LPFORMATETC aFE)
{
  mFEList[mNumFEs++] = *aFE;
}

/*
 * CEnumFormatEtc::QueryInterface
 * CEnumFormatEtc::AddRef
 * CEnumFormatEtc::Release
 *
 * Purpose:
 *  IUnknown members for CEnumFormatEtc object.  For QueryInterface
 *  we only return out own interfaces and not those of the data
 *  object.  However, since enumerating formats only makes sense
 *  when the data object is around, we insure that it stays as
 *  long as we stay by calling an outer IUnknown for AddRef
 *  and Release.  But since we are not controlled by the lifetime
 *  of the outer object, we still keep our own reference count in
 *  order to free ourselves.
 */

STDMETHODIMP CEnumFormatEtc::QueryInterface(REFIID riid, LPVOID *ppv)
{
  *ppv=NULL;

  /*
   * Enumerators are separate objects, not the data object, so
   * we only need to support out IUnknown and IEnumFORMATETC
   * interfaces here with no concern for aggregation.
   */
  if (IsEqualIID(riid, IID_IUnknown)
      || IsEqualIID(riid, IID_IEnumFORMATETC))
      *ppv=(LPVOID)this;

  //AddRef any interface we'll return.
  if (NULL!=*ppv)
      {
      ((LPUNKNOWN)*ppv)->AddRef();
      return NOERROR;
      }

  return ResultFromScode(E_NOINTERFACE);
}

STDMETHODIMP_(ULONG) CEnumFormatEtc::AddRef(void)
{
  ++mRefCnt;
  //printf("CEnumFormatEtc::AddRef >>>>>>>>>>>>>>>>>> %d on %p\n", mRefCnt, this);
  return mRefCnt;
}

STDMETHODIMP_(ULONG) CEnumFormatEtc::Release(void)
{
  ULONG cRefT;

  cRefT = --mRefCnt;

  if (0L == mRefCnt)
      delete this;

  //printf("CEnumFormatEtc::Release >>>>>>>>>>>>>>>>>> %d on %p\n", mRefCnt, this);
  return cRefT;
}


/*
 * CEnumFormatEtc::Next
 *
 * Purpose:
 *  Returns the next element in the enumeration.
 *
 * Parameters:
 *  cFE             ULONG number of FORMATETCs to return.
 *  pFE             LPFORMATETC in which to store the returned
 *                  structures.
 *  pulFE           ULONG * in which to return how many we
 *                  enumerated.
 *
 * Return Value:
 *  HRESULT         NOERROR if successful, S_FALSE otherwise,
 */

STDMETHODIMP CEnumFormatEtc::Next(ULONG cFE, LPFORMATETC pFE, ULONG * pulFE)
{
  ULONG               cReturn=0L;

  if (NULL==mFEList)
      return ResultFromScode(S_FALSE);

  if (NULL==pulFE)
      {
      if (1L!=cFE)
          return ResultFromScode(E_POINTER);
      }
  else
      *pulFE=0L;

  if (NULL==pFE || mCurrentInx >= mNumFEs)
      return ResultFromScode(S_FALSE);

  while (mCurrentInx < mNumFEs && cFE > 0)
      {
      *pFE++=mFEList[mCurrentInx++];
      cReturn++;
      cFE--;
      }

  if (NULL!=pulFE)
      *pulFE=cReturn;

  return NOERROR;
}

/*
 * CEnumFormatEtc::Skip
 *
 * Purpose:
 *  Skips the next n elements in the enumeration.
 *
 * Parameters:
 *  cSkip           ULONG number of elements to skip.
 *
 * Return Value:
 *  HRESULT         NOERROR if successful, S_FALSE if we could not
 *                  skip the requested number.
 */

STDMETHODIMP CEnumFormatEtc::Skip(ULONG cSkip)
{
  if (((mCurrentInx+cSkip) >= mNumFEs) || NULL==mFEList)
      return ResultFromScode(S_FALSE);

  mCurrentInx+=cSkip;
  return NOERROR;
}

/*
 * CEnumFormatEtc::Reset
 *
 * Purpose:
 *  Resets the current element index in the enumeration to zero.
 *
 * Parameters:
 *  None
 *
 * Return Value:
 *  HRESULT         NOERROR
 */

STDMETHODIMP CEnumFormatEtc::Reset(void)
{
  mCurrentInx=0;
  return NOERROR;
}

/*
 * CEnumFormatEtc::Clone
 *
 * Purpose:
 *  Returns another IEnumFORMATETC with the same state as ourselves.
 *
 * Parameters:
 *  ppEnum          LPENUMFORMATETC * in which to return the
 *                  new object.
 *
 * Return Value:
 *  HRESULT         NOERROR or a general error value.
 */

STDMETHODIMP CEnumFormatEtc::Clone(LPENUMFORMATETC *ppEnum)
{
  LPCEnumFormatEtc    pNew;

  *ppEnum=NULL;

  //Create the clone
  pNew = new CEnumFormatEtc(mNumFEs, mFEList);

  if (NULL==pNew)
      return ResultFromScode(E_OUTOFMEMORY);

  pNew->AddRef();
  pNew->mCurrentInx=mCurrentInx;

  *ppEnum=pNew;
  return NOERROR;
}
