/*
 * Implementation of IEnumUnknown (for internal use).
 *
 * hidenori@a2.ctktv.ne.jp
 */

#include "config.h"

#include "windef.h"
#include "winbase.h"
#include "winerror.h"
#include "objbase.h"

#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(quartz);

#include "quartz_private.h"
#include "enumunk.h"
#include "iunk.h"



typedef struct IEnumUnknownImpl
{
	ICOM_VFIELD(IEnumUnknown);
} IEnumUnknownImpl;

typedef struct
{
	QUARTZ_IUnkImpl	unk;
	IEnumUnknownImpl	enumunk;
	struct QUARTZ_IFEntry	IFEntries[1];
	QUARTZ_CompList*	pCompList;
	QUARTZ_CompListItem*	pItemCur;
} CEnumUnknown;

#define	CEnumUnknown_THIS(iface,member)		CEnumUnknown*	This = ((CEnumUnknown*)(((char*)iface)-offsetof(CEnumUnknown,member)))




static HRESULT WINAPI
IEnumUnknown_fnQueryInterface(IEnumUnknown* iface,REFIID riid,void** ppobj)
{
	CEnumUnknown_THIS(iface,enumunk);

	TRACE("(%p)->()\n",This);

	return IUnknown_QueryInterface(This->unk.punkControl,riid,ppobj);
}

static ULONG WINAPI
IEnumUnknown_fnAddRef(IEnumUnknown* iface)
{
	CEnumUnknown_THIS(iface,enumunk);

	TRACE("(%p)->()\n",This);

	return IUnknown_AddRef(This->unk.punkControl);
}

static ULONG WINAPI
IEnumUnknown_fnRelease(IEnumUnknown* iface)
{
	CEnumUnknown_THIS(iface,enumunk);

	TRACE("(%p)->()\n",This);

	return IUnknown_Release(This->unk.punkControl);
}

static HRESULT WINAPI
IEnumUnknown_fnNext(IEnumUnknown* iface,ULONG cReq,IUnknown** ppunk,ULONG* pcFetched)
{
	CEnumUnknown_THIS(iface,enumunk);
	HRESULT	hr;
	ULONG	cFetched;

	TRACE("(%p)->(%lu,%p,%p)\n",This,cReq,ppunk,pcFetched);

	if ( pcFetched == NULL && cReq > 1 )
		return E_INVALIDARG;
	if ( ppunk == NULL )
		return E_POINTER;

	QUARTZ_CompList_Lock( This->pCompList );

	hr = NOERROR;
	cFetched = 0;
	while ( cReq > 0 )
	{
		if ( This->pItemCur == NULL )
		{
			hr = S_FALSE;
			break;
		}
		ppunk[ cFetched ] = QUARTZ_CompList_GetItemPtr( This->pItemCur );
		IUnknown_AddRef( ppunk[ cFetched ] );

		This->pItemCur =
			QUARTZ_CompList_GetNext( This->pCompList, This->pItemCur );
		cFetched ++;
		cReq --;
	}

	QUARTZ_CompList_Unlock( This->pCompList );

	if ( pcFetched != NULL )
		*pcFetched = cFetched;

	return hr;
}

static HRESULT WINAPI
IEnumUnknown_fnSkip(IEnumUnknown* iface,ULONG cSkip)
{
	CEnumUnknown_THIS(iface,enumunk);
	HRESULT	hr;

	TRACE("(%p)->()\n",This);

	QUARTZ_CompList_Lock( This->pCompList );

	hr = NOERROR;
	while ( cSkip > 0 )
	{
		if ( This->pItemCur == NULL )
		{
			hr = S_FALSE;
			break;
		}
		This->pItemCur =
			QUARTZ_CompList_GetNext( This->pCompList, This->pItemCur );
		cSkip --;
	}

	QUARTZ_CompList_Unlock( This->pCompList );

	return hr;
}

static HRESULT WINAPI
IEnumUnknown_fnReset(IEnumUnknown* iface)
{
	CEnumUnknown_THIS(iface,enumunk);

	TRACE("(%p)->()\n",This);

	QUARTZ_CompList_Lock( This->pCompList );

	This->pItemCur = QUARTZ_CompList_GetFirst( This->pCompList );

	QUARTZ_CompList_Unlock( This->pCompList );

	return NOERROR;
}

static HRESULT WINAPI
IEnumUnknown_fnClone(IEnumUnknown* iface,IEnumUnknown** ppunk)
{
	CEnumUnknown_THIS(iface,enumunk);
	HRESULT	hr;

	TRACE("(%p)->()\n",This);

	if ( ppunk == NULL )
		return E_POINTER;

	QUARTZ_CompList_Lock( This->pCompList );

	hr = QUARTZ_CreateEnumUnknown(
		This->IFEntries[0].piid,
		(void**)ppunk,
		This->pCompList );
	FIXME( "current pointer must be seeked correctly\n" );

	QUARTZ_CompList_Unlock( This->pCompList );

	return hr;
}


static ICOM_VTABLE(IEnumUnknown) ienumunk =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	/* IUnknown fields */
	IEnumUnknown_fnQueryInterface,
	IEnumUnknown_fnAddRef,
	IEnumUnknown_fnRelease,
	/* IEnumUnknown fields */
	IEnumUnknown_fnNext,
	IEnumUnknown_fnSkip,
	IEnumUnknown_fnReset,
	IEnumUnknown_fnClone,
};

void QUARTZ_DestroyEnumUnknown(IUnknown* punk)
{
	CEnumUnknown_THIS(punk,unk);

	if ( This->pCompList != NULL )
		QUARTZ_CompList_Free( This->pCompList );
}

HRESULT QUARTZ_CreateEnumUnknown(
	REFIID riidEnum, void** ppobj, const QUARTZ_CompList* pCompList )
{
	CEnumUnknown*	penum;
	QUARTZ_CompList*	pCompListDup;

	TRACE("(%s,%p,%p)\n",debugstr_guid(riidEnum),ppobj,pCompList);

	pCompListDup = QUARTZ_CompList_Dup( pCompList, FALSE );
	if ( pCompListDup == NULL )
		return E_OUTOFMEMORY;

	penum = (CEnumUnknown*)QUARTZ_AllocObj( sizeof(CEnumUnknown) );
	if ( penum == NULL )
	{
		QUARTZ_CompList_Free( pCompListDup );
		return E_OUTOFMEMORY;
	}

	QUARTZ_IUnkInit( &penum->unk, NULL );
	ICOM_VTBL(&penum->enumunk) = &ienumunk;

	penum->IFEntries[0].piid = riidEnum;
	penum->IFEntries[0].ofsVTPtr =
		offsetof(CEnumUnknown,enumunk)-offsetof(CEnumUnknown,unk);
	penum->pCompList = pCompListDup;
	penum->pItemCur = QUARTZ_CompList_GetFirst( pCompListDup );

	penum->unk.pEntries = penum->IFEntries;
	penum->unk.dwEntries = 1;
	penum->unk.pOnFinalRelease = QUARTZ_DestroyEnumUnknown;

	*ppobj = (void*)(&penum->enumunk);

	return S_OK;
}

