/*++

Copyright (c) 1997-2000 Microsoft Corporation

Module Name:

    rndcoll.h

Abstract:

    Definitions for CRendezvous collection template class.
--*/

#ifndef __RENDCOLL_H
#define __RENDCOLL_H

#include "tapi3if.h"
#include "rndobjsf.h"

EXTERN_C const IID LIBID_TAPI3Lib;

template <class T>
class ATL_NO_VTABLE TInterfaceCollection :
    public CComObjectRootEx<CComMultiThreadModel>,
    public IDispatchImpl<ITCollection, &IID_ITCollection, &LIBID_TAPI3Lib>,
    public CObjectSafeImpl

{
public:
    TInterfaceCollection()
        : m_Var(NULL), m_nSize(0), m_pFTM(NULL) {}

    ~TInterfaceCollection() 
    {
        delete [] m_Var;

        if ( m_pFTM )
        {
            m_pFTM->Release();
        }
    }

typedef TInterfaceCollection<T> _TCollection;

    DECLARE_GET_CONTROLLING_UNKNOWN()

    BEGIN_COM_MAP(_TCollection)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(ITCollection)
        COM_INTERFACE_ENTRY(IObjectSafety)
        COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pFTM)
    END_COM_MAP()

private:
    // Array of Dispatch pointers.
    CComVariant*    m_Var ;
    long            m_nSize;
    IUnknown      * m_pFTM;          // pointer to the free threaded marshaler

public:

    HRESULT Initialize (long nSize, T* begin, T* end);

// ICollection methods
    STDMETHOD (get_Count) (OUT long* retval);
    STDMETHOD (get_Item) (IN long Index, OUT VARIANT* retval);
    STDMETHOD (get__NewEnum)(IUnknown** retval);
};

template <class T>
HRESULT 
TInterfaceCollection<T>::Initialize (long nSize, T* begin, T* end)
{
    if ( nSize < 0 )
    {
        return E_INVALIDARG;
    }


    HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
                                                & m_pFTM );

    if ( FAILED(hr) )
    {
        LOG((MSP_INFO, "CTInterfaceCollection::Initialize - "
            "create FTM returned 0x%08x; exit", hr));

        return hr;
    }

    if (NULL == (m_Var = new CComVariant[nSize]))
    {
        return E_OUTOFMEMORY;
    }

    m_nSize = nSize;

    DWORD i = 0;

    for (T *iter = begin; iter != end; iter ++, i ++)
    {
        // get IDispatch pointer
        IDispatch * pDisp;

        hr = (*iter)->QueryInterface(IID_IDispatch, (void**)&pDisp);
        if (S_OK != hr) return hr;

        V_VT(&m_Var[i])         = VT_DISPATCH;
        V_DISPATCH(&m_Var[i])   = pDisp;
    }

    return S_OK;    
}

template <class T>
STDMETHODIMP 
TInterfaceCollection<T>::get_Count(OUT long* retval)
{
    if ( IsBadWritePtr(retval, sizeof(long) ) )
    {
        return E_POINTER;
    }

    *retval = m_nSize;

    return S_OK;
}

template <class T>
STDMETHODIMP 
TInterfaceCollection<T>::get_Item(IN long Index, OUT VARIANT* retval)
{
    if ( IsBadWritePtr(retval, sizeof(VARIANT) ) )
    {
        return E_POINTER;
    }

    retval->vt = VT_UNKNOWN;
    retval->punkVal = NULL;

    // use 1-based index, VB like
    if ((Index < 1) || (Index > m_nSize))
    {
        return E_INVALIDARG;
    }

    VariantCopy(retval, &m_Var[Index-1]);

    return S_OK;    
}

template <class T>
STDMETHODIMP 
TInterfaceCollection<T>::get__NewEnum(IUnknown** retval)
{
    if ( IsBadWritePtr(retval, sizeof( IUnknown * ) ) )
    {
        return E_POINTER;
    }

    *retval = NULL;

    typedef CComObject<CSafeComEnum<IEnumVARIANT, 
        &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT> > > enumvar;

    enumvar* p; // = new enumvar;
    enumvar::CreateInstance( &p );

    if (p == NULL)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT hr = p->Init(&m_Var[0], &m_Var[m_nSize], NULL, AtlFlagCopy);

    if (SUCCEEDED(hr))
    {
        hr = p->QueryInterface(IID_IEnumVARIANT, (void**)retval);
    }
    else
    {
        delete p;
    }

    return hr;
}

class ATL_NO_VTABLE TBstrCollection :
    public CComObjectRootEx<CComMultiThreadModel>,
    public IDispatchImpl<ITCollection, &IID_ITCollection, &LIBID_TAPI3Lib>,
    public CObjectSafeImpl
{
public:
    TBstrCollection()
        : m_Var(NULL), m_nSize(0), m_pFTM(NULL) {}

    ~TBstrCollection()
    {
        delete [] m_Var;

        if ( m_pFTM )
        {
            m_pFTM->Release();
        }
    }

    DECLARE_GET_CONTROLLING_UNKNOWN()

    BEGIN_COM_MAP(TBstrCollection)
        COM_INTERFACE_ENTRY(IDispatch)
        COM_INTERFACE_ENTRY(ITCollection)
        COM_INTERFACE_ENTRY(IObjectSafety)
        COM_INTERFACE_ENTRY_AGGREGATE(IID_IMarshal, m_pFTM)
    END_COM_MAP()

private:
    // Array of Dispatch pointers.
    CComVariant*    m_Var ;
    long            m_nSize;
    IUnknown      * m_pFTM;          // pointer to the free threaded marshaler

public:

    inline HRESULT Initialize (long nSize, BSTR* begin, BSTR* end, CComEnumFlags flags);

// ITCollection methods
    inline STDMETHOD (get_Count) (OUT long* retval);
    inline STDMETHOD (get_Item) (IN long Index, OUT VARIANT* retval);
    inline STDMETHOD (get__NewEnum)(IUnknown** retval);
};

inline
HRESULT 
TBstrCollection::Initialize (long nSize, BSTR * begin, BSTR * end, CComEnumFlags flags)
{
    if ( nSize < 0 )
    {
        return E_INVALIDARG;
    }

    HRESULT hr = CoCreateFreeThreadedMarshaler( GetControllingUnknown(),
                                                & m_pFTM );

    if ( FAILED(hr) )
    {
        LOG((MSP_INFO, "TCollectionCollection::Initialize - "
            "create FTM returned 0x%08x; exit", hr));

        return hr;
    }

    if (NULL == (m_Var = new CComVariant[nSize]))
    {
        return E_OUTOFMEMORY;
    }

    m_nSize = nSize;

    DWORD i = 0;

    for (BSTR * iter = begin; iter != end; iter ++, i ++)
    {
        m_Var[i].vt         = VT_BSTR;
        switch (flags)
        {
        case AtlFlagNoCopy: 
        case AtlFlagTakeOwnership :
            m_Var[i].bstrVal    = (*iter);
            break;

        case AtlFlagCopy: 
            m_Var[i].bstrVal    = SysAllocString(*iter);
            if (m_Var[i].bstrVal == NULL)
            {
                return E_OUTOFMEMORY;
            }
            break;
        }
    }

    if (AtlFlagTakeOwnership) delete begin;

    return S_OK;    
}

inline 
STDMETHODIMP 
TBstrCollection::get_Count(OUT long* retval)
{
    if ( IsBadWritePtr(retval, sizeof(long) ) )
    {
        return E_POINTER;
    }
    
    *retval = m_nSize;

    return S_OK;
}

inline
STDMETHODIMP 
TBstrCollection::get_Item(IN long Index, OUT VARIANT* retval)
{
    if ( IsBadWritePtr(retval, sizeof(VARIANT) ) )
    {
        return E_POINTER;
    }

    retval->vt = VT_UNKNOWN;
    retval->punkVal = NULL;

    // use 1-based index, VB like
    if ((Index < 1) || (Index > m_nSize))
    {
        return E_INVALIDARG;
    }

    VariantCopy(retval, &m_Var[Index-1]);

    return S_OK;    
}

inline 
STDMETHODIMP 
TBstrCollection::get__NewEnum(IUnknown** retval)
{
    if ( IsBadWritePtr(retval, sizeof( IUnknown * ) ) )
    {
        return E_POINTER;
    }

    *retval = NULL;

    typedef CComObject<CSafeComEnum<IEnumVARIANT, 
        &IID_IEnumVARIANT, VARIANT, _Copy<VARIANT> > > enumvar;

    enumvar* p; // = new enumvar;
    enumvar::CreateInstance( &p );

    if (p == NULL)
    {
        return E_OUTOFMEMORY;
    }

    HRESULT hr = p->Init(&m_Var[0], &m_Var[m_nSize], NULL, AtlFlagCopy);

    if (SUCCEEDED(hr))
    {
        hr = p->QueryInterface(IID_IEnumVARIANT, (void**)retval);
    }
    else
    {
        delete p;
    }

    return hr;
}

template <class T>
HRESULT CreateInterfaceCollection(
    IN  long        nSize,
    IN  T *         begin,
    IN  T *         end,
    OUT VARIANT *   pVariant              
    )
{
    // create the collection object
    typedef TInterfaceCollection<T> CCollection;

    CComObject<CCollection> * p;
    HRESULT hr = CComObject<CCollection>::CreateInstance( &p );

    if (NULL == p)
    {
        LOG((MSP_ERROR, "Could not create Collection object, %x",hr));
        return hr;
    }

    hr = p->Initialize(nSize, begin, end);

    if (S_OK != hr)
    {
        LOG((MSP_ERROR, "Could not initialize Collection object, %x", hr));
        delete p;
        return hr;
    }

    IDispatch *pDisp;

    // get the IDispatch interface
    hr = p->_InternalQueryInterface(IID_IDispatch, (void **)&pDisp);

    if (S_OK != hr)
    {
        LOG((MSP_ERROR, "QI for IDispatch in CreateCollection, %x", hr));
        delete p;
        return hr;
    }

    // put it in the variant
    VariantInit(pVariant);
    V_VT(pVariant)       = VT_DISPATCH;
    V_DISPATCH(pVariant) = pDisp;

    return S_OK;
}


#endif