//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft shared
// source or premium shared source license agreement under which you licensed
// this source code. If you did not accept the terms of the license agreement,
// you are not authorized to use this source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the SOURCE.RTF on your install media or the root of your tools installation.
// THE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
#ifndef __COLLECTION_HXX__
#define __COLLECTION_HXX__

#include "DispatchImpl.hxx"
#include "list.hxx"
#include "string.hxx"
#include "com_macros.h"
#include "assert.h"
#include <combook.h>

#pragma warning(push)
#pragma warning(disable : 4786)

namespace ce
{

// EnumVARIANT_Unknown
template <typename iterator>
class EnumVARIANT_Unknown : public IEnumUnknown,
							public IEnumVARIANT
{
	typedef EnumVARIANT_Unknown<iterator> _Myt;
public:
	EnumVARIANT_Unknown(IUnknown* punkColl, iterator itBegin, iterator itEnd)
		: m_punkColl(punkColl),
		  m_it(itBegin),
		  m_itBegin(itBegin),
		  m_itEnd(itEnd)
	{
		assert(m_punkColl);
		
		// hold reference to underlying collection object
		m_punkColl->AddRef();
	}


	~EnumVARIANT_Unknown()
	{
		// release underlying collection
		m_punkColl->Release();
	}

	// implement GetInterfaceTable
    BEGIN_INTERFACE_TABLE(EnumVARIANT_Unknown)
        IMPLEMENTS_INTERFACE(IEnumVARIANT)
		IMPLEMENTS_INTERFACE(IEnumUnknown)
    END_INTERFACE_TABLE()

    // implement QueryInterface/AddRef/Release
    IMPLEMENT_UNKNOWN(EnumVARIANT_Unknown)

// IEnumUnknown methods
public:
	// Next
	STDMETHOD(Next)(unsigned long celt, IUnknown** rgelt, unsigned long FAR* pceltFetched)
	{
		CHECK_POINTER(rgelt);

		if(pceltFetched)
			*pceltFetched = 0;

		for(; celt > 0 && m_it != m_itEnd; ++m_it, --celt)
		{
			m_it->get_item(rgelt++);

			if(pceltFetched)
				++(*pceltFetched);
		}

		return celt ? S_FALSE : S_OK;
	}


	// Skip
	STDMETHOD(Skip)(unsigned long celt)
	{
		for(; celt > 0 && m_it != m_itEnd; ++m_it, --celt);

		return celt ? S_FALSE : S_OK;
	}


	// Reset
	STDMETHOD(Reset)()
	{
		m_it = m_itBegin;

		return S_OK;
	}


	// Clone
	STDMETHOD(Clone)(IEnumUnknown **ppenum)
	{
		_Myt* pEnum = new _Myt(m_punkColl, m_itBegin, m_itEnd);

		if(!pEnum)
			return E_OUTOFMEMORY;

		return pEnum->QueryInterface(IID_IEnumUnknown, (void**)ppenum);
	}

// IEnumVARIANT methods
public:
	// Next
	STDMETHOD(Next)(unsigned long celt, VARIANT* rgelt, unsigned long FAR* pceltFetched)
	{
		CHECK_POINTER(rgelt);

		if(pceltFetched)
			*pceltFetched = 0;

		for(; celt > 0 && m_it != m_itEnd; ++m_it, --celt)
		{
			m_it->get_item(rgelt++);

			if(pceltFetched)
				++(*pceltFetched);
		}

		return celt ? S_FALSE : S_OK;
	}

	// Clone
	STDMETHOD(Clone)(IEnumVARIANT **ppenum)
	{
		_Myt* pEnum = new _Myt(m_punkColl, m_itBegin, m_itEnd);

		if(!pEnum)
			return E_OUTOFMEMORY;

		return pEnum->QueryInterface(IID_IEnumVARIANT, (void**)ppenum);
	}

protected:
	IUnknown*	m_punkColl;
	iterator	m_itBegin;
	iterator	m_itEnd;
	iterator	m_it;
};


// com_ptr
template <typename T>
class com_ptr
{
public:
	com_ptr(T* p)
		: m_p(p)
	{
		m_p->AddRef();
	}

	com_ptr(const com_ptr& x)
		: m_p(x.m_p)
	{
		m_p->AddRef();
	}

	~com_ptr()
	{
		m_p->Release();
	}

	T* operator->()
	{
		return m_p;
	}

private:
	T* m_p;
};


// owned_ptr
template <typename T>
class owned_ptr
{
public:
	owned_ptr(T* p)
		: m_p(p),
		  m_bOwn(true)
	{
	}

	owned_ptr(const owned_ptr& x)
		: m_p(x.m_p),
		  m_bOwn(true)
	{
		// take over ownership
		x.m_bOwn = false;
	}

	~owned_ptr()
	{
		if(m_bOwn)
			delete m_p;
	}

	T* operator->()
	{
		return m_p;
	}

private:
	T*				m_p;
	mutable bool	m_bOwn;
};


// com_element
template<typename T, const IID* piid>
class com_element
{
public:
	typedef LPCWSTR key_type;
	typedef T		item_type;

	com_element(key_type key, item_type item)
		: m_key(key),
		  m_item(item)
	{}

	// compare keys
	bool compare_key(key_type key)
	{
		return m_key == key;
	}

	// get item into a VARIANT
	HRESULT get_item(VARIANT* pItem)
	{
		IDispatch* pdisp;

        if(SUCCEEDED(m_item->QueryInterface(IID_IDispatch, (void**)&pdisp)))
        {
            pItem->vt = VT_DISPATCH;
            pdisp->Release();
        }
        else
            pItem->vt = VT_UNKNOWN;

		return m_item->QueryInterface(*piid, (void**)&pItem->punkVal);
	}

	// get item into an interface pointer
	HRESULT get_item(void* pItem)
	{
		return m_item->QueryInterface(*piid, (void**)pItem);
	}

private:
	wstring			m_key;
	T				m_item;
};


// Collection template
template <typename item_type,				// type of items in the collection
		  typename collection_interface,	// interface of collection object
		  const IID* pcollection_iid,		// IID of collection interface
		  typename element_trait>			// class specifying behavior of elements
class Collection : public DispatchImpl<collection_interface, pcollection_iid>
{
public:
	typedef list<element_trait>				collection;
	typedef typename collection::iterator			iterator;

	// Note: it would be nice to be able to specify enumerator as template argument for the
	// collection but it can't be elegantly done w\o support for template template arguments
	// For now hard code to EnumVARIANT_Unknown
	typedef EnumVARIANT_Unknown<iterator>	enumerator;

	HRESULT AddItem(const typename element_trait::key_type key, typename element_trait::item_type item)
	{
		m_Elements.push_back(element_trait(key, item));

		return S_OK;
	}

// collection_interface methods
public:	
	// get_Count
	STDMETHOD(get_Count)(/*[retval][out]*/ LONG* plCount)
	{
		CHECK_POINTER(plCount);

		*plCount = m_Elements.size();

		return S_OK;
	}

	// get_Item
    STDMETHOD(get_Item)(/*[in]*/ BSTR index, /*[retval][out]*/ item_type* ppItem)
	{
		for(iterator it = m_Elements.begin(), itEnd = m_Elements.end(); it != itEnd; ++it)
			if(it->compare_key(index))
				return it->get_item(ppItem);

		return E_FAIL;
	}

    // get__NewEnum
	STDMETHOD(get__NewEnum)(/*[retval][out] */ LPUNKNOWN* ppunk)
	{
		enumerator *pEnum = new enumerator(this, m_Elements.begin(), m_Elements.end());

		if(!pEnum)
			return E_OUTOFMEMORY;
		
		return pEnum->QueryInterface(IID_IUnknown, (void**)ppunk);
	}

protected:
	collection m_Elements;
};

}; // namespace ce

#pragma warning(pop)

#endif // __COLLECTION_HXX__
