/*************************************************************************
**
**  This is a part of the Microsoft Source Code Samples.
**
**  Copyright (C) 1992-1996 Microsoft Corporation. All rights reserved.
**
**  This source code is only intended as a supplement to Microsoft Development
**  Tools and/or WinHelp documentation.  See these sources for detailed
**  information regarding the Microsoft samples programs.
**
**  OLE Automation TypeLibrary Browse Helper Sample
**
**  collect.cpp
**
**  CCollection implementation
**
**  Written by Microsoft Product Support Services, Windows Developer Support
**
*************************************************************************/

#include <windows.h>
#include <windowsx.h>
#ifdef WIN16   
  #include <ole2.h>
  #include <compobj.h>    
  #include <dispatch.h> 
  #include <variant.h>
  #include <olenls.h>  
#endif   
#include "browseh.h"  

/*
 * CCollection::Create
 *
 * Purpose:
 *  Creates an instance of a collection object and initializes it.
 *
 * Parameters:
 *  lMaxSize   Maximum number of items that can added to collection.
 *  lLBound    Lower bound of index of collection.  
 *  ppCollection    Returns collection object.
 *
 * Return Value:
 *  HRESULT
 *
 */
HRESULT 
CCollection::Create(ULONG lMaxSize, long lLBound, CCollection FAR* FAR* ppCollection) 
{   
    HRESULT hr;
    CCollection FAR* pCollection = NULL;
    SAFEARRAYBOUND sabound[1];
     
    *ppCollection = NULL;
    
    // Create new collection
    pCollection = new CCollection();
    if (pCollection == NULL)
        goto error; 
    // Load type information for the collection from type library. 
    hr = pCollection->LoadTypeInfo(IID_ICollection);
    if (FAILED(hr))
        goto error;
    
    // If lMaxSize is 0 increment it to 1 or SafeArrayCreate will fail
    if (lMaxSize == 0)   
       lMaxSize = 1;       
    pCollection->m_cMax = lMaxSize;
    pCollection->m_cElements = 0; 
    pCollection->m_lLBound = lLBound;

    // Create a safe array which is used to implement the collection   
    sabound[0].cElements = lMaxSize;
    sabound[0].lLbound = lLBound;        
    pCollection->m_psa = SafeArrayCreate(VT_VARIANT, 1, sabound);
    if (pCollection->m_psa == NULL)
    {
        hr = ResultFromScode(E_OUTOFMEMORY);
        goto error;
    }
    
#ifdef _DEBUG  
    lstrcpyn(pCollection->m_szClassName, TEXT("Collection"), 100);
#endif          

    *ppCollection = pCollection;
    return NOERROR;
    
error: 
    if (pCollection == NULL)
        return ResultFromScode(E_OUTOFMEMORY);                       
    if (pCollection->m_psa) 
        SafeArrayDestroy(pCollection->m_psa); 
  
    pCollection->m_psa = NULL;
    
    delete pCollection;
    return hr;
}

/*
 * CCollection::CCollection
 *
 * Purpose:
 *  Constructor for CCollection object. Initializes members to NULL.
 *
 */
CCollection::CCollection()
{
    m_psa = NULL;
}

/*
 * CCollection::~CCollection
 *
 * Purpose:
 *  Destructor for CCollection object. 
 *
 */
CCollection::~CCollection()
{                  
     if (m_psa) SafeArrayDestroy(m_psa);
}

STDMETHODIMP_(REFCLSID)
CCollection::GetInterfaceID()
{
    return IID_ICollection;
}

/*
 * CCollection::get_Count
 *
 * Purpose:
 *  Returns number of items in collection.
 *
 */
STDMETHODIMP_(long)     
CCollection::get_Count(void)  
{
    return m_cElements;             
}

/*
 * CCollection::get_Item
 *
 * Purpose:
 *  Retrieves item from collection, given an index.
 *
 * Parameters:   
 *   lIndex   Index of item to be retrieved. 
 *
 * Returns
 *  IDispatch of item retrieved from collection.
 *
 */
STDMETHODIMP_(LPDISPATCH)
CCollection::get_Item(long lIndex)
{ 
    HRESULT hr;
    VARIANT v;
    LPDISPATCH pdisp = NULL;
   
    // Check if integer index is within range         
    if (lIndex < m_lLBound || lIndex >= (long)(m_lLBound+m_cElements)) 
        {RaiseException(IDS_InvalidIndex); return NULL;}
    
    // Retrieve and return item. Note that SafeArrayGetElement AddRefs so an additional AddRef
    // is not required.
    VariantInit(&v);
    hr = SafeArrayGetElement(m_psa, &lIndex, &v);
    if (FAILED(hr))
        {RaiseException(IDS_Unexpected); return NULL;}
    return V_DISPATCH(&v); 
}

/*
 * CCollection::get_NewEnum
 *
 * Purpose:
 *  Returns an enumerator (IEnumVARIANT) for the items curently in the collection. 
 *  The NewEnum property is restricted and so is invisible to users of an 
 *  automation controller's scripting language. Automation controllers that support
 *  a 'For Each' statement to iterate through the elements of a collection will use
 *  the enumerator returned by NewEnum. The enumerator creates a snapshot of the
 *  the current state of the collection.
 *
 */
STDMETHODIMP_(LPUNKNOWN)
CCollection::get__NewEnum(void)
{
    CEnumVariant FAR* penum = NULL;;
    LPUNKNOWN punkEnumVariant = NULL;
    HRESULT hr;

    // Create new enumerator for items currently in collection and QI for IUnknown
    hr = CEnumVariant::Create(m_psa, m_cElements, &penum);
    if (FAILED(hr))
        {RaiseException(IDS_OutOfMemory); goto error;}       
    hr = penum->QueryInterface(IID_IUnknown, (VOID FAR* FAR*)&punkEnumVariant);    
    if (FAILED(hr)) 
        {RaiseException(IDS_Unexpected); goto error;}         
    return punkEnumVariant; 
    
error:
    if (penum)
        delete penum;    
    return NULL;              
}

/*
 * CCollection::Add
 *
 * Purpose:
 *  Adds an item to the collection. 
 *
 * Parameters:
 *  pdispItemAdd    Item to be added to collection.
 *
 */
STDMETHODIMP_(void)   
CCollection::Add(LPDISPATCH pdispItem)
{ 
    HRESULT hr;   
    LONG l;
    VARIANT v;  
    
    // Is the collection full?
    if (m_cElements == m_cMax) 
        {RaiseException(IDS_CollectionFull); return;}             
       
    l = m_lLBound+m_cElements;              
    VariantInit(&v);
    V_VT(&v) = VT_DISPATCH;
    V_DISPATCH(&v) = pdispItem; 
       
    hr = SafeArrayPutElement(m_psa, &l, &v);
    if (FAILED(hr))
        {RaiseException(IDS_Unexpected); return;}               
          
    m_cElements++;                      
}
