//---------------------------------------------------------------
//
//  For conditions of distribution and use, see copyright notice
//  in Flashpix.h
//
//  Copyright (c) 1999 Digital Imaging Group, Inc.
//
//  Contents:   CExposedIterator implementation
//
//---------------------------------------------------------------

#include "exphead.cxx"

#include "expiter.hxx"
#include "h/sstream.hxx"

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::CExposedIterator, public
//
//  Synopsis:   Constructor
//
//  Arguments:  [ppdf] - Public Docfile
//              [pKey] - Initial cursor (doc file name)
//
//---------------------------------------------------------------

CExposedIterator::CExposedIterator(CExposedDocFile *ppdf, CDfName *pKey)
{
    olDebugOut((DEB_ITRACE, "In  CExposedIterator::CExposedIterator("
            "%p, %p)\n", ppdf, pKey));
    _dfnKey.Set(pKey);
    _ppdf = ppdf;
    // keep a ref, so that the pointer is valid throughout life time
    // of iterator
    _ppdf->AddRef();
    _cReferences = 1;
    _sig = CEXPOSEDITER_SIG;
    olDebugOut((DEB_ITRACE,
                "Out CExposedIterator::CExposedIterator\n"));
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::~CExposedIterator, public
//
//  Synopsis:   Destructor
//
//---------------------------------------------------------------

CExposedIterator::~CExposedIterator(void)
{
    olDebugOut((DEB_ITRACE, "In  CExposedIterator::~CExposedIterator\n"));
    _sig = CEXPOSEDITER_SIGDEL;
    olAssert(_cReferences == 0);
    if (_ppdf) _ppdf->Release();
    olDebugOut((DEB_ITRACE, "Out CExposedIterator::~CExposedIterator\n"));
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::Next, public
//
//  Synopsis:   Gets N entries from an iterator
//
//  Arguments:  [celt] - Count of elements
//              [rgelt] - Array for element return
//              [pceltFetched] - If non-NULL, contains the number of
//                      elements fetched
//
//  Returns:    Appropriate status code
//
//  Modifies:   [rgelt]
//              [pceltFetched]
//
//---------------------------------------------------------------

SCODE CExposedIterator::Next(ULONG celt,
                             STATSTGW FAR *rgelt,
                             ULONG *pceltFetched)
{
    SCODE sc;
    STATSTGW stat, *pelt = rgelt;
    ULONG celtDone;
    CDfName dfnInitial;

    olDebugOut((DEB_ITRACE, "In CExposedIterator::Next(%lu, %p, %p)\n",
                celt, rgelt, pceltFetched));

    if (pceltFetched)
    {
        olChk(ValidateBuffer(pceltFetched, sizeof(ULONG)));
        *pceltFetched = 0;
    }
    else if (celt > 1)
        olErr(EH_Err, STG_E_INVALIDPARAMETER);
    olAssert(0xffffUL/sizeof(STATSTGW) >= celt);
    olChkTo(EH_RetSc,
            ValidateOutBuffer(rgelt, sizeof(STATSTGW)*celt));
    memset(rgelt, 0, (size_t)(sizeof(STATSTGW)*celt));
    olChk(Validate());
    olChk(_ppdf->CheckReverted());

    dfnInitial.Set(&_dfnKey); // preserve initial key to reset on failure
    for (; pelt<rgelt+celt; pelt++)
    {
        sc = _ppdf->FindGreaterEntry(&_dfnKey, NULL, &stat);
        if (FAILED(sc))
        {
            if (sc == STG_E_NOMOREFILES)   sc = S_FALSE;
            break;
        }
        _dfnKey.Set(stat.pwcsName); // advance key

        stat.grfMode = 0;
        stat.grfLocksSupported = 0;
        stat.reserved = 0;
        *pelt = stat;
    }

    // Can't move this down because dfnInitial isn't set for all EH_Err cases
    if (FAILED(sc)) _dfnKey.Set(&dfnInitial);

    olDebugOut((DEB_ITRACE, "Out CExposedIterator::Next => %lX\n", sc));
EH_Err:
    celtDone = pelt-rgelt;
    if (FAILED(sc))
    {
        ULONG i;

        for (i = 0; i<celtDone; i++)
        delete[] rgelt[i].pwcsName;
        memset(rgelt, 0, (size_t)(sizeof(STATSTGW)*celt));
    }
    else if (pceltFetched)
        *pceltFetched = celtDone;
EH_RetSc:
    return sc;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::Skip, public
//
//  Synopsis:   Skips N entries from an iterator
//
//  Arguments:  [celt] - Count of elements
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP CExposedIterator::Skip(ULONG celt)
{
    SCODE sc;
    CDfName dfnNext;

    olDebugOut((DEB_ITRACE, "In  CExposedIterator::Skip(%lu)\n", celt));

    olChk(Validate());
    olChk(_ppdf->CheckReverted());
    for (; celt>0; celt--)
    {
        sc = _ppdf->FindGreaterEntry(&_dfnKey, &dfnNext, NULL);
        if (FAILED(sc))
        {
            if (sc == STG_E_NOMOREFILES)
                sc = S_FALSE;
            break;
        }
        _dfnKey.Set(&dfnNext); // advance the cursor
    }

    olDebugOut((DEB_ITRACE, "Out CExposedIterator::Skip\n"));
EH_Err:
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::Reset, public
//
//  Synopsis:   Rewinds the iterator
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP CExposedIterator::Reset(void)
{
    SCODE sc;

    olDebugOut((DEB_ITRACE, "In  CExposedIterator::Reset()\n"));

    olChk(Validate());
    _dfnKey.Set((WORD)0, (BYTE*)NULL);  // set to smallest key
    sc = _ppdf->CheckReverted();

    olDebugOut((DEB_ITRACE, "Out CExposedIterator::Reset\n"));
EH_Err:
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::Clone, public
//
//  Synopsis:   Clones this iterator
//
//  Arguments:  [ppenm] - Clone return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppenm]
//
//---------------------------------------------------------------

STDMETHODIMP CExposedIterator::Clone(IEnumSTATSTG **ppenm)
{
    SCODE sc;
    CExposedIterator *piExp;

    olDebugOut((DEB_ITRACE, "In  CExposedIterator::Clone(%p)\n", ppenm));

    olChk(ValidateOutPtrBuffer(ppenm));
    *ppenm = NULL;
    olChk(Validate());
    olChk(_ppdf->CheckReverted());
    olMem(piExp = new CExposedIterator(_ppdf, &_dfnKey));
    *ppenm = piExp;

    olDebugOut((DEB_ITRACE, "Out CExposedIterator::Clone => %p\n",
                SAFE_DREF(ppenm)));
    // Fall through
EH_Err:
    return ResultFromScode(sc);
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::Release, public
//
//  Synopsis:   Releases resources for the iterator
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP_(ULONG) CExposedIterator::Release(void)
{
    ULONG lRet;

    olDebugOut((DEB_ITRACE, "In  CExposedIterator::Release()\n"));
    if (FAILED(Validate()))
        return 0;
    olAssert(_cReferences > 0);
    lRet = --(_cReferences);
    if (_cReferences <= 0)
        delete this;

    olDebugOut((DEB_ITRACE, "Out CExposedIterator::Release\n"));
    return lRet;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::AddRef, public
//
//  Synopsis:   Increments the ref count
//
//  Returns:    Appropriate status code
//
//---------------------------------------------------------------

STDMETHODIMP_(ULONG) CExposedIterator::AddRef(void)
{
    ULONG ulRet;

    olDebugOut((DEB_ITRACE, "In  CExposedIterator::AddRef()\n"));

    if (FAILED(Validate())) return 0;
    ulRet = ++(_cReferences);

    olDebugOut((DEB_ITRACE, "Out CExposedIterator::AddRef\n"));
    return ulRet;
}

//+--------------------------------------------------------------
//
//  Member:     CExposedIterator::QueryInterface, public
//
//  Synopsis:   Returns an object for the requested interface
//
//  Arguments:  [iid] - Interface ID
//              [ppvObj] - Object return
//
//  Returns:    Appropriate status code
//
//  Modifies:   [ppvObj]
//
//---------------------------------------------------------------

STDMETHODIMP CExposedIterator::QueryInterface(REFIID iid, void **ppvObj)
{
    SCODE sc;

    olDebugOut((DEB_ITRACE, "In  CExposedIterator::QueryInterface(?, %p)\n",
                ppvObj));

    olChk(Validate());
    olChk(ValidateOutPtrBuffer(ppvObj));
    *ppvObj = NULL;
    olChk(_ppdf->CheckReverted());
    olChk(ValidateIid(iid));
    if (IsEqualIID(iid, IID_IEnumSTATSTG) || IsEqualIID(iid, IID_IUnknown))
    {
        *ppvObj = this;
        AddRef();
        sc = S_OK;
    }
    else
    {
        sc = E_NOINTERFACE;
    }

    olDebugOut((DEB_ITRACE, "Out CExposedIterator::QueryInterface => %p\n",
                SAFE_DREF(ppvObj)));
EH_Err:
    return ResultFromScode(sc);
}
