/*
 * Implements IBaseFilter for parsers. (internal)
 *
 * Copyright (C) Hidenori TAKESHIMA <hidenori@a2.ctktv.ne.jp>
 * Copyright (C) TransGaming Technologies Inc.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * FIXME - handle errors/flushing correctly.
 * FIXME - handle seeking.
 */

#include "config.h"

#include <unistd.h>

#include "windef.h"
#include "winbase.h"
#include "wingdi.h"
#include "winuser.h"
#include "mmsystem.h"
#include "winerror.h"
#include "strmif.h"
#include "control.h"
#include "vfwmsgs.h"
#include "evcode.h"
#include "uuids.h"

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

#include "quartz_private.h"
#include "parser.h"
#include "mtype.h"
#include "memalloc.h"

#define	QUARTZ_MSG_EXITTHREAD	(WM_APP+2)
#define	QUARTZ_MSG_SEEK			(WM_APP+3)

#ifndef OATRUE
#define OATRUE (-1)
#define OAFALSE (0)
#endif

HRESULT CParserOutPinImpl_InitIMediaSeeking( CParserOutPinImpl* This );
void CParserOutPinImpl_UninitIMediaSeeking( CParserOutPinImpl* This );
HRESULT CParserOutPinImpl_InitIMediaPosition( CParserOutPinImpl* This );
void CParserOutPinImpl_UninitIMediaPosition( CParserOutPinImpl* This );

/***************************************************************************
 *
 *	CParserImpl internal thread
 *
 */

static
void CParserImplThread_ClearAllRequests( CParserImpl* This )
{
	ULONG	nIndex;

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

	for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
	{
		This->m_ppOutPins[nIndex]->m_bReqUsed = FALSE;
		This->m_ppOutPins[nIndex]->m_pReqSample = NULL;
	}
}

static
void CParserImplThread_ReleaseAllRequests( CParserImpl* This )
{
	ULONG	nIndex;

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

	for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
	{
		if ( This->m_ppOutPins[nIndex]->m_bReqUsed )
		{
			if ( This->m_ppOutPins[nIndex]->m_pReqSample != NULL )
			{
				IMediaSample_Release(This->m_ppOutPins[nIndex]->m_pReqSample);
				This->m_ppOutPins[nIndex]->m_pReqSample = NULL;
			}
			This->m_ppOutPins[nIndex]->m_bReqUsed = FALSE;
		}
	}
}

static
BOOL CParserImplThread_HasPendingSamples( CParserImpl* This )
{
	ULONG	nIndex;

	for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
	{
		if ( This->m_ppOutPins[nIndex]->m_bReqUsed &&
			 This->m_ppOutPins[nIndex]->m_pReqSample != NULL )
			return TRUE;
	}

	return FALSE;
}

static
HRESULT CParserImplThread_FlushAllPendingSamples( CParserImpl* This )
{
	HRESULT hr;
	IMediaSample*	pSample;
	DWORD_PTR	dwContext;

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

	/* remove all samples from queue. */
	hr = IAsyncReader_BeginFlush(This->m_pReader);
	if ( FAILED(hr) )
		return hr;
	IAsyncReader_EndFlush(This->m_pReader);

	/* remove all processed samples from queue. */
	while ( 1 )
	{
		hr = IAsyncReader_WaitForNext(This->m_pReader,0,&pSample,&dwContext);
		if ( hr != S_OK )
			break;
	}

	CParserImplThread_ReleaseAllRequests(This);

	return NOERROR;
}

static HRESULT CParserImplThread_SendEndOfStream( CParserImpl* This )
{
	ULONG	nIndex;
	HRESULT hr;
	HRESULT hrRet;
	CParserOutPinImpl*	pOutPin;

	TRACE("(%p)\n",This);
	if ( This->m_bSendEOS )
		return NOERROR;
	This->m_bSendEOS = TRUE;

	hrRet = S_OK;
	for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
	{
		pOutPin = This->m_ppOutPins[nIndex];
		hr = CPinBaseImpl_SendEndOfStream(&pOutPin->pin);
		if ( FAILED(hr) )
		{
			if ( SUCCEEDED(hrRet) )
				hrRet = hr;
		}
		else
		{
			if ( hr != S_OK && hrRet == S_OK )
				hrRet = hr;
		}
	}

	return hrRet;
}

static
void CParserImplThread_MemDecommit( CParserImpl* This )
{
	ULONG	nIndex;
	IMemAllocator*	pAlloc;

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

	if ( This->m_pAllocator != NULL )
		IMemAllocator_Decommit( This->m_pAllocator );

	if ( This->m_ppOutPins != NULL && This->m_cOutStreams > 0 )
	{
		for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
		{
			pAlloc = This->m_ppOutPins[nIndex]->m_pOutPinAllocator;
			if ( pAlloc != NULL )
				IMemAllocator_Decommit( pAlloc );
		}
	}
}

static HRESULT CParserImplThread_SendFlush( CParserImpl* This )
{
	ULONG	nIndex;
	HRESULT hr;
	HRESULT hrRet;
	CParserOutPinImpl*	pOutPin;

	TRACE("(%p)\n",This);
	hrRet = S_OK;
	for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
	{
		pOutPin = This->m_ppOutPins[nIndex];
		hr = CPinBaseImpl_SendBeginFlush(&pOutPin->pin);
		if ( FAILED(hr) )
		{
			if ( SUCCEEDED(hrRet) )
				hrRet = hr;
		}
		else
		{
			if ( hr != S_OK && hrRet == S_OK )
				hrRet = hr;
			hr = CPinBaseImpl_SendEndFlush(&pOutPin->pin);
			if ( FAILED(hr) )
				hrRet = hr;
		}
	}

	return hrRet;
}

static void CParserImplThread_ErrorAbort( CParserImpl* This, HRESULT hr )
{
	ERR("(%p): result=%08lx\n", This, hr);
	CBaseFilterImpl_MediaEventNotify(
		&This->basefilter,EC_ERRORABORT,(LONG_PTR)hr,(LONG_PTR)0);
	CParserImplThread_SendEndOfStream(This);
}

static
void CParserImplThread_ResetAllStreams( CParserImpl* This )
{
	ULONG	nIndex;

	if ( This->m_pHandler->pSetCurPos != NULL )
	{
		for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
			This->m_pHandler->pSetCurPos(This,
				&This->m_guidTimeFormat,nIndex,(LONGLONG)0);
	}
}

static
HRESULT CParserImplThread_ProcessNextSample( CParserImpl* This )
{
	IMediaSample*	pSample;
	DWORD_PTR	dwContext;
	ULONG	nIndex;
	HRESULT hr;
	CParserOutPinImpl*	pOutPin;
	MSG	msg;

	while ( 1 )
	{
		if ( PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_REMOVE ) )
		{
			hr = NOERROR;
			switch ( msg.message )
			{
			case QUARTZ_MSG_EXITTHREAD:
				TRACE("(%p) EndThread\n",This);
				CParserImplThread_FlushAllPendingSamples(This);
				CParserImplThread_ClearAllRequests(This);
				CParserImplThread_SendFlush(This);
				CParserImplThread_SendEndOfStream(This);
				This->m_bSendEOS = FALSE;

				CParserImplThread_ResetAllStreams(This);
				CParserImplThread_MemDecommit(This);

				TRACE("(%p) exit thread\n",This);
				return S_FALSE;
			case QUARTZ_MSG_SEEK:
				FIXME("(%p) Seek\n",This);
				CParserImplThread_FlushAllPendingSamples(This);
				hr = CParserImplThread_SendFlush(This);
				CParserImplThread_SendEndOfStream(This);
				/* FIXME - process seeking. */
				/* FIXME - Send NewSegment. */
				break;
			default:
				FIXME( "invalid message %04u\n", (unsigned)msg.message );
				hr = E_FAIL;
				CParserImplThread_ErrorAbort(This,hr);
			}

			return hr;
		}

		hr = IAsyncReader_WaitForNext(This->m_pReader,PARSER_POLL_INTERVAL,&pSample,&dwContext);
		nIndex = (ULONG)dwContext;
		if ( hr != VFW_E_TIMEOUT )
			break;
	}
	if ( FAILED(hr) )
	{
		CParserImplThread_ErrorAbort(This,hr);
		return hr;
	}

	pOutPin = This->m_ppOutPins[nIndex];
	if ( pOutPin != NULL && pOutPin->m_bReqUsed )
	{
		if ( This->m_pHandler->pProcessSample != NULL ) {
			hr = This->m_pHandler->pProcessSample(This,nIndex,pOutPin->m_llReqStart,pOutPin->m_lReqLength,pOutPin->m_pReqSample);
			TRACE("ProcessSample returns %08lx\n", hr);
		}

		IMediaSample_SetSyncPoint( pOutPin->m_pReqSample, (pOutPin->m_dwSampleFlags & AM_SAMPLE_SPLICEPOINT) ? TRUE : FALSE );
		IMediaSample_SetPreroll( pOutPin->m_pReqSample, (pOutPin->m_dwSampleFlags & AM_SAMPLE_PREROLL) ? TRUE : FALSE );
		IMediaSample_SetDiscontinuity( pOutPin->m_pReqSample, (pOutPin->m_dwSampleFlags & AM_SAMPLE_DATADISCONTINUITY) ? TRUE : FALSE );

		if ( SUCCEEDED(hr) )
		{
			if ( pOutPin->m_pOutPinAllocator != NULL &&
				 pOutPin->m_pOutPinAllocator != This->m_pAllocator )
			{
				/* if pin has its own allocator, sample must be copied */
				hr = IMemAllocator_GetBuffer( This->m_pAllocator, &pSample, NULL, NULL, 0 );
				if ( SUCCEEDED(hr) )
				{
					hr = QUARTZ_IMediaSample_Copy(
						pSample, pOutPin->m_pReqSample, TRUE );
					TRACE("Copy returns %08lx\n", hr);
					if ( SUCCEEDED(hr) ) {
						hr = CPinBaseImpl_SendSample(&pOutPin->pin,pSample);
						TRACE("SendSample returns %08lx\n", hr);
					}
					IMediaSample_Release(pSample);
				}
			}
			else
			{
				hr = CPinBaseImpl_SendSample(&pOutPin->pin,pOutPin->m_pReqSample);
				TRACE("SendSample returns %08lx\n", hr);
			}
		}

		if ( FAILED(hr) )
			CParserImplThread_ErrorAbort(This,hr);

		IMediaSample_Release(pOutPin->m_pReqSample);
		pOutPin->m_pReqSample = NULL;
		pOutPin->m_bReqUsed = FALSE;
	}

	if ( SUCCEEDED(hr) )
		hr = NOERROR;

	TRACE("return %08lx\n",hr);

	return hr;
}

static
DWORD WINAPI CParserImplThread_Entry( LPVOID pv )
{
	CParserImpl*	This = (CParserImpl*)pv;
	BOOL	bReqNext;
	ULONG	nIndex = 0;
	HRESULT hr;
	REFERENCE_TIME	rtSampleTimeStart, rtSampleTimeEnd;
	LONGLONG	llReqStart;
	LONG	lReqLength;
	DWORD	dwSampleFlags;
	REFERENCE_TIME	rtReqStart, rtReqStop;
	IMediaSample*	pSample;
	MSG	msg;

	/* initialize the message queue. */
	PeekMessageA( &msg, (HWND)NULL, 0, 0, PM_NOREMOVE );

	CParserImplThread_ClearAllRequests(This);

	/* resume the owner thread. */
	SetEvent( This->m_hEventInit );

	TRACE( "Holding...\n" );
	sleep(1);
	TRACE( "Enter message loop.\n" );

	bReqNext = TRUE;
	while ( 1 )
	{
		if ( bReqNext )
		{
			/* Get the next request.  */
			hr = This->m_pHandler->pGetNextRequest( This, &nIndex, &llReqStart, &lReqLength, &rtReqStart, &rtReqStop, &dwSampleFlags );
			if ( FAILED(hr) )
			{
				CParserImplThread_ErrorAbort(This,hr);
				break;
			}
			if ( hr != S_OK )
			{
				/* Flush pending samples. */
				hr = S_OK;
				while ( CParserImplThread_HasPendingSamples(This) )
				{
					hr = CParserImplThread_ProcessNextSample(This);
					if ( hr != S_OK )
						break;
				}
				if ( hr != S_OK )
				{
					/* notification is already sent */
					break;
				}

				/* Send End Of Stream. */
				hr = CParserImplThread_SendEndOfStream(This);
				if ( hr != S_OK )
				{
					/* notification is already sent */
					break;
				}

				/* Blocking... */
				TRACE( "stream ended\n" );
				hr = CParserImplThread_ProcessNextSample(This);
				if ( hr != S_OK )
				{
					/* notification is already sent */
					break;
				}
				continue;
			}
			if ( This->m_ppOutPins[nIndex]->pin.pPinConnectedTo == NULL )
				continue;

			/* the AsyncReader uses these "time stamps" as file offsets */
			rtSampleTimeStart = llReqStart * QUARTZ_TIMEUNITS;
			rtSampleTimeEnd = (llReqStart + lReqLength) * QUARTZ_TIMEUNITS;
			bReqNext = FALSE;
		}

		if ( !This->m_ppOutPins[nIndex]->m_bReqUsed )
		{
			hr = IMemAllocator_GetBuffer( This->m_pAllocator, &pSample, NULL, NULL, 0 );
			if ( FAILED(hr) )
			{
				CParserImplThread_ErrorAbort(This,hr);
				break;
			}
			hr = IMediaSample_SetTime(pSample,&rtSampleTimeStart,&rtSampleTimeEnd);
			if ( SUCCEEDED(hr) )
				hr = IAsyncReader_Request(This->m_pReader,pSample,nIndex);
			if ( SUCCEEDED(hr) )
				hr = IMediaSample_SetTime(pSample,&rtReqStart,&rtReqStop);
			if ( FAILED(hr) )
			{
				CParserImplThread_ErrorAbort(This,hr);
				break;
			}

			This->m_ppOutPins[nIndex]->m_bReqUsed = TRUE;
			This->m_ppOutPins[nIndex]->m_pReqSample = pSample;
			This->m_ppOutPins[nIndex]->m_llReqStart = llReqStart;
			This->m_ppOutPins[nIndex]->m_lReqLength = lReqLength;
			This->m_ppOutPins[nIndex]->m_rtReqStart = rtReqStart;
			This->m_ppOutPins[nIndex]->m_rtReqStop = rtReqStop;
			This->m_ppOutPins[nIndex]->m_dwSampleFlags = dwSampleFlags;
			bReqNext = TRUE;
			continue;
		}

		hr = CParserImplThread_ProcessNextSample(This);
		if ( hr != S_OK )
		{
			/* notification is already sent */
			break;
		}
	}

	TRACE( "Leave message loop.\n" );

	This->m_dwThreadId = 0;
	This->basefilter.bIntermediateState = FALSE;
	SetEvent( This->m_hEventInit );

	return 0;
}

/***************************************************************************
 *
 *	CParserImpl internal methods
 *
 */

static
void CParserImpl_SetAsyncReader( CParserImpl* This, IAsyncReader* pReader )
{
	if ( This->m_pReader != NULL )
	{
		IAsyncReader_Release( This->m_pReader );
		This->m_pReader = NULL;
	}
	if ( pReader != NULL )
	{
		This->m_pReader = pReader;
		IAsyncReader_AddRef(This->m_pReader);
	}
}

static
void CParserImpl_ReleaseOutPins( CParserImpl* This )
{
	ULONG nIndex;

	if ( This->m_ppOutPins != NULL )
	{
		for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
		{
			if ( This->m_ppOutPins[nIndex] != NULL )
			{
				IUnknown_Release(This->m_ppOutPins[nIndex]->unk.punkControl);
				This->m_ppOutPins[nIndex] = NULL;
			}
		}
		QUARTZ_FreeMem(This->m_ppOutPins);
		This->m_ppOutPins = NULL;
	}
	This->m_cOutStreams = 0;
}

static
BOOL CParserImpl_OutPinsAreConnected( CParserImpl* This )
{
	QUARTZ_CompListItem*	pItem;
	IPin*	pPin;
	IPin*	pPinPeer;
	HRESULT hr;

	QUARTZ_CompList_Lock( This->basefilter.pOutPins );
	pItem = QUARTZ_CompList_GetFirst( This->basefilter.pOutPins );
	while ( pItem != NULL )
	{
		if ( pItem == NULL )
			break;
		pPin = (IPin*)QUARTZ_CompList_GetItemPtr(pItem);
		pPinPeer = NULL;
		hr = IPin_ConnectedTo(pPin,&pPinPeer);
		if ( hr == S_OK && pPinPeer != NULL )
		{
			IPin_Release(pPinPeer);
			QUARTZ_CompList_Unlock( This->basefilter.pOutPins );
			return TRUE;
		}
		pItem = QUARTZ_CompList_GetNext( This->basefilter.pOutPins, pItem );
	}
	QUARTZ_CompList_Unlock( This->basefilter.pOutPins );

	return FALSE;
}

static
void CParserImpl_ReleaseListOfOutPins( CParserImpl* This )
{
	QUARTZ_CompListItem*	pItem;

	QUARTZ_CompList_Lock( This->basefilter.pOutPins );
	while ( 1 )
	{
		pItem = QUARTZ_CompList_GetFirst( This->basefilter.pOutPins );
		if ( pItem == NULL )
			break;
		QUARTZ_CompList_RemoveComp(
			This->basefilter.pOutPins,
			QUARTZ_CompList_GetItemPtr(pItem) );
	}
	QUARTZ_CompList_Unlock( This->basefilter.pOutPins );
}


static
HRESULT CParserImpl_BeginThread( CParserImpl* This )
{
	DWORD dwRes;
	HANDLE hEvents[2];

	if ( This->m_bIsRunning )
	{
		TRACE("(%p) - already running\n",This);
		return NOERROR;
	}

	TRACE("launching thread\n");

	This->m_hEventInit = CreateEventA(NULL,TRUE,FALSE,NULL);
	if ( This->m_hEventInit == (HANDLE)NULL )
		return E_OUTOFMEMORY;

	/* create the processing thread. */
	This->m_hThread = CreateThread(
		NULL, 0,
		CParserImplThread_Entry,
		(LPVOID)This,
		0, &This->m_dwThreadId );
	if ( This->m_hThread == (HANDLE)NULL )
	{
		CloseHandle( This->m_hEventInit );
		This->m_hEventInit = (HANDLE)NULL;
		return E_FAIL;
	}

	hEvents[0] = This->m_hEventInit;
	hEvents[1] = This->m_hThread;

	dwRes = WaitForMultipleObjects(2,hEvents,FALSE,INFINITE);

	ResetEvent( This->m_hEventInit );
	CloseHandle( This->m_hThread );
	This->m_hThread = (HANDLE)NULL;

	if ( dwRes != WAIT_OBJECT_0 )
		return E_FAIL;

	TRACE("thread is running\n");
	This->m_bIsRunning = TRUE;

	return NOERROR;
}

static
BOOL CParserImpl_EndThread( CParserImpl* This, BOOL bAsync )
{
	DWORD	dwThreadId;

	TRACE("(%p)\n",This);
	dwThreadId = This->m_dwThreadId;
	if ( This->m_hEventInit != (HANDLE)NULL )
	{
		if ( dwThreadId != 0 )
			PostThreadMessageA(
				dwThreadId, QUARTZ_MSG_EXITTHREAD, 0, 0 );
		if ( bAsync &&
			 WaitForSingleObject( This->m_hEventInit, 0 ) == WAIT_TIMEOUT )
			return FALSE;

		WaitForSingleObject( This->m_hEventInit, INFINITE );
		CloseHandle( This->m_hEventInit );
		This->m_bIsRunning = FALSE;
		This->m_hEventInit = (HANDLE)NULL;
		TRACE("thread is ended\n");
	}

	return TRUE;
}

static
HRESULT CParserImpl_MemCommit( CParserImpl* This )
{
	HRESULT hr;
	ULONG	nIndex;
	IMemAllocator*	pAlloc;

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

	if ( This->m_pAllocator == NULL )
		return E_UNEXPECTED;

	hr = IMemAllocator_Commit( This->m_pAllocator );
	if ( FAILED(hr) )
		return hr;

	if ( This->m_ppOutPins != NULL && This->m_cOutStreams > 0 )
	{
		for ( nIndex = 0; nIndex < This->m_cOutStreams; nIndex++ )
		{
			pAlloc = This->m_ppOutPins[nIndex]->m_pOutPinAllocator;
			if ( pAlloc != NULL && pAlloc != This->m_pAllocator )
			{
				hr = IMemAllocator_Commit( pAlloc );
				if ( FAILED(hr) )
					return hr;
			}
		}
	}

	return NOERROR;
}

static
HRESULT CParserImpl_GetPreferredTimeFormat( CParserImpl* This, GUID* pguidFormat )
{
	static const GUID* tryformats[] =
	{
		&TIME_FORMAT_MEDIA_TIME,
		&TIME_FORMAT_FRAME,
		&TIME_FORMAT_SAMPLE,
		&TIME_FORMAT_FIELD,
		&TIME_FORMAT_BYTE,
		NULL,
	};
	DWORD	n;

	if ( This->m_pHandler->pIsTimeFormatSupported == NULL )
		return E_NOTIMPL;

	n = 0;
	while ( tryformats[n] != NULL )
	{
		if ( This->m_pHandler->pIsTimeFormatSupported( This, tryformats[n] ) == S_OK )
		{
			memcpy( pguidFormat, tryformats[n], sizeof(GUID) );
			return S_OK;
		}
		n ++;
	}

	return E_FAIL;

}


/***************************************************************************
 *
 *	CParserImpl methods
 *
 */

static HRESULT CParserImpl_OnActive( CBaseFilterImpl* pImpl )
{
	CParserImpl_THIS(pImpl,basefilter);

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

	if ( !CParserImpl_OutPinsAreConnected(This) )
		return NOERROR;

	return NOERROR;
}

static HRESULT CParserImpl_OnInactive( CBaseFilterImpl* pImpl )
{
	CParserImpl_THIS(pImpl,basefilter);
	HRESULT hr;

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

	if ( !CParserImpl_OutPinsAreConnected(This) )
		return NOERROR;

	TRACE( "I'm connected\n" );
	hr = CParserImpl_MemCommit(This);
	if ( FAILED(hr) )
		return hr;

	if ( This->basefilter.fstate == State_Stopped )
		CParserImpl_EndThread(This,FALSE);

	hr = CParserImpl_BeginThread(This);
	if ( FAILED(hr) )
	{
		FIXME("CParserImpl_BeginThread returns %08lx\n",hr);
		CParserImpl_EndThread(This,FALSE);
		return hr;
	}

	return NOERROR;
}

static HRESULT CParserImpl_OnStop( CBaseFilterImpl* pImpl )
{
	CParserImpl_THIS(pImpl,basefilter);

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

	This->basefilter.bIntermediateState = TRUE;
	if ( !CParserImpl_EndThread(This,FALSE) ) /* FIXME: bAsync=TRUE isn't stable yet */
		return VFW_S_STATE_INTERMEDIATE;

	This->basefilter.bIntermediateState = FALSE;
	return NOERROR;
}


static const CBaseFilterHandlers filterhandlers =
{
	CParserImpl_OnActive, /* pOnActive */
	CParserImpl_OnInactive, /* pOnInactive */
	CParserImpl_OnStop, /* pOnStop */
};


/***************************************************************************
 *
 *	CParserInPinImpl methods
 *
 */

static HRESULT CParserInPinImpl_OnPreConnect( CPinBaseImpl* pImpl, IPin* pPin )
{
	CParserInPinImpl_THIS(pImpl,pin);
	HRESULT hr;
	ULONG	nIndex;
	IUnknown*	punk;
	IAsyncReader* pReader = NULL;
	LPCWSTR pwszOutPinName;
	IMemAllocator*	pAllocActual;
	AM_MEDIA_TYPE*	pmt;

	TRACE("(%p,%p)\n",This,pPin);

	if ( This->pParser->m_pHandler->pInitParser == NULL ||
		 This->pParser->m_pHandler->pUninitParser == NULL ||
		 This->pParser->m_pHandler->pGetOutPinName == NULL ||
		 This->pParser->m_pHandler->pGetStreamType == NULL ||
		 This->pParser->m_pHandler->pCheckStreamType == NULL ||
		 This->pParser->m_pHandler->pGetAllocProp == NULL ||
		 This->pParser->m_pHandler->pGetNextRequest == NULL )
	{
		FIXME("this parser is not implemented.\n");
		return E_NOTIMPL;
	}

	/* at first, release all output pins. */
	if ( CParserImpl_OutPinsAreConnected(This->pParser) )
		return E_FAIL;
	CParserImpl_ReleaseListOfOutPins(This->pParser);
	CParserImpl_ReleaseOutPins(This->pParser);

	CParserImpl_SetAsyncReader( This->pParser, NULL );
	hr = IPin_QueryInterface( pPin, &IID_IAsyncReader, (void**)&pReader );
	if ( FAILED(hr) )
		return hr;
	CParserImpl_SetAsyncReader( This->pParser, pReader );
	IAsyncReader_Release(pReader);

	/* initialize parser. */
	hr = This->pParser->m_pHandler->pInitParser(This->pParser,&This->pParser->m_cOutStreams);
	if ( FAILED(hr) )
		return hr;
	This->pParser->m_ppOutPins = (CParserOutPinImpl**)QUARTZ_AllocMem(
		sizeof(CParserOutPinImpl*) * This->pParser->m_cOutStreams );
	if ( This->pParser->m_ppOutPins == NULL )
		return E_OUTOFMEMORY;
	for ( nIndex = 0; nIndex < This->pParser->m_cOutStreams; nIndex++ )
		This->pParser->m_ppOutPins[nIndex] = NULL;

	/* create and initialize an allocator. */
	hr = This->pParser->m_pHandler->pGetAllocProp(This->pParser,&This->pParser->m_propAlloc);
	if ( FAILED(hr) )
		return hr;
	if ( This->pParser->m_propAlloc.cbAlign == 0 )
		This->pParser->m_propAlloc.cbAlign = 1;

	if ( This->pParser->m_pAllocator == NULL )
	{
		hr = QUARTZ_CreateMemoryAllocator(NULL,(void**)&punk);
		if ( FAILED(hr) )
			return hr;
		hr = IUnknown_QueryInterface( punk, &IID_IMemAllocator, (void**)&This->pParser->m_pAllocator );
		IUnknown_Release(punk);
		if ( FAILED(hr) )
			return hr;
	}
	pAllocActual = NULL;
	hr = IAsyncReader_RequestAllocator(pReader,This->pParser->m_pAllocator,&This->pParser->m_propAlloc,&pAllocActual);
	if ( FAILED(hr) )
		return hr;
	IMemAllocator_Release(This->pParser->m_pAllocator);
	This->pParser->m_pAllocator = pAllocActual;

	/* create output pins. */
	for ( nIndex = 0; nIndex < This->pParser->m_cOutStreams; nIndex++ )
	{
		pwszOutPinName = This->pParser->m_pHandler->pGetOutPinName(This->pParser,nIndex);
		if ( pwszOutPinName == NULL )
			return E_FAIL;
		hr = QUARTZ_CreateParserOutPin(
			This->pParser,
			&This->pParser->m_csParser,
			&This->pParser->m_ppOutPins[nIndex],
			nIndex, pwszOutPinName );
		if ( SUCCEEDED(hr) )
			hr = QUARTZ_CompList_AddTailComp(
				This->pParser->basefilter.pOutPins,
				(IUnknown*)&(This->pParser->m_ppOutPins[nIndex]->pin),
				NULL, 0 );
		if ( FAILED(hr) )
			return hr;
		pmt = &This->pParser->m_ppOutPins[nIndex]->m_mtOut;
		QUARTZ_MediaType_Free( pmt );
		ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
		hr = This->pParser->m_pHandler->pGetStreamType(This->pParser,nIndex,pmt);
		if ( FAILED(hr) )
		{
			ZeroMemory( pmt, sizeof(AM_MEDIA_TYPE) );
			return hr;
		}
		This->pParser->m_ppOutPins[nIndex]->pin.cAcceptTypes = 1;
		This->pParser->m_ppOutPins[nIndex]->pin.pmtAcceptTypes = pmt;
	}

	return NOERROR;
}

static HRESULT CParserInPinImpl_OnDisconnect( CPinBaseImpl* pImpl )
{
	CParserInPinImpl_THIS(pImpl,pin);

	/* assume the graph is already stopped */
	/*CParserImpl_OnInactive(&This->pParser->basefilter);*/
	/*CParserImpl_OnStop(&This->pParser->basefilter);*/
	if ( This->pParser->m_pHandler->pUninitParser != NULL )
		This->pParser->m_pHandler->pUninitParser(This->pParser);
	CParserImpl_SetAsyncReader( This->pParser, NULL );
	if ( This->pParser->m_pAllocator != NULL )
	{
		IMemAllocator_Decommit(This->pParser->m_pAllocator);
		IMemAllocator_Release(This->pParser->m_pAllocator);
		This->pParser->m_pAllocator = NULL;
	}

	return NOERROR;
}

static HRESULT CParserInPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt )
{
	CParserInPinImpl_THIS(pImpl,pin);

	TRACE("(%p,%p)\n",This,pmt);

	if ( !IsEqualGUID( &pmt->majortype, &MEDIATYPE_Stream ) )
		return E_FAIL;

	return NOERROR;
}

static const CBasePinHandlers inputpinhandlers =
{
	CParserInPinImpl_OnPreConnect, /* pOnPreConnect */
	NULL, /* pOnPostConnect */
	CParserInPinImpl_OnDisconnect, /* pOnDisconnect */
	CParserInPinImpl_CheckMediaType, /* pCheckMediaType */
	NULL, /* pQualityNotify */
	NULL, /* pReceive */
	NULL, /* pReceiveCanBlock */
	NULL, /* pEndOfStream */
	NULL, /* pBeginFlush */
	NULL, /* pEndFlush */
	NULL, /* pNewSegment */
};

/***************************************************************************
 *
 *	CParserOutPinImpl methods
 *
 */

static HRESULT CParserOutPinImpl_OnPostConnect( CPinBaseImpl* pImpl, IPin* pPin )
{
	CParserOutPinImpl_THIS(pImpl,pin);
	ALLOCATOR_PROPERTIES	propReq;
	ALLOCATOR_PROPERTIES	propActual;
	IMemAllocator*	pAllocator;
	HRESULT hr;
	BOOL	bNewAllocator = FALSE;

	TRACE("(%p,%p)\n",This,pPin);

	if ( This->pin.pMemInputPinConnectedTo == NULL )
		return E_UNEXPECTED;

	if ( This->m_pOutPinAllocator != NULL )
	{
		IMemAllocator_Release(This->m_pOutPinAllocator);
		This->m_pOutPinAllocator = NULL;
	}

	/* try to use This->pParser->m_pAllocator. */
	ZeroMemory( &propReq, sizeof(ALLOCATOR_PROPERTIES) );
	hr = IMemInputPin_GetAllocatorRequirements(
		This->pin.pMemInputPinConnectedTo, &propReq );
	if ( propReq.cbAlign != 0 )
	{
		if ( This->pParser->m_propAlloc.cbAlign != ( This->pParser->m_propAlloc.cbAlign / propReq.cbAlign * propReq.cbAlign ) )
			bNewAllocator = TRUE;
	}
	if ( propReq.cbPrefix != 0 )
		bNewAllocator = TRUE;
	if ( !bNewAllocator )
	{
		hr = IMemInputPin_NotifyAllocator(
			This->pin.pMemInputPinConnectedTo,
			This->pParser->m_pAllocator, FALSE );
		if ( hr == NOERROR )
		{
			This->m_pOutPinAllocator = This->pParser->m_pAllocator;
			IMemAllocator_AddRef(This->m_pOutPinAllocator);
			return NOERROR;
		}
	}

	hr = IMemInputPin_GetAllocator(
			This->pin.pMemInputPinConnectedTo, &pAllocator );
	if ( FAILED(hr) )
		return hr;
	hr = IMemAllocator_SetProperties( pAllocator, &This->pParser->m_propAlloc, &propActual );
	if ( SUCCEEDED(hr) )
		hr = IMemInputPin_NotifyAllocator(
			This->pin.pMemInputPinConnectedTo, pAllocator, FALSE );
	if ( FAILED(hr) )
	{
		IMemAllocator_Release(pAllocator);
		return hr;
	}

	This->m_pOutPinAllocator = pAllocator;
	return NOERROR;
}

static HRESULT CParserOutPinImpl_OnDisconnect( CPinBaseImpl* pImpl )
{
	CParserOutPinImpl_THIS(pImpl,pin);

	if ( This->m_pOutPinAllocator != NULL )
	{
		IMemAllocator_Release(This->m_pOutPinAllocator);
		This->m_pOutPinAllocator = NULL;
	}

	return NOERROR;
}

static HRESULT CParserOutPinImpl_CheckMediaType( CPinBaseImpl* pImpl, const AM_MEDIA_TYPE* pmt )
{
	CParserOutPinImpl_THIS(pImpl,pin);
	HRESULT hr;

	TRACE("(%p,%p)\n",This,pmt);
	if ( pmt == NULL )
		return E_POINTER;

	if ( This->pParser->m_pHandler->pCheckStreamType == NULL )
		return E_NOTIMPL;

	hr = This->pParser->m_pHandler->pCheckStreamType( This->pParser, This->nStreamIndex, pmt );
	if ( FAILED(hr) )
		return hr;

	return NOERROR;
}


static const CBasePinHandlers outputpinhandlers =
{
	NULL, /* pOnPreConnect */
	CParserOutPinImpl_OnPostConnect, /* pOnPostConnect */
	CParserOutPinImpl_OnDisconnect, /* pOnDisconnect */
	CParserOutPinImpl_CheckMediaType, /* pCheckMediaType */
	NULL, /* pQualityNotify */
	OutputPinSync_Receive, /* pReceive */
	OutputPinSync_ReceiveCanBlock, /* pReceiveCanBlock */
	OutputPinSync_EndOfStream, /* pEndOfStream */
	OutputPinSync_BeginFlush, /* pBeginFlush */
	OutputPinSync_EndFlush, /* pEndFlush */
	OutputPinSync_NewSegment, /* pNewSegment */
};

/***************************************************************************
 *
 *	new/delete CParserImpl
 *
 */

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry FilterIFEntries[] =
{
  { &IID_IPersist, offsetof(CParserImpl,basefilter)-offsetof(CParserImpl,unk) },
  { &IID_IMediaFilter, offsetof(CParserImpl,basefilter)-offsetof(CParserImpl,unk) },
  { &IID_IBaseFilter, offsetof(CParserImpl,basefilter)-offsetof(CParserImpl,unk) },
};

static void QUARTZ_DestroyParser(IUnknown* punk)
{
	CParserImpl_THIS(punk,unk);

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

	if ( This->m_pInPin != NULL )
		CParserInPinImpl_OnDisconnect(&This->m_pInPin->pin);

	CParserImpl_SetAsyncReader( This, NULL );
	if ( This->m_pAllocator != NULL )
	{
		IMemAllocator_Release(This->m_pAllocator);
		This->m_pAllocator = NULL;
	}
	if ( This->m_pInPin != NULL )
	{
		IUnknown_Release(This->m_pInPin->unk.punkControl);
		This->m_pInPin = NULL;
	}
	CParserImpl_ReleaseOutPins( This );

	DeleteCriticalSection( &This->m_csParser );

	CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
}

HRESULT QUARTZ_CreateParser(
	IUnknown* punkOuter,void** ppobj,
	const CLSID* pclsidParser,
	LPCWSTR pwszParserName,
	LPCWSTR pwszInPinName,
	const ParserHandlers* pHandler )
{
	CParserImpl*	This = NULL;
	HRESULT hr;

	TRACE("(%p,%p)\n",punkOuter,ppobj);

	This = (CParserImpl*)
		QUARTZ_AllocObj( sizeof(CParserImpl) );
	if ( This == NULL )
		return E_OUTOFMEMORY;
	ZeroMemory( This, sizeof(CParserImpl) );

	This->m_pInPin = NULL;
	This->m_cOutStreams = 0;
	This->m_ppOutPins = NULL;
	memcpy( &This->m_guidTimeFormat, &TIME_FORMAT_NONE, sizeof(GUID) );
	This->m_pReader = NULL;
	This->m_pAllocator = NULL;
	ZeroMemory( &This->m_propAlloc, sizeof(ALLOCATOR_PROPERTIES) );
	This->m_hEventInit = (HANDLE)NULL;
	This->m_bIsRunning = FALSE;
	This->m_hThread = (HANDLE)NULL;
	This->m_dwThreadId = 0;
	This->m_bSendEOS = FALSE;
	This->m_pHandler = pHandler;
	This->m_pUserData = NULL;

	QUARTZ_IUnkInit( &This->unk, punkOuter );

	hr = CBaseFilterImpl_InitIBaseFilter(
		&This->basefilter,
		This->unk.punkControl,
		pclsidParser,
		pwszParserName,
		&filterhandlers );
	if ( SUCCEEDED(hr) )
	{
		/* construct this class. */
		hr = S_OK;

		if ( FAILED(hr) )
		{
			CBaseFilterImpl_UninitIBaseFilter(&This->basefilter);
		}
	}

	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj(This);
		return hr;
	}

	This->unk.pEntries = FilterIFEntries;
	This->unk.dwEntries = sizeof(FilterIFEntries)/sizeof(FilterIFEntries[0]);
	This->unk.pOnFinalRelease = QUARTZ_DestroyParser;
	InitializeCriticalSection( &This->m_csParser );

	/* create the input pin. */
	hr = QUARTZ_CreateParserInPin(
		This,
		&This->m_csParser,
		&This->m_pInPin,
		pwszInPinName );
	if ( SUCCEEDED(hr) )
		hr = QUARTZ_CompList_AddComp(
			This->basefilter.pInPins,
			(IUnknown*)&(This->m_pInPin->pin),
			NULL, 0 );

	if ( FAILED(hr) )
	{
		IUnknown_Release( This->unk.punkControl );
		return hr;
	}

	*ppobj = (void*)&(This->unk);

	(void)CParserImpl_GetPreferredTimeFormat( This, &This->m_guidTimeFormat );

	return S_OK;
}

/***************************************************************************
 *
 *	new/delete CParserInPinImpl
 *
 */

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry InPinIFEntries[] =
{
  { &IID_IPin, offsetof(CParserInPinImpl,pin)-offsetof(CParserInPinImpl,unk) },
  { &IID_IMemInputPin, offsetof(CParserInPinImpl,meminput)-offsetof(CParserInPinImpl,unk) },
};

static void QUARTZ_DestroyParserInPin(IUnknown* punk)
{
	CParserInPinImpl_THIS(punk,unk);

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

	CPinBaseImpl_UninitIPin( &This->pin );
	CMemInputPinBaseImpl_UninitIMemInputPin( &This->meminput );
}

HRESULT QUARTZ_CreateParserInPin(
	CParserImpl* pFilter,
	CRITICAL_SECTION* pcsPin,
	CParserInPinImpl** ppPin,
	LPCWSTR pwszPinName )
{
	CParserInPinImpl*	This = NULL;
	HRESULT hr;

	TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin);

	This = (CParserInPinImpl*)
		QUARTZ_AllocObj( sizeof(CParserInPinImpl) );
	if ( This == NULL )
		return E_OUTOFMEMORY;

	QUARTZ_IUnkInit( &This->unk, NULL );
	This->pParser = pFilter;

	hr = CPinBaseImpl_InitIPin(
		&This->pin,
		This->unk.punkControl,
		pcsPin, NULL,
		&pFilter->basefilter,
		pwszPinName,
		FALSE,
		&inputpinhandlers );

	if ( SUCCEEDED(hr) )
	{
		hr = CMemInputPinBaseImpl_InitIMemInputPin(
			&This->meminput,
			This->unk.punkControl,
			&This->pin );
		if ( FAILED(hr) )
		{
			CPinBaseImpl_UninitIPin( &This->pin );
		}
	}

	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj(This);
		return hr;
	}

	This->unk.pEntries = InPinIFEntries;
	This->unk.dwEntries = sizeof(InPinIFEntries)/sizeof(InPinIFEntries[0]);
	This->unk.pOnFinalRelease = QUARTZ_DestroyParserInPin;

	*ppPin = This;

	TRACE("returned with %p\n", *ppPin);

	return S_OK;
}


/***************************************************************************
 *
 *	new/delete CParserOutPinImpl
 *
 */

/* can I use offsetof safely? - FIXME? */
static QUARTZ_IFEntry OutPinIFEntries[] =
{
  { &IID_IPin, offsetof(CParserOutPinImpl,pin)-offsetof(CParserOutPinImpl,unk) },
  { &IID_IQualityControl, offsetof(CParserOutPinImpl,qcontrol)-offsetof(CParserOutPinImpl,unk) },
  { &IID_IMediaSeeking, offsetof(CParserOutPinImpl,mediaseeking)-offsetof(CParserOutPinImpl,unk) },
  { &IID_IMediaPosition, offsetof(CParserOutPinImpl,mediaposition)-offsetof(CParserOutPinImpl,unk) },
};

static void QUARTZ_DestroyParserOutPin(IUnknown* punk)
{
	CParserOutPinImpl_THIS(punk,unk);

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

	QUARTZ_MediaType_Free( &This->m_mtOut );
	if ( This->m_pOutPinAllocator != NULL )
		IMemAllocator_Release(This->m_pOutPinAllocator);

	CParserOutPinImpl_UninitIMediaPosition(This);
	CParserOutPinImpl_UninitIMediaSeeking(This);
	CQualityControlPassThruImpl_UninitIQualityControl( &This->qcontrol );
	CPinBaseImpl_UninitIPin( &This->pin );

}

HRESULT QUARTZ_CreateParserOutPin(
	CParserImpl* pFilter,
	CRITICAL_SECTION* pcsPin,
	CParserOutPinImpl** ppPin,
	ULONG nStreamIndex,
	LPCWSTR pwszPinName )
{
	CParserOutPinImpl*	This = NULL;
	HRESULT hr;

	TRACE("(%p,%p,%p)\n",pFilter,pcsPin,ppPin);

	This = (CParserOutPinImpl*)
		QUARTZ_AllocObj( sizeof(CParserOutPinImpl) );
	if ( This == NULL )
		return E_OUTOFMEMORY;

	QUARTZ_IUnkInit( &This->unk, NULL );
	This->pParser = pFilter;
	This->nStreamIndex = nStreamIndex;
	ZeroMemory( &This->m_mtOut, sizeof(AM_MEDIA_TYPE) );
	This->m_pOutPinAllocator = NULL;
	This->m_pUserData = NULL;
	This->m_bReqUsed = FALSE;
	This->m_pReqSample = NULL;
	This->m_llReqStart = 0;
	This->m_lReqLength = 0;
	This->m_rtReqStart = 0;
	This->m_rtReqStop = 0;
	This->m_dwSampleFlags = 0;


	hr = CPinBaseImpl_InitIPin(
		&This->pin,
		This->unk.punkControl,
		pcsPin, NULL,
		&pFilter->basefilter,
		pwszPinName,
		TRUE,
		&outputpinhandlers );

	if ( SUCCEEDED(hr) )
	{
		hr = CQualityControlPassThruImpl_InitIQualityControl(
			&This->qcontrol,
			This->unk.punkControl,
			&This->pin );
		if ( SUCCEEDED(hr) )
		{
			hr = CParserOutPinImpl_InitIMediaSeeking(This);
			if ( SUCCEEDED(hr) )
			{
				hr = CParserOutPinImpl_InitIMediaPosition(This);
				if ( FAILED(hr) )
				{
					CParserOutPinImpl_UninitIMediaSeeking(This);
				}
			}
			if ( FAILED(hr) )
			{
				CQualityControlPassThruImpl_UninitIQualityControl( &This->qcontrol );
			}
		}
		if ( FAILED(hr) )
		{
			CPinBaseImpl_UninitIPin( &This->pin );
		}
	}

	if ( FAILED(hr) )
	{
		QUARTZ_FreeObj(This);
		return hr;
	}

	This->unk.pEntries = OutPinIFEntries;
	This->unk.dwEntries = sizeof(OutPinIFEntries)/sizeof(OutPinIFEntries[0]);
	This->unk.pOnFinalRelease = QUARTZ_DestroyParserOutPin;

	*ppPin = This;

	TRACE("returned with %p\n", *ppPin);

	return S_OK;
}


/***************************************************************************
 *
 *	IMediaSeeking for CParserOutPinImpl
 *
 */

static HRESULT WINAPI
IMediaSeeking_fnQueryInterface(IMediaSeeking* iface,REFIID riid,void** ppobj)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

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

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

static ULONG WINAPI
IMediaSeeking_fnAddRef(IMediaSeeking* iface)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

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

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

static ULONG WINAPI
IMediaSeeking_fnRelease(IMediaSeeking* iface)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

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

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


static HRESULT WINAPI
IMediaSeeking_fnGetCapabilities(IMediaSeeking* iface,DWORD* pdwCaps)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pdwCaps == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetSeekingCaps == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pdwCaps);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetSeekingCaps( This->pParser, pdwCaps );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnCheckCapabilities(IMediaSeeking* iface,DWORD* pdwCaps)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;
	DWORD	dwCaps;

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

	if ( pdwCaps == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetSeekingCaps == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pdwCaps);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetSeekingCaps( This->pParser, &dwCaps );
		if ( SUCCEEDED(hr) )
		{
			dwCaps &= *pdwCaps;
			if ( dwCaps == *pdwCaps )
				hr = S_OK;
			else
			if ( dwCaps != 0 )
				hr = S_FALSE;
			else
				hr = E_FAIL;
			*pdwCaps = dwCaps;
		}
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaSeeking_fnIsFormatSupported(IMediaSeeking* iface,const GUID* pidFormat)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pidFormat == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pIsTimeFormatSupported == NULL )
	{
		FIXME("(%p)->(%s) not implemented\n",This,debugstr_guid(pidFormat));
	}
	else
	{
		hr = This->pParser->m_pHandler->pIsTimeFormatSupported( This->pParser, pidFormat );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnQueryPreferredFormat(IMediaSeeking* iface,GUID* pidFormat)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT hr;

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

	EnterCriticalSection( &This->pParser->m_csParser );
	hr = CParserImpl_GetPreferredTimeFormat( This->pParser, pidFormat );
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnGetTimeFormat(IMediaSeeking* iface,GUID* pidFormat)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pidFormat == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pIsTimeFormatSupported == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pidFormat);
	}
	else
	{
		memcpy( pidFormat, &This->pParser->m_guidTimeFormat, sizeof(GUID) );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnIsUsingTimeFormat(IMediaSeeking* iface,const GUID* pidFormat)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pidFormat == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pIsTimeFormatSupported == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pidFormat);
	}
	else
	{
		hr = IsEqualGUID( pidFormat, &This->pParser->m_guidTimeFormat ) ? S_OK : S_FALSE;
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnSetTimeFormat(IMediaSeeking* iface,const GUID* pidFormat)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pidFormat == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pIsTimeFormatSupported == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pidFormat);
	}
	else
	{
		if ( This->pParser->m_pHandler->pIsTimeFormatSupported( This->pParser, pidFormat ) == S_OK )
		{
			memcpy( &This->pParser->m_guidTimeFormat, pidFormat, sizeof(GUID) );
		}
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnGetDuration(IMediaSeeking* iface,LONGLONG* pllDuration)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pllDuration == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetDuration == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pllDuration);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetDuration( This->pParser, &This->pParser->m_guidTimeFormat, This->nStreamIndex, pllDuration );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnGetStopPosition(IMediaSeeking* iface,LONGLONG* pllPos)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pllPos == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetStopPos == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pllPos);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetStopPos( This->pParser, &This->pParser->m_guidTimeFormat, This->nStreamIndex, pllPos );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnGetCurrentPosition(IMediaSeeking* iface,LONGLONG* pllPos)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pllPos == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetCurPos == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pllPos);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetCurPos( This->pParser, &This->pParser->m_guidTimeFormat, This->nStreamIndex, pllPos );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnConvertTimeFormat(IMediaSeeking* iface,LONGLONG* pllOut,const GUID* pidFmtOut,LONGLONG llIn,const GUID* pidFmtIn)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaSeeking_fnSetPositions(IMediaSeeking* iface,LONGLONG* pllCur,DWORD dwCurFlags,LONGLONG* pllStop,DWORD dwStopFlags)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaSeeking_fnGetPositions(IMediaSeeking* iface,LONGLONG* pllCur,LONGLONG* pllStop)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);
	HRESULT	hr = E_NOTIMPL;

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

	if ( pllCur == NULL || pllStop == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetCurPos == NULL ||
		 This->pParser->m_pHandler->pGetStopPos == NULL )
	{
		FIXME("(%p)->(%p,%p) not implemented\n",This,pllCur,pllStop);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetCurPos( This->pParser, &This->pParser->m_guidTimeFormat, This->nStreamIndex, pllCur );
		if ( SUCCEEDED(hr) )
			hr = This->pParser->m_pHandler->pGetStopPos( This->pParser, &This->pParser->m_guidTimeFormat, This->nStreamIndex, pllStop );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaSeeking_fnGetAvailable(IMediaSeeking* iface,LONGLONG* pllFirst,LONGLONG* pllLast)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaSeeking_fnSetRate(IMediaSeeking* iface,double dblRate)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaSeeking_fnGetRate(IMediaSeeking* iface,double* pdblRate)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaSeeking_fnGetPreroll(IMediaSeeking* iface,LONGLONG* pllPreroll)
{
	CParserOutPinImpl_THIS(iface,mediaseeking);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}




static ICOM_VTABLE(IMediaSeeking) imediaseeking =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	/* IUnknown fields */
	IMediaSeeking_fnQueryInterface,
	IMediaSeeking_fnAddRef,
	IMediaSeeking_fnRelease,
	/* IMediaSeeking fields */
	IMediaSeeking_fnGetCapabilities,
	IMediaSeeking_fnCheckCapabilities,
	IMediaSeeking_fnIsFormatSupported,
	IMediaSeeking_fnQueryPreferredFormat,
	IMediaSeeking_fnGetTimeFormat,
	IMediaSeeking_fnIsUsingTimeFormat,
	IMediaSeeking_fnSetTimeFormat,
	IMediaSeeking_fnGetDuration,
	IMediaSeeking_fnGetStopPosition,
	IMediaSeeking_fnGetCurrentPosition,
	IMediaSeeking_fnConvertTimeFormat,
	IMediaSeeking_fnSetPositions,
	IMediaSeeking_fnGetPositions,
	IMediaSeeking_fnGetAvailable,
	IMediaSeeking_fnSetRate,
	IMediaSeeking_fnGetRate,
	IMediaSeeking_fnGetPreroll,
};

HRESULT CParserOutPinImpl_InitIMediaSeeking( CParserOutPinImpl* This )
{
	TRACE("(%p)\n",This);
	ICOM_VTBL(&This->mediaseeking) = &imediaseeking;

	return NOERROR;
}

void CParserOutPinImpl_UninitIMediaSeeking( CParserOutPinImpl* This )
{
	TRACE("(%p)\n",This);
}

/***************************************************************************
 *
 *	IMediaPosition for CParserOutPinImpl
 *
 */

static HRESULT WINAPI
IMediaPosition_fnQueryInterface(IMediaPosition* iface,REFIID riid,void** ppobj)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

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

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

static ULONG WINAPI
IMediaPosition_fnAddRef(IMediaPosition* iface)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

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

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

static ULONG WINAPI
IMediaPosition_fnRelease(IMediaPosition* iface)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

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

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

static HRESULT WINAPI
IMediaPosition_fnGetTypeInfoCount(IMediaPosition* iface,UINT* pcTypeInfo)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaPosition_fnGetTypeInfo(IMediaPosition* iface,UINT iTypeInfo, LCID lcid, ITypeInfo** ppobj)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaPosition_fnGetIDsOfNames(IMediaPosition* iface,REFIID riid, LPOLESTR* ppwszName, UINT cNames, LCID lcid, DISPID* pDispId)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaPosition_fnInvoke(IMediaPosition* iface,DISPID DispId, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarRes, EXCEPINFO* pExcepInfo, UINT* puArgErr)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}


static HRESULT WINAPI
IMediaPosition_fnget_Duration(IMediaPosition* iface,REFTIME* prefTime)
{
	CParserOutPinImpl_THIS(iface,mediaposition);
	HRESULT	hr = E_NOTIMPL;
	LONGLONG	llPos;

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

	if ( prefTime == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetDuration == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,prefTime);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetDuration( This->pParser, &TIME_FORMAT_MEDIA_TIME, This->nStreamIndex, &llPos );
		if ( SUCCEEDED(hr) )
			*prefTime = (REFTIME)llPos;
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaPosition_fnput_CurrentPosition(IMediaPosition* iface,REFTIME refTime)
{
	CParserOutPinImpl_THIS(iface,mediaposition);
	HRESULT	hr = E_NOTIMPL;
	/*LONGLONG	llPos;*/

	FIXME("(%p)->() stub!\n",This);

#if 0	/* not yet */
	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pSetCurPos == NULL )
	{
		FIXME("(%p)->() not implemented\n",This);
	}
	else
	{
		llPos = (LONGLONG)refTime;
		hr = This->pParser->m_pHandler->pSetCurPos( This->pParser, &TIME_FORMAT_MEDIA_TIME, This->nStreamIndex, llPos );
		/* FIXME - flush all streams. */
	}
	LeaveCriticalSection( &This->pParser->m_csParser );
#endif

	return hr;
}

static HRESULT WINAPI
IMediaPosition_fnget_CurrentPosition(IMediaPosition* iface,REFTIME* prefTime)
{
	CParserOutPinImpl_THIS(iface,mediaposition);
	HRESULT	hr = E_NOTIMPL;
	LONGLONG	llPos;

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

	if ( prefTime == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetCurPos == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,prefTime);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetCurPos( This->pParser, &TIME_FORMAT_MEDIA_TIME, This->nStreamIndex, &llPos );
		if ( SUCCEEDED(hr) )
			*prefTime = (REFTIME)llPos;
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaPosition_fnget_StopTime(IMediaPosition* iface,REFTIME* prefTime)
{
	CParserOutPinImpl_THIS(iface,mediaposition);
	HRESULT	hr = E_NOTIMPL;
	LONGLONG	llPos;

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

	if ( prefTime == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetStopPos == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,prefTime);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetStopPos( This->pParser, &TIME_FORMAT_MEDIA_TIME, This->nStreamIndex, &llPos );
		if ( SUCCEEDED(hr) )
			*prefTime = (REFTIME)llPos;
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaPosition_fnput_StopTime(IMediaPosition* iface,REFTIME refTime)
{
	CParserOutPinImpl_THIS(iface,mediaposition);
	HRESULT	hr = E_NOTIMPL;
	LONGLONG	llPos;

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

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pSetStopPos == NULL )
	{
		FIXME("(%p)->() not implemented\n",This);
	}
	else
	{
		llPos = (LONGLONG)refTime;
		hr = This->pParser->m_pHandler->pSetStopPos( This->pParser, &TIME_FORMAT_MEDIA_TIME, This->nStreamIndex, llPos );
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaPosition_fnget_PrerollTime(IMediaPosition* iface,REFTIME* prefTime)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaPosition_fnput_PrerollTime(IMediaPosition* iface,REFTIME refTime)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	FIXME("(%p)->() stub!\n",This);

	return E_NOTIMPL;
}

static HRESULT WINAPI
IMediaPosition_fnput_Rate(IMediaPosition* iface,double dblRate)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	return IMediaSeeking_SetRate(CParserOutPinImpl_IMediaSeeking(This),dblRate);
}

static HRESULT WINAPI
IMediaPosition_fnget_Rate(IMediaPosition* iface,double* pdblRate)
{
	CParserOutPinImpl_THIS(iface,mediaposition);

	return IMediaSeeking_GetRate(CParserOutPinImpl_IMediaSeeking(This),pdblRate);
}

static HRESULT WINAPI
IMediaPosition_fnCanSeekForward(IMediaPosition* iface,LONG* pCanSeek)
{
	CParserOutPinImpl_THIS(iface,mediaposition);
	HRESULT	hr = E_NOTIMPL;
	DWORD	dwCaps;

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

	if ( pCanSeek == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetSeekingCaps == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pCanSeek);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetSeekingCaps( This->pParser, &dwCaps );
		if ( SUCCEEDED(hr) )
		{
			*pCanSeek = (dwCaps & AM_SEEKING_CanSeekForwards) ? OATRUE : OAFALSE;
			hr = S_OK;
		}
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}

static HRESULT WINAPI
IMediaPosition_fnCanSeekBackward(IMediaPosition* iface,LONG* pCanSeek)
{
	CParserOutPinImpl_THIS(iface,mediaposition);
	HRESULT	hr = E_NOTIMPL;
	DWORD	dwCaps;

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

	if ( pCanSeek == NULL )
		return E_POINTER;

	EnterCriticalSection( &This->pParser->m_csParser );
	if ( This->pParser->m_pHandler->pGetSeekingCaps == NULL )
	{
		FIXME("(%p)->(%p) not implemented\n",This,pCanSeek);
	}
	else
	{
		hr = This->pParser->m_pHandler->pGetSeekingCaps( This->pParser, &dwCaps );
		if ( SUCCEEDED(hr) )
		{
			*pCanSeek = (dwCaps & AM_SEEKING_CanSeekBackwards) ? OATRUE : OAFALSE;
			hr = S_OK;
		}
	}
	LeaveCriticalSection( &This->pParser->m_csParser );

	return hr;
}


static ICOM_VTABLE(IMediaPosition) imediaposition =
{
	ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
	/* IUnknown fields */
	IMediaPosition_fnQueryInterface,
	IMediaPosition_fnAddRef,
	IMediaPosition_fnRelease,
	/* IDispatch fields */
	IMediaPosition_fnGetTypeInfoCount,
	IMediaPosition_fnGetTypeInfo,
	IMediaPosition_fnGetIDsOfNames,
	IMediaPosition_fnInvoke,
	/* IMediaPosition fields */
	IMediaPosition_fnget_Duration,
	IMediaPosition_fnput_CurrentPosition,
	IMediaPosition_fnget_CurrentPosition,
	IMediaPosition_fnget_StopTime,
	IMediaPosition_fnput_StopTime,
	IMediaPosition_fnget_PrerollTime,
	IMediaPosition_fnput_PrerollTime,
	IMediaPosition_fnput_Rate,
	IMediaPosition_fnget_Rate,
	IMediaPosition_fnCanSeekForward,
	IMediaPosition_fnCanSeekBackward,
};


HRESULT CParserOutPinImpl_InitIMediaPosition( CParserOutPinImpl* This )
{
	TRACE("(%p)\n",This);
	ICOM_VTBL(&This->mediaposition) = &imediaposition;

	return NOERROR;
}

void CParserOutPinImpl_UninitIMediaPosition( CParserOutPinImpl* This )
{
	TRACE("(%p)\n",This);
}

