/****************************************************************************
 * 
 *  $Id: exadvsnk.cpp,v 1.1.1.1 1999/02/23 23:48:01 jordanb Exp $
 *
 *  Copyright (C) 1995,1996,1997 RealNetworks, Inc.
 *  All rights reserved.
 *
 *  http://www.real.com/devzone
 *
 *  This program contains proprietary information of RealNetworks, Inc.,
 *  and is licensed subject to restrictions on use and distribution.
 * 
 *  exadvsnk.cpp
 *
 *  Sample Implementation of Client Advise Sink Interfaces
 *
 */


/****************************************************************************
 * Includes
 */
#include "pntypes.h"
#include "pncom.h"
#include "rmacomm.h"
#include "rmamon.h"
#include "rmacore.h"

//#include "../x/scolplugin.h"
#include "Real.h"

#include "os.h"
#include "rmaclsnk.h"
#include "exadvsnk.h"

/****************************************************************************
 * Globals
 */
extern BOOL bShowMeTheStatistics;


/****************************************************************************
 *  ExampleClientAdviceSink::ExampleClientAdviceSink         ref:  exadvsnk.h
 *
 *  Constructor
 */
ExampleClientAdviceSink::ExampleClientAdviceSink(IUnknown* pUnknown,myrealbuf p)
    : m_lRefCount (0)
    , m_pUnknown (NULL)
    , m_pRegistry (NULL)
	, m_myrealbuf (p)

{
    if (pUnknown)
    {
	m_pUnknown = pUnknown;
	m_pUnknown->AddRef();

	if (PNR_OK != m_pUnknown->QueryInterface(IID_IRMAPNRegistry, (void**)&m_pRegistry))
	{
	    m_pRegistry = NULL;
	}

	IRMAPlayer* pPlayer;
	if(PNR_OK == m_pUnknown->QueryInterface(IID_IRMAPlayer,
						(void**)&pPlayer))
	{
	    pPlayer->AddAdviseSink(this);
	    pPlayer->Release();
	}
    }
}


/****************************************************************************
 *  ExampleClientAdviceSink::~ExampleClientAdviceSink        ref:  exadvsnk.h
 *
 *  Destructor
 */
ExampleClientAdviceSink::~ExampleClientAdviceSink(void)
{
    if (m_pRegistry)
    {
	m_pRegistry->Release();
	m_pRegistry = NULL;
    }

    if (m_pUnknown)
    {
	m_pUnknown->Release();
	m_pUnknown = NULL;
    }
}


/****************************************************************************
 *  ExampleClientAdviceSink::ShowMeTheStatistics             ref:  exadvsnk.h
 *
 */
void ExampleClientAdviceSink::ShowMeTheStatistics (char* pszRegistryKey)
{
}


// IRMAClientAdviseSink Interface Methods

/****************************************************************************
 *  IRMAClientAdviseSink::OnPosLength                        ref:  rmaclsnk.h
 *
 *  Called to advise the client that the position or length of the
 *  current playback context has changed.
 */
STDMETHODIMP
ExampleClientAdviceSink::OnPosLength(UINT32	  ulPosition,
				   UINT32	  ulLength)
{
	int k;
//    MMechostr(MSKDEBUG, "OnPosLength(%ld, %ld)\n", ulPosition, ulLength);  

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALposition)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,ulPosition<<1)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,ulLength<<1)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,2<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,2<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnPresentationOpened               ref:  rmaclsnk.h
 *
 *  Called to advise the client a presentation has been opened.
 */
STDMETHODIMP
ExampleClientAdviceSink::OnPresentationOpened()
{
	int k;
//    MMechostr(MSKDEBUG, "OnPresentationOpened()\n");

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALonOpened)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,1<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnPresentationClosed               ref:  rmaclsnk.h
 *
 *  Called to advise the client a presentation has been closed.
 */
STDMETHODIMP
ExampleClientAdviceSink::OnPresentationClosed()
{
	int k;
//    MMechostr(MSKDEBUG, "OnPresentationClosed()\n");

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALonClosed)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,1<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnStatisticsChanged                ref:  rmaclsnk.h
 *
 *  Called to advise the client that the presentation statistics
 *  have changed. 
 */
STDMETHODIMP
ExampleClientAdviceSink::OnStatisticsChanged(void)
{
//    MMechostr(MSKDEBUG, "OnStatisticsChanged():\n");

    UINT32  unPlayerIndex = 0;
    UINT32  unSourceIndex = 0;
    UINT32  unStreamIndex = 0;

    INT32   nRegistryValue = 0;

    char*   pszRegistryPrefix = "Statistics";
    char    szRegistryName[MAX_DISPLAY_NAME] = {0};
   
    // display the content of whole statistic registry
    if (m_pRegistry)
    {
	// ok, let's start from the top (player)
	sprintf(szRegistryName, "%s.Player%ld", pszRegistryPrefix, unPlayerIndex);
	while (PT_COMPOSITE == m_pRegistry->GetTypeByName(szRegistryName))
	{
	    // display player statistic
	    ShowMeTheStatistics(szRegistryName);

	    sprintf(szRegistryName, "%s.Source%ld", szRegistryName, unSourceIndex);
	    while (PT_COMPOSITE == m_pRegistry->GetTypeByName(szRegistryName))
	    {
		// display source statistic
		ShowMeTheStatistics(szRegistryName);

		sprintf(szRegistryName, "%s.Stream%ld", szRegistryName, unStreamIndex);
		while (PT_COMPOSITE == m_pRegistry->GetTypeByName(szRegistryName))
		{
		    // display stream statistic
		    ShowMeTheStatistics(szRegistryName);

		    unStreamIndex++;

		    sprintf(szRegistryName, "%s.Player%ld.Source%ld.Stream%ld", 
			    pszRegistryPrefix, unPlayerIndex, unSourceIndex, unStreamIndex);
		}

		unSourceIndex++;

		sprintf(szRegistryName, "%s.Player%ld.Source%ld",
			pszRegistryPrefix, unPlayerIndex, unSourceIndex);
	    }

	    unPlayerIndex++;

	    sprintf(szRegistryName, "%s.Player%ld", pszRegistryPrefix, unPlayerIndex);
	}
    }

    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnPreSeek                          ref:  rmaclsnk.h
 *
 *  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
ExampleClientAdviceSink::OnPreSeek(	UINT32	ulOldTime,
						UINT32	ulNewTime)
{
//    MMechostr(MSKDEBUG, "OnPreSeek(%ld, %ld)\n", ulOldTime, ulNewTime);
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnPostSeek                         ref:  rmaclsnk.h
 *
 *  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
ExampleClientAdviceSink::OnPostSeek(	UINT32	ulOldTime,
						UINT32	ulNewTime)
{
//    MMechostr(MSKDEBUG, "OnPostSeek(%ld, %ld)\n", ulOldTime, ulNewTime);
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnStop                             ref:  rmaclsnk.h
 *
 *  Called by client engine to inform the client that a stop has
 *  just occured. 
 */
STDMETHODIMP
ExampleClientAdviceSink::OnStop(void)
{
	int k;
//    MMechostr(MSKDEBUG, "OnStop()\n");

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALonStop)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,1<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnPause                            ref:  rmaclsnk.h
 *
 *  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
ExampleClientAdviceSink::OnPause(UINT32 ulTime)
{
	int k;
//    MMechostr(MSKDEBUG, "OnPause(%ld)\n", ulTime);

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALonPause)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,1<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnBegin                            ref:  rmaclsnk.h
 *
 *  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
ExampleClientAdviceSink::OnBegin(UINT32 ulTime)
{
	int k;
//    MMechostr(MSKDEBUG, "OnBegin(%ld)\n", ulTime);

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALonBegin)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,1<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnBuffering                        ref:  rmaclsnk.h
 *
 *  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
ExampleClientAdviceSink::OnBuffering
(
    UINT32 ulFlags,
    UINT16 unPercentComplete
)
{
	int k;
//    MMechostr(MSKDEBUG, "OnBuffering(%ld, %d)\n", ulFlags, unPercentComplete);

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALbuffering)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,unPercentComplete<<1)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,2<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


/****************************************************************************
 *  IRMAClientAdviseSink::OnContacting                       ref:  rmaclsnk.h
 *
 *  Called by client engine to inform the client is contacting hosts(s).
 */
STDMETHODIMP
ExampleClientAdviceSink::OnContacting(const char* pHostName)
{
	int k;
//    MMechostr(MSKDEBUG, "OnContacting(\"%s\")\n", pHostName);

	k=OBJbeginreflex(m_myrealbuf->m,typeReal,m_myrealbuf->id,RFLREAL_STATE);
	if (k==0)
	{
		if (MMpush(m_myrealbuf->m,REALcontacting)) return PNR_OK;
		if (Mpushstrbloc(m_myrealbuf->m,(char*)pHostName)) return PNR_OK;
		if (MMpush(m_myrealbuf->m,2<<1)) return PNR_OK;
		if (k=MBdeftab(m_myrealbuf->m)) return PNR_OK;
		OBJcallreflex(m_myrealbuf->m,1);
	}
    return PNR_OK;
}


// IUnknown COM Interface Methods

/****************************************************************************
 *  IUnknown::AddRef                                            ref:  pncom.h
 *
 *  This routine increases the object reference count in a thread safe
 *  manner. The reference count is used to manage the lifetime of an object.
 *  This method must be explicitly called by the user whenever a new
 *  reference to an object is used.
 */
STDMETHODIMP_(UINT32) ExampleClientAdviceSink::AddRef()
{
    return InterlockedIncrement(&m_lRefCount);
}


/****************************************************************************
 *  IUnknown::Release                                           ref:  pncom.h
 *
 *  This routine decreases the object reference count in a thread safe
 *  manner, and deletes the object if no more references to it exist. It must
 *  be called explicitly by the user whenever an object is no longer needed.
 */
STDMETHODIMP_(UINT32) ExampleClientAdviceSink::Release()
{
    if (InterlockedDecrement(&m_lRefCount) > 0)
    {
        return m_lRefCount;
    }

    delete this;
    return 0;
}


/****************************************************************************
 *  IUnknown::QueryInterface                                    ref:  pncom.h
 *
 *  This routine indicates which interfaces this object supports. If a given
 *  interface is supported, the object's reference count is incremented, and
 *  a reference to that interface is returned. Otherwise a NULL object and
 *  error code are returned. This method is called by other objects to
 *  discover the functionality of this object.
 */
STDMETHODIMP ExampleClientAdviceSink::QueryInterface(REFIID riid, void** ppvObj)
{
    if (IsEqualIID(riid, IID_IUnknown))
    {
	AddRef();
	*ppvObj = (IUnknown*)(IRMAClientAdviseSink*)this;
	return PNR_OK;
    }
    else if (IsEqualIID(riid, IID_IRMAClientAdviseSink))
    {
	AddRef();
	*ppvObj = (IRMAClientAdviseSink*)this;
	return PNR_OK;
    }

    *ppvObj = NULL;
    return PNR_NOINTERFACE;
}
