/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: sinkctl.cpp,v 1.6.8.1 2004/07/09 02:05:58 hubbe Exp $
 * 
 * Portions Copyright (c) 1995-2004 RealNetworks, Inc. All Rights Reserved.
 * 
 * The contents of this file, and the files included with this file,
 * are subject to the current version of the RealNetworks Public
 * Source License (the "RPSL") available at
 * http://www.helixcommunity.org/content/rpsl unless you have licensed
 * the file under the current version of the RealNetworks Community
 * Source License (the "RCSL") available at
 * http://www.helixcommunity.org/content/rcsl, in which case the RCSL
 * will apply. You may also obtain the license terms directly from
 * RealNetworks.  You may not use this file except in compliance with
 * the RPSL or, if you have a valid RCSL with RealNetworks applicable
 * to this file, the RCSL.  Please see the applicable RPSL or RCSL for
 * the rights, obligations and limitations governing use of the
 * contents of the file.
 * 
 * Alternatively, the contents of this file may be used under the
 * terms of the GNU General Public License Version 2 or later (the
 * "GPL") in which case the provisions of the GPL are applicable
 * instead of those above. If you wish to allow use of your version of
 * this file only under the terms of the GPL, and not to allow others
 * to use your version of this file under the terms of either the RPSL
 * or RCSL, indicate your decision by deleting the provisions above
 * and replace them with the notice and other provisions required by
 * the GPL. If you do not delete the provisions above, a recipient may
 * use your version of this file under the terms of any one of the
 * RPSL, the RCSL or the GPL.
 * 
 * This file is part of the Helix DNA Technology. RealNetworks is the
 * developer of the Original Code and owns the copyrights in the
 * portions it created.
 * 
 * This file, and the files included with this file, is distributed
 * and made available on an 'AS IS' basis, WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, AND REALNETWORKS HEREBY DISCLAIMS
 * ALL SUCH WARRANTIES, INCLUDING WITHOUT LIMITATION, ANY WARRANTIES
 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET
 * ENJOYMENT OR NON-INFRINGEMENT.
 * 
 * Technology Compatibility Kit Test Suite(s) Location:
 *    http://www.helixcommunity.org/content/tck
 * 
 * Contributor(s):
 * 
 * ***** END LICENSE BLOCK ***** */

#include "hxcom.h"
#include "hxtypes.h"
#include "hxclsnk.h"
#include "hxengin.h"
#include "hxcore.h"
#include "hxengin.h"
#include "hxsched.h"
#include "hxerror.h"
#include "hxslist.h"
#include "hxcbobj.h"
#include "sinkctl.h"

#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE
static const char HX_THIS_FILE[] = __FILE__;
#endif

CHXAdviseSinkControl::PlayerAdviseSink::PlayerAdviseSink(
				    IHXClientAdviseSink* pAdviseSink,
				    BOOL bInterruptSafe)
{
    m_pAdviseSink	    = pAdviseSink;
    m_pAdviseSink->AddRef();
    m_bInterruptSafe	    = bInterruptSafe;
    m_pPendingAdviseList    = NULL;
}

CHXAdviseSinkControl::PlayerAdviseSink::~PlayerAdviseSink()
{
    while (m_pPendingAdviseList && m_pPendingAdviseList->GetCount() > 0)
    {
	PendingAdvise* pPendingAdvise = 
		    (PendingAdvise*) m_pPendingAdviseList->RemoveHead();
	delete pPendingAdvise;
    }

    HX_RELEASE(m_pAdviseSink);
    HX_DELETE(m_pPendingAdviseList);
}

CHXAdviseSinkControl::CHXAdviseSinkControl()
    : m_lRefCount(0) 
    , m_pInterruptState(NULL)
{
    m_pCallback = new CHXGenericCallback((void*)this, (fGenericCBFunc)AdviseSinkCallback);
    m_pCallback->AddRef();
}

CHXAdviseSinkControl::~CHXAdviseSinkControl()
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);

    	HX_DELETE(pPlayerAdviseSink);
    }
    m_pSinkList.RemoveAll();

    HX_RELEASE(m_pInterruptState);

    if (m_pCallback && m_pScheduler)
    {
        m_pScheduler->Remove(m_pCallback->GetPendingCallback());
        m_pCallback->CallbackCanceled();
    }

    HX_RELEASE(m_pCallback);
    HX_RELEASE(m_pScheduler);
}

void
CHXAdviseSinkControl::Init(IHXClientEngine* pEngine)
{
    pEngine->QueryInterface(IID_IHXInterruptState, 
				(void**) &m_pInterruptState);
    
    pEngine->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler);
}

HX_RESULT   
CHXAdviseSinkControl::AddAdviseSink(IHXClientAdviseSink* pAdviseSink)
{
    IHXInterruptSafe*	pInterruptSafe	=NULL;
    BOOL		bInterruptSafe	=FALSE;
    pAdviseSink->QueryInterface(IID_IHXInterruptSafe,(void**) &pInterruptSafe);
    if (pInterruptSafe)
    {
	bInterruptSafe=pInterruptSafe->IsInterruptSafe();
	pInterruptSafe->Release();
    }

    PlayerAdviseSink* pPlayerAdviseSink = new PlayerAdviseSink(pAdviseSink, 
							bInterruptSafe);
    m_pSinkList.AddTail(pPlayerAdviseSink);

    return HXR_OK;
}

HX_RESULT   
CHXAdviseSinkControl::RemoveAdviseSink(IHXClientAdviseSink* pAdviseSink)
{
    PlayerAdviseSink* pPlayerAdviseSink;
    CHXSimpleList::Iterator lIterator	= m_pSinkList.Begin();
    for (; lIterator != m_pSinkList.End(); ++lIterator)
    {
	pPlayerAdviseSink = (PlayerAdviseSink*) (*lIterator);
    	if (pPlayerAdviseSink->m_pAdviseSink == pAdviseSink)
    	{
	    LISTPOSITION pos = m_pSinkList.Find(pPlayerAdviseSink);
	    m_pSinkList.RemoveAt(pos);	    
	    HX_DELETE(pPlayerAdviseSink);
	    return HXR_OK;
    	}
    }

    return HXR_FAIL;
}



//
//	This object is never interrupt safe
//	and thus cannot be called at interrupt time.
//
STDMETHODIMP_(BOOL) CHXAdviseSinkControl::IsInterruptSafe()
{
	return FALSE;
}
	
// *** IUnknown methods ***

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::QueryInterface
//  Purpose:
//	Implement this to export the interfaces supported by your 
//	object.
//
STDMETHODIMP CHXAdviseSinkControl::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IHXClientAdviseSink), (IHXClientAdviseSink*)this },
            { GET_IIDHANDLE(IID_IHXInterruptSafe), (IHXInterruptSafe*)this },
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXClientAdviseSink*)this },
        };
    
    return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::AddRef
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) CHXAdviseSinkControl::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

/////////////////////////////////////////////////////////////////////////
//  Method:
//	IUnknown::Release
//  Purpose:
//	Everyone usually implements this the same... feel free to use
//	this implementation.
//
STDMETHODIMP_(ULONG32) CHXAdviseSinkControl::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}


void
CHXAdviseSinkControl::AddToPendingList(PlayerAdviseSink* pPlayerAdviseSink,
				       AdviseType type,
				       UINT32 ulArg1, 
				       UINT32 ulArg2, 
				       char*  pHostName)
{
    if (!pPlayerAdviseSink->m_pPendingAdviseList)
    {
	pPlayerAdviseSink->m_pPendingAdviseList = new CHXSimpleList;
    }

    PendingAdvise* pPendingAdvise = new PendingAdvise;
    pPendingAdvise->m_AdviseType = type;
    pPendingAdvise->m_ulArg1 = ulArg1;
    pPendingAdvise->m_ulArg2 = ulArg2;

    if (pHostName)
    {
	pPendingAdvise->m_pHostName = new char[strlen(pHostName) + 1];
	strcpy(pPendingAdvise->m_pHostName, pHostName); /* Flawfinder: ignore */
    }

    pPlayerAdviseSink->m_pPendingAdviseList->AddTail((void*) pPendingAdvise);

    if (!m_pCallback->GetPendingCallback())
    {
        m_pCallback->CallbackScheduled(m_pScheduler->RelativeEnter(m_pCallback, 0));
    }
}

void
CHXAdviseSinkControl::ProcessAllRequests(void)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	ProcessPendingRequests(pPlayerAdviseSink);
    }
}

void
CHXAdviseSinkControl::ProcessPendingRequests(PlayerAdviseSink* 
						pPlayerAdviseSink)
{
    while (pPlayerAdviseSink->m_pPendingAdviseList && 
	   pPlayerAdviseSink->m_pPendingAdviseList->GetCount() > 0)
    {
	PendingAdvise* pPendingAdvise = (PendingAdvise*) 
		pPlayerAdviseSink->m_pPendingAdviseList->RemoveHead();
	switch(pPendingAdvise->m_AdviseType)
	{
	    case ONPOSLENGTH:
    		pPlayerAdviseSink->m_pAdviseSink->OnPosLength(
					    pPendingAdvise->m_ulArg1, 
					    pPendingAdvise->m_ulArg2);
		break;
	    case ONPRESENTATIONOPENED:
    		pPlayerAdviseSink->m_pAdviseSink->OnPresentationOpened();
		break;
	    case ONPRESENTATIONCLOSED:
    		pPlayerAdviseSink->m_pAdviseSink->OnPresentationClosed();
		break;
	    case ONSTATISTICSCHANGED:
    		pPlayerAdviseSink->m_pAdviseSink->OnStatisticsChanged();
		break;
	    case ONPRESEEK:
    		pPlayerAdviseSink->m_pAdviseSink->OnPreSeek(
					    pPendingAdvise->m_ulArg1, 
					    pPendingAdvise->m_ulArg2);
		break;
	    case ONPOSTSEEK:
    		pPlayerAdviseSink->m_pAdviseSink->OnPostSeek(
					    pPendingAdvise->m_ulArg1, 
					    pPendingAdvise->m_ulArg2);
		break;
	    case ONSTOP:
    		pPlayerAdviseSink->m_pAdviseSink->OnStop();
		break;
	    case ONPAUSE:
        	pPlayerAdviseSink->m_pAdviseSink->OnPause(
						pPendingAdvise->m_ulArg1);
		break;
	    case ONBEGIN:
        	pPlayerAdviseSink->m_pAdviseSink->OnBegin(
						pPendingAdvise->m_ulArg1);
		break;
	    case ONBUFFERING:
    		pPlayerAdviseSink->m_pAdviseSink->OnBuffering(
						pPendingAdvise->m_ulArg1, 
				       (UINT16)	pPendingAdvise->m_ulArg2);
		break;
	    case ONCONTACTING:
    		pPlayerAdviseSink->m_pAdviseSink->OnContacting(
				    		pPendingAdvise->m_pHostName);
		break;
	    default:
		HX_ASSERT(FALSE);
		break;
	}

	delete pPendingAdvise;
    }
}

/*
 *	IHXClientAdviseSink methods
 */

/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnPosLength
 *	Purpose:
 *	    Called to advise the client that the position or length of the
 *	    current playback context has changed.
 */
STDMETHODIMP
CHXAdviseSinkControl::OnPosLength(UINT32	  ulPosition,
				   UINT32	  ulLength)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnPosLength(ulPosition, 
							  ulLength);
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONPOSLENGTH, 
			     ulPosition, ulLength);
	}
    }

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnPresentationOpened
 *	Purpose:
 *	    Called to advise the client a presentation has been opened.
 */
STDMETHODIMP CHXAdviseSinkControl::OnPresentationOpened()
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnPresentationOpened();
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONPRESENTATIONOPENED);
	}
    }

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnPresentationClosed
 *	Purpose:
 *	    Called to advise the client a presentation has been closed.
 */
STDMETHODIMP CHXAdviseSinkControl::OnPresentationClosed()
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnPresentationClosed();
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONPRESENTATIONCLOSED);
	}
    }

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnStatisticsChanged
 *	Purpose:
 *	    Called to advise the client that the presentation statistics
 *	    have changed. 
 */
STDMETHODIMP CHXAdviseSinkControl::OnStatisticsChanged(void)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnStatisticsChanged();
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONSTATISTICSCHANGED);
	}
    }

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnPreSeek
 *	Purpose:
 *	    Called by client engine to inform the client that a seek is
 *	    about to occur. The render is informed the last time for the 
 *	    stream's time line before the seek, as well as the first new
 *	    time for the stream's time line after the seek will be completed.
 *
 */
STDMETHODIMP CHXAdviseSinkControl::OnPreSeek(	ULONG32	ulOldTime,
						ULONG32	ulNewTime)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnPreSeek(ulOldTime, 
							ulNewTime);
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONPRESEEK, 
			     ulOldTime, ulNewTime);
	}
    }

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnPostSeek
 *	Purpose:
 *	    Called by client engine to inform the client that a seek has
 *	    just occured. The render is informed the last time for the 
 *	    stream's time line before the seek, as well as the first new
 *	    time for the stream's time line after the seek.
 *
 */
STDMETHODIMP CHXAdviseSinkControl::OnPostSeek(	ULONG32	ulOldTime,
						ULONG32	ulNewTime)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnPostSeek(ulOldTime, 
							 ulNewTime);
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONPOSTSEEK, 
			     ulOldTime, ulNewTime);
	}
    }

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnStop
 *	Purpose:
 *	    Called by client engine to inform the client that a stop has
 *	    just occured. 
 *
 */
STDMETHODIMP CHXAdviseSinkControl::OnStop(void)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnStop();
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONSTOP);
	}
    }

    return HXR_OK;
}

/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnPause
 *	Purpose:
 *	    Called by client engine to inform the client that a pause has
 *	    just occured. The render is informed the last time for the 
 *	    stream's time line before the pause.
 *
 */
STDMETHODIMP CHXAdviseSinkControl::OnPause(ULONG32 ulTime)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnPause(ulTime);
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONPAUSE, ulTime);
	}
    }

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnBegin
 *	Purpose:
 *	    Called by client engine to inform the client that a begin or
 *	    resume has just occured. The render is informed the first time 
 *	    for the stream's time line after the resume.
 *
 */
STDMETHODIMP CHXAdviseSinkControl::OnBegin(ULONG32 ulTime)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnBegin(ulTime);
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONBEGIN, ulTime);
	}
    }

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnBuffering
 *	Purpose:
 *	    Called by client engine to inform the client that buffering
 *	    of data is occuring. The render is informed of the reason for
 *	    the buffering (start-up of stream, seek has occured, network
 *	    congestion, etc.), as well as percentage complete of the 
 *	    buffering process.
 *
 */
STDMETHODIMP CHXAdviseSinkControl::OnBuffering(ULONG32	ulFlags,
						UINT16	unPercentComplete)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnBuffering(ulFlags, 
							  unPercentComplete);
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONBUFFERING, 
			     ulFlags, (UINT32) unPercentComplete);
	}
    }

    return HXR_OK;
}


/************************************************************************
 *	Method:
 *	    IHXClientAdviseSink::OnContacting
 *	Purpose:
 *	    Called by client engine to inform the client is contacting
 *	    hosts(s).
 *
 */
STDMETHODIMP CHXAdviseSinkControl::OnContacting(const char* pHostName)
{
    CHXSimpleList::Iterator lSinkerator = m_pSinkList.Begin();
    for (;  lSinkerator != m_pSinkList.End(); ++lSinkerator)
    {
	PlayerAdviseSink* pPlayerAdviseSink = 
		    (PlayerAdviseSink*) (*lSinkerator);
	if (!m_pInterruptState->AtInterruptTime() || 
	    pPlayerAdviseSink->m_bInterruptSafe)
	{
	    ProcessPendingRequests(pPlayerAdviseSink);
    	    pPlayerAdviseSink->m_pAdviseSink->OnContacting(pHostName);
	}
	else
	{
	    AddToPendingList(pPlayerAdviseSink, ONCONTACTING, 
			     0, 0, (char*) pHostName);
	}
    }

    return HXR_OK;
}


void CHXAdviseSinkControl::AdviseSinkCallback(void* pParam)
{
    CHXAdviseSinkControl* pObj = (CHXAdviseSinkControl*)pParam;
    
    if (pObj)
    {
        pObj->ProcessAllRequests();
    }
}    


/************************************************************************

/* CHXErrorSinkControl */

CHXErrorSinkControl::CHXErrorSinkControl() :
     m_lRefCount (0)
    ,m_pPendingErrorList(NULL) 
    ,m_pInterruptState(NULL)
    ,m_pScheduler(NULL)
    ,m_pErrorCallback(NULL)
{
    m_pErrorCallback = new CHXGenericCallback((void*)this, (fGenericCBFunc)ErrorCallback);
    m_pErrorCallback->AddRef();
}

CHXErrorSinkControl::~CHXErrorSinkControl()
{
    Close();
}

STDMETHODIMP
CHXErrorSinkControl::AddErrorSink	(
				IHXErrorSink*	pErrorSink,
                                const UINT8     unLowSeverity,
                                const UINT8     unHighSeverity)
{
    if (pErrorSink)
    {
    	PlayerErrorSink* pPlayerErrorSink = new PlayerErrorSink(pErrorSink, unLowSeverity, unHighSeverity);
    	m_pSinkList.AddTail(pPlayerErrorSink);
    	pErrorSink->AddRef();
    	
    }
    return HXR_OK;
}

STDMETHODIMP
CHXErrorSinkControl::RemoveErrorSink(IHXErrorSink* pErrorSink)
{
    PlayerErrorSink* pPlayerErrorSink;
    CHXSimpleList::Iterator lIterator	= m_pSinkList.Begin();
    for (; lIterator != m_pSinkList.End(); ++lIterator)
    {
	pPlayerErrorSink = (PlayerErrorSink *) (*lIterator);
    	if (pPlayerErrorSink->m_pErrorSink == pErrorSink)
    	{
	    HX_RELEASE (pErrorSink);
	    
	    LISTPOSITION pos = m_pSinkList.Find(pPlayerErrorSink);
	    m_pSinkList.RemoveAt(pos);	    
	    delete pPlayerErrorSink;
	    return HXR_OK;
    	}
    }
    return HXR_FAIL;
}

STDMETHODIMP 
CHXErrorSinkControl::QueryInterface(REFIID riid, void** ppvObj)
{
    QInterfaceList qiList[] =
        {
            { GET_IIDHANDLE(IID_IHXErrorSinkControl), (IHXErrorSinkControl*)this },
            { GET_IIDHANDLE(IID_IUnknown), (IUnknown*)(IHXErrorSinkControl*)this },
        };
    
    return ::QIFind(qiList, QILISTSIZE(qiList), riid, ppvObj);
}

STDMETHODIMP_ (ULONG32) 
CHXErrorSinkControl::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}

STDMETHODIMP_ (ULONG32) 
CHXErrorSinkControl::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
	return m_lRefCount;
    }

    delete this;
    return 0;
}

void 
CHXErrorSinkControl::GetSeverityRange(IHXErrorSink* pErrorSink, 
							UINT8& unLowSeverity, 
    							UINT8& unHighSeverity)
{
    PlayerErrorSink* pPlayerErrorSink;
    CHXSimpleList::Iterator lIterator	= m_pSinkList.Begin();
    for (; lIterator != m_pSinkList.End(); ++lIterator)
    {
	pPlayerErrorSink = (PlayerErrorSink *) (*lIterator);
    	if (pPlayerErrorSink->m_pErrorSink == pErrorSink)
    	{
	    unLowSeverity = pPlayerErrorSink->m_unLowSeverity;
	    unHighSeverity = pPlayerErrorSink->m_unHighSeverity;
	    break;
    	}
    }
}

void
CHXErrorSinkControl::Init(IHXClientEngine* pEngine)
{
    pEngine->QueryInterface(IID_IHXInterruptState, 
				(void**) &m_pInterruptState);
    
    pEngine->QueryInterface(IID_IHXScheduler, (void**) &m_pScheduler);
}

void 
CHXErrorSinkControl::Close()
{
    PlayerErrorSink* pPlayerErrorSink;
    CHXSimpleList::Iterator lIterator	= m_pSinkList.Begin();
    for (; lIterator != m_pSinkList.End(); ++lIterator)
    {
	pPlayerErrorSink = (PlayerErrorSink *) (*lIterator);
	HX_RELEASE (pPlayerErrorSink->m_pErrorSink);
	delete pPlayerErrorSink;
    }

    m_pSinkList.RemoveAll();

    while (m_pPendingErrorList && m_pPendingErrorList->GetCount() > 0)
    {
	ErrorReport* pErrorReport = 
	    (ErrorReport*) m_pPendingErrorList->RemoveHead();
	delete pErrorReport;
    }

    
    if (m_pErrorCallback)
    {
        m_pScheduler->Remove(m_pErrorCallback->GetPendingCallback());
        m_pErrorCallback->CallbackCanceled();
        HX_RELEASE(m_pErrorCallback);
    }

    HX_DELETE(m_pPendingErrorList);
    HX_RELEASE(m_pInterruptState);
    HX_RELEASE(m_pScheduler);
}

HX_RESULT 
CHXErrorSinkControl::ErrorOccurred( const UINT8	unSeverity,  
				    const ULONG32	ulHXCode,
				    const ULONG32	ulUserCode,
				    const char*	pUserString,
				    const char*	pMoreInfoURL)
{
    if (m_pInterruptState->AtInterruptTime())
    {
	if (!m_pPendingErrorList)
	{
	    m_pPendingErrorList = new CHXSimpleList;
	}

	ErrorReport* pErrorReport = new ErrorReport;
	pErrorReport->m_unSeverity	= unSeverity;
	pErrorReport->m_ulHXCode	= ulHXCode;
	pErrorReport->m_ulUserCode	= ulUserCode;

	if (pUserString && *pUserString)
	{
	    pErrorReport->m_pUserString = new char[strlen(pUserString) + 1];
	    ::strcpy(pErrorReport->m_pUserString, pUserString); /* Flawfinder: ignore */
	}

	if (pMoreInfoURL && *pMoreInfoURL)
	{
	    pErrorReport->m_pMoreInfoURL = new char[strlen(pMoreInfoURL) + 1];
	    ::strcpy(pErrorReport->m_pMoreInfoURL, pMoreInfoURL); /* Flawfinder: ignore */
	}

	m_pPendingErrorList->AddTail((void*) pErrorReport);

        if (!m_pErrorCallback->GetPendingCallback())
	{
            m_pErrorCallback->CallbackScheduled(
             m_pScheduler->RelativeEnter(m_pErrorCallback, 0));
	}

	return HXR_OK;
    }

    ReportPendingErrors();

    CallReport(unSeverity,
	       ulHXCode,
	       ulUserCode,
	       pUserString,
	       pMoreInfoURL);

    return HXR_OK;
}

void
CHXErrorSinkControl::ReportPendingErrors()
{
    if (m_pErrorCallback)
    {
        m_pScheduler->Remove(m_pErrorCallback->GetPendingCallback());
        m_pErrorCallback->CallbackCanceled();
    }

    while (m_pPendingErrorList && m_pPendingErrorList->GetCount() > 0)
    {
	ErrorReport* pErrorReport = 
	    (ErrorReport*) m_pPendingErrorList->RemoveHead();
	CallReport(pErrorReport->m_unSeverity,
		   pErrorReport->m_ulHXCode,
		   pErrorReport->m_ulUserCode,
		   pErrorReport->m_pUserString,
		   pErrorReport->m_pMoreInfoURL);
	delete pErrorReport;
    }
}

HX_RESULT	
CHXErrorSinkControl::CallReport
(
    const UINT8	unSeverity,  
    HX_RESULT	ulHXCode,
    const ULONG32	ulUserCode,
    const char*	pUserString,
    const char*	pMoreInfoURL
)
{
    CHXSimpleList::Iterator ndxSink;
    for (ndxSink = m_pSinkList.Begin(); 
	 ndxSink != m_pSinkList.End(); ++ndxSink)
    {
	PlayerErrorSink* pPlayerErrorSink =
			(PlayerErrorSink*) (*ndxSink);

	// error-reporting based on its severity	  
	if (unSeverity >= pPlayerErrorSink->m_unLowSeverity &&
	    unSeverity <= pPlayerErrorSink->m_unHighSeverity)
//	    unSeverity <= pPlayerErrorSink->m_unHighSeverity && !m_pAltURLs->GetCount())
	{
	    pPlayerErrorSink->m_pErrorSink->ErrorOccurred(unSeverity, ulHXCode,
						ulUserCode, pUserString, pMoreInfoURL);
	}
    }

    return HXR_OK;
}


void CHXErrorSinkControl::ErrorCallback(void* pParam)
{
    CHXErrorSinkControl* pObj = (CHXErrorSinkControl*)pParam;

    if (pObj)
    {
        pObj->ReportPendingErrors();
    }
}
