//==========================================================================
//
//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
//  PURPOSE.
//
//  Copyright 1998 - 1999 Microsoft Corporation.  All Rights Reserved.
//
//--------------------------------------------------------------------------


#include "precomp.h"

extern HINSTANCE g_hmodThisDll; // Handle to this DLL itself.

//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::CGenericEnum, public
//
//  Synopsis:   Constructor
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

CGenericEnum::CGenericEnum(LPGENERICITEMLIST pGenericItemList,DWORD cOffset)
{
DWORD dwItemIndex;
m_cRef                   = 1;   // give the intial reference
m_pGenericItemList   = pGenericItemList;
m_cOffset                = cOffset;

    AddRef_ItemList(m_pGenericItemList); // increment our hold on shared memory.

    // set the current item to point to next record.
    m_pNextItem = m_pGenericItemList->pFirstGenericItem;
    dwItemIndex = cOffset;

    // this is a bug if this happens so assert in final.
    if (dwItemIndex > m_pGenericItemList->dwNumItems)
        dwItemIndex = 0;

    // move the nextItem pointer to the proper position
    while(dwItemIndex--)
    {
    m_pNextItem = m_pNextItem->pNextGenericItem;
    ++m_cOffset;

    if (NULL == m_pNextItem)
        break; // Again, another error.
    }
}


//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::~CGenericEnum, public
//
//  Synopsis:   Destructor.
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

CGenericEnum::~CGenericEnum()
{
    Release_ItemList(m_pGenericItemList); // decrement our hold on shared memory.
}


//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::QueryInterface, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

STDMETHODIMP CGenericEnum::QueryInterface(REFIID riid, LPVOID FAR *ppv)
{
    return E_NOINTERFACE;
}

//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::Addref, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CGenericEnum::AddRef()
{
    return ++m_cRef;
}


//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::Release, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

STDMETHODIMP_(ULONG) CGenericEnum::Release()
{
    if (--m_cRef)
        return m_cRef;

    DeleteThisObject();

    return 0L;
}

//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::Next, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

STDMETHODIMP CGenericEnum::Next(ULONG celt, LPGENERICITEM rgelt,ULONG *pceltFetched)
{
HRESULT hr = NOERROR;
ULONG ulFetchCount = celt;
ULONG ulNumFetched = 0;
LPGENERICITEM pGenericItem;

    if ( (m_cOffset + celt) > m_pGenericItemList->dwNumItems)
    {
    ulFetchCount = m_pGenericItemList->dwNumItems - m_cOffset;
    hr = S_FALSE;
    }

    pGenericItem = rgelt;

    while (ulFetchCount--)
    {
    *pGenericItem = *(m_pNextItem->pNextGenericItem);
    m_pNextItem = m_pNextItem->pNextGenericItem; // add error checking
    ++m_cOffset;
        ++ulNumFetched;
    ++pGenericItem;
    }

    if (pceltFetched)
    {
        *pceltFetched = ulNumFetched;
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::Skip, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

STDMETHODIMP CGenericEnum::Skip(ULONG celt)
{
HRESULT hr;

    if ( (m_cOffset + celt) > m_pGenericItemList->dwNumItems)
    {
    m_cOffset = m_pGenericItemList->dwNumItems;
    m_pNextItem = NULL;
    hr = S_FALSE;
    }
    else
    {
    while (celt--)
    {
        ++m_cOffset;
        m_pNextItem = m_pNextItem->pNextGenericItem;
    }

    hr = NOERROR;
    }

    return hr;
}


//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::Reset, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

STDMETHODIMP CGenericEnum::Reset()
{
    m_pNextItem = m_pGenericItemList->pFirstGenericItem;
    return NOERROR;
}


//+---------------------------------------------------------------------------
//
//  Member:     CGenericEnum::Clone, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

STDMETHODIMP CGenericEnum::Clone(CGenericEnum **ppenum)
{
    return E_NOTIMPL;
}


// help functions for managing enum list
// helper functions for managing the Generics list.
// properly increments and destroys shared Genericlist in memory.


//+---------------------------------------------------------------------------
//
//  Function:     AddRef_ItemList, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

DWORD AddRef_ItemList(LPGENERICITEMLIST pGenericItemList)
{
    return ++pGenericItemList->_cRefs;
}

//+---------------------------------------------------------------------------
//
//  Function:     Release_ItemList, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

DWORD Release_ItemList(LPGENERICITEMLIST pGenericItemList)
{
DWORD cRefs;

    // bug, not threadsafe
    cRefs = --pGenericItemList->_cRefs;

    if (0 == pGenericItemList->_cRefs)
    {
    LPGENERICITEM pCurItem = pGenericItemList->pFirstGenericItem;

        while(pCurItem)
        {
        LPGENERICITEM pFreeItem = pCurItem;

            pCurItem = pCurItem->pNextGenericItem;
            FREE(pFreeItem);
        }

    FREE(pGenericItemList);
    }

    return cRefs;
}

//+---------------------------------------------------------------------------
//
//  Function:     CreateItemList, public
//
//  Synopsis: create an new list and initialize it to nothing
//            and set the refcount to 1.
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

LPGENERICITEMLIST CreateItemList()
{
LPGENERICITEMLIST lpGenericList = (LPGENERICITEMLIST) ALLOC(sizeof(GENERICITEMLIST));

    if (lpGenericList)
    {
    memset(lpGenericList,0,sizeof(GENERICITEMLIST));
    AddRef_ItemList(lpGenericList);
    }

    return lpGenericList;
}

//+---------------------------------------------------------------------------
//
//  Function:     DuplicateList, public
//
//  Synopsis:   Duplicates the list
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

LPGENERICITEMLIST DuplicateItemList(LPGENERICITEMLIST pItemList)
{
LPGENERICITEMLIST lpNewItemList;
LPGENERICITEM pCurItem;

    lpNewItemList = CreateItemList();
    if (!lpNewItemList)
    {
        return NULL;
    }

    pCurItem = pItemList->pFirstGenericItem;

    while (pCurItem)
    {
    LPGENERICITEM pNewItemInList;

        Assert(pCurItem->cbSize >= sizeof(GENERICITEM));

        pNewItemInList = CreateNewListItem(pCurItem->cbSize);

        if (pNewItemInList)
        {
            memcpy(pNewItemInList,pCurItem,pCurItem->cbSize);
            pNewItemInList->pNextGenericItem = NULL;
            AddItemToList(lpNewItemList,pNewItemInList);
        }

        pCurItem = pCurItem->pNextGenericItem;
    }

    return lpNewItemList;
}


//+---------------------------------------------------------------------------
//
//  Function:     AddNewItemToList, public
//
//  Synopsis: allocates space for a new item and adds it to the list,
//      if successfull returns pointer to new item so caller can initialize it.
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

LPGENERICITEM AddNewItemToList(LPGENERICITEMLIST lpGenericList,ULONG cbSize)
{
LPGENERICITEM pNewGenericItem;

    pNewGenericItem = CreateNewListItem(cbSize);

    if (pNewGenericItem)
    {
        if (!AddItemToList(lpGenericList,pNewGenericItem))
        {
            FREE(pNewGenericItem);
            pNewGenericItem = NULL;
        }
    }

    return pNewGenericItem;
}


//+---------------------------------------------------------------------------
//
//  Function:     AddItemToList, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

BOOL AddItemToList(LPGENERICITEMLIST lpGenericList,LPGENERICITEM pNewGenericItem)
{
LPGENERICITEM pGenericItem;

    if (!pNewGenericItem || !lpGenericList)
    {
        Assert(lpGenericList);
        Assert(pNewGenericItem);
        return FALSE;
    }

    if (pNewGenericItem)
    {
        Assert(pNewGenericItem->cbSize >= sizeof(GENERICITEM));

        pGenericItem = lpGenericList->pFirstGenericItem;

        if (NULL == pGenericItem)
        {
            lpGenericList->pFirstGenericItem = pNewGenericItem;
        }
        else
        {
            while (pGenericItem->pNextGenericItem)
                pGenericItem = pGenericItem->pNextGenericItem;

            pGenericItem->pNextGenericItem = pNewGenericItem;

        }

    ++lpGenericList->dwNumItems;
    }

    return TRUE;
}

//+---------------------------------------------------------------------------
//
//  Function:     DeleteItemFromList, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

BOOL DeleteItemFromList(LPGENERICITEMLIST lpGenericList,LPGENERICITEM pGenericItem)
{

    if (!pGenericItem || !lpGenericList)
    {
        Assert(lpGenericList);
        Assert(pGenericItem);
        return FALSE;
    }

    if (pGenericItem)
    {
    LPGENERICITEM pCurItem;
    GENERICITEM  GenericItemTempHead;

        Assert(pGenericItem->cbSize >= sizeof(GENERICITEM));

        GenericItemTempHead.pNextGenericItem = lpGenericList->pFirstGenericItem;
        pCurItem = &GenericItemTempHead;


        while(pCurItem->pNextGenericItem)
        {

            if (pCurItem->pNextGenericItem == pGenericItem)
            {

                pCurItem->pNextGenericItem = pGenericItem->pNextGenericItem;
                FREE(pGenericItem);


                // update the head
                lpGenericList->pFirstGenericItem = GenericItemTempHead.pNextGenericItem;

                // update the number of items in the list
                --lpGenericList->dwNumItems;

                return TRUE;
            }

            pCurItem = pCurItem->pNextGenericItem;
        }

    }

    AssertSz(0,"Didn't find item in List");
    return FALSE; // didn't find anything.
}

//+---------------------------------------------------------------------------
//
//  Function:     CreateNewListItem, public
//
//  Synopsis:
//
//  Arguments:
//
//  Returns:
//
//  Modifies:
//
//----------------------------------------------------------------------------

LPGENERICITEM CreateNewListItem(ULONG cbSize)
{
LPGENERICITEM pNewGenericItem;

    // size must be at least size of the base generic item.
    if (cbSize < sizeof(GENERICITEM))
    return NULL;

     pNewGenericItem = (LPGENERICITEM) ALLOC(cbSize);

    // add item to the end of the list so if commit we do
    // it in the same order items were added.

    if (pNewGenericItem)
    {
    // initialize to zero, and then add it to the list.
    memset(pNewGenericItem,0,cbSize);
        pNewGenericItem->cbSize = cbSize;
    }

    return pNewGenericItem;
}