/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: smlelem.cpp,v 1.5.12.1 2004/07/09 01:58:01 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 ***** */

// system
#include <time.h>
// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "ihxpckts.h"
#include "hxxml.h"
#include "smiltype.h"
// pncont
#include "hxstring.h"
#include "hxslist.h"
#include "hxstack.h"
#include "chxpckts.h"
// pnmisc
#include "nptime.h"
#include "smpte.h"
#include "hxwinver.h"
#include "hxstrutl.h"  /* for new_string(). */
// rnxmllib
#include "hxxmlprs.h"
// rmasmil
#include "smlparse.h"
#include "smltime.h"
#include "smlprstime.h"
#include "animattr.h"
#include "smlelem.h"
// pndebug
#include "hxheap.h"
#ifdef _DEBUG
#undef HX_THIS_FILE		
static const char HX_THIS_FILE[] = __FILE__;
#endif
 
/*
 * CSmilElement methods
 */

CSmilElement::CSmilElement(SMILNode* pNode):
    m_pNode(pNode),
    m_lBeginOffset(0),
    m_bBeginOffsetSet(FALSE),
    m_bNegBeginOffsetAlreadyUsed(FALSE),
    m_bCurBeginIsOffsetFromSyncBase(FALSE),
    m_ulBeginOffsetFromSyncBase((UINT32)-1),
    m_ulClipBegin((UINT32)-1),
    m_ulAuthoredClipBegin((UINT32)-1),
    m_ulClipEnd((UINT32)-1),
    m_ulDelay((UINT32)-1),
    m_ulDuration((UINT32)-1),
    m_ulOriginalDuration((UINT32)-1),
    m_ulDurationInAddTrack((UINT32)-1),
    m_bCurEndClippedByParent(FALSE),
    m_ulMaxDuration((UINT32)-1),
    m_ulMaxActiveDur((UINT32)-1),
    m_ulMinActiveDur(0),
    m_ulAuthoredDur((UINT32)-1),
    m_bHasExplicitDur(FALSE),
    m_bHasExplicitEnd(FALSE),
    m_bDurationIncludesDelayBeyondSyncbase(FALSE),
    m_bAddDurationAlreadyDone(FALSE),
    m_bUseMediaDurForMinDur(FALSE),
    m_bUseMediaDurForMaxDur(FALSE),
    m_lEndOffset(0),
    m_bEndOffsetSet(FALSE),
    m_ulAnticipatedPauseDur(0),
    m_ulEndSync((UINT32)-1),
    m_fRepeatValue(1.0),
    m_ulTimestamp(0),
    m_bInsertedIntoTimeline(FALSE),
    m_bHasBeenScheduled(FALSE),
    m_bIsPausedInExcl(FALSE),
    m_bIsDeferredInExcl(FALSE),
    m_ulTimeDeferralOccurred((UINT32)-1),
    m_bIsStoppedInExcl(FALSE),
    m_ulStopTimeInExcl((UINT32)-1),
    m_bCurrentSourceIsLive(FALSE),
    m_bRendererInitialized(FALSE),
    m_bIsRestarting(FALSE),
    m_bAwaitingSyncAncestorBeginNotification(FALSE),
    m_ulLongSyncArcBeginInGroupTime((UINT32)-1),
    m_bIndefiniteDuration(FALSE),
    m_bIndefiniteEnd(FALSE),
    m_bIndefiniteBegin(FALSE),
    m_restartBehavior(SmilRestartAlways),
    m_restartDefaultBehavior(SmilRestartInherit),
    // /Defaults to "default" which means use val of syncToleranceDefault:
    m_ulSyncTolerance((UINT32)-1),
    // /Defaults to "inherit"; if there is no parent timed element, then
    // it is implementation-dependent (but should be less than or = 2s):
    m_ulSyncToleranceDefault((UINT32)-1),
    m_syncBehavior(SmilSyncBehaviorDefault),
    m_syncBehaviorDefault(SmilSyncBehaviorInherit),
    m_ulReadIndex(0),
    m_nBeginEventSourceTag(SMILEventSourceNone),
    m_nEndEventSourceTag(SMILEventSourceNone),
    m_nEndsyncEventSourceTag(SMILEventSourceNone),
    m_lBeginEventClockValue(0),
    m_lEndEventClockValue(0),
    m_pHandler(0),
    m_pTimelineElement(NULL),
    m_pBeginTimeList(NULL),
    m_pEndTimeList(NULL),
    m_bWallClockBegin(FALSE),
    m_bWallClockEnd(FALSE),
    m_bHasAtLeastOneEventBasedBegin(FALSE),
    m_bHasAtLeastOneNonEventBasedBegin(FALSE),
    m_eFill(FillDefault),
    m_eFillDefault(FillDefaultInherit),
    m_eActualFill(FillDefault),
    m_eErase(EraseWhenDone),
    m_ulRemoveTime(((UINT32) -1)),
    m_bUsesExternalMediaMarkerFile(FALSE),
    m_bClipBeginUsesMarker(FALSE),
    m_bClipBeginMarkerResolved(FALSE),
    m_bWaitingOnClipBeginToResolve(FALSE),
    m_pszClipBeginMarkerName(NULL),
    m_pszClipBeginExternalMarkerFileName(NULL),
    m_bClipEndUsesMarker(FALSE),
    m_bClipEndMarkerResolved(FALSE),
    m_bWaitingOnClipEndToResolve(FALSE),
    m_pszClipEndMarkerName(NULL),
    m_pszClipEndExternalMarkerFileName(NULL),
    m_eAccessErrorBehavior(AccessErrorBehaviorInherit)
{
    // /XXXEH- we may need to set m_bIndefiniteBegin to TRUE if
    // hasAncestor(SMILExcl, this), but setInitialDelay() fix seems to be
    // all we need (...<==famous last words).
    m_pHyperlinks = new CHXSimpleList;
    getParentRestartDefault();
}

CSmilElement::~CSmilElement()
{
    if (m_pBeginTimeList)
    {
	CHXSimpleList::Iterator ndx = m_pBeginTimeList->Begin();
	for (; ndx != m_pBeginTimeList->End(); ++ndx)
	{
	    SmilTimeValue* pTV = (SmilTimeValue*)(*ndx);
	    HX_DELETE(pTV);
	}
	HX_DELETE(m_pBeginTimeList);
    }
    if (m_pEndTimeList)
    {
	CHXSimpleList::Iterator ndx = m_pEndTimeList->Begin();
	for (; ndx != m_pEndTimeList->End(); ++ndx)
	{
	    SmilTimeValue* pTV = (SmilTimeValue*)(*ndx);
	    HX_DELETE(pTV);
	}
	HX_DELETE(m_pEndTimeList);
    }


    HX_DELETE(m_pHyperlinks);
    HX_DELETE(m_pTimelineElement);
    HX_VECTOR_DELETE(m_pszClipBeginMarkerName);
    HX_VECTOR_DELETE(m_pszClipBeginExternalMarkerFileName);
    HX_VECTOR_DELETE(m_pszClipEndMarkerName);
    HX_VECTOR_DELETE(m_pszClipEndExternalMarkerFileName);
}

void
CSmilElement::addDuration(UINT32 ulDuration)
{
    // default add duration just attempts to add it to it's parent
    m_ulDuration = ulDuration;
    if(m_pNode->m_pParent && m_pNode->m_pParent->m_pElement)
    {
	m_pNode->m_pParent->m_pElement->addDuration(m_ulDuration);
	m_bAddDurationAlreadyDone = TRUE;
    }
}

void
CSmilElement::addElement(CSmilElement* pElement,
    UINT32& ulBeginOffset, UINT32& ulDuration)
{
}


void
CSmilElement::prepForRestart(BOOL bParentIsRestarting, LONG32 lWhen)
{
    m_bInsertedIntoTimeline = FALSE;
    m_bRendererInitialized = FALSE;
    m_bBeginOffsetSet = FALSE;
    // /Reset these, too, or hiding of site will happen too early next time:
    m_bCurBeginIsOffsetFromSyncBase = TRUE;
    // /This seems to work fine in case where begin="x; y" where x is a
    // clock value and y is an event-arc that resolves at/to a time prior
    // to x.  (The other option is to set m_bCurBeginIsOffsetFromSyncBase
    // to FALSE when !m_bBeginOffsetSet):
    m_ulBeginOffsetFromSyncBase = 0;

    m_bNegBeginOffsetAlreadyUsed = FALSE;

    m_ulClipBegin = m_ulAuthoredClipBegin;

    m_bAddDurationAlreadyDone = FALSE;


    // /XXXEH- TODO: test this!:
    m_bDurationIncludesDelayBeyondSyncbase = FALSE;

    m_bIsPausedInExcl = FALSE;
    m_bIsDeferredInExcl = FALSE;
    m_ulTimeDeferralOccurred = (UINT32)-1;
    m_bIsStoppedInExcl = FALSE;
    m_ulAnticipatedPauseDur = 0;

    // /If we restart a source, the next play may be a different file, even
    // live one time and on-demand the next, so reset this and let next call
    // to TrackDurationSet() tell us if it's live this time:
    m_bCurrentSourceIsLive = FALSE;

    // /Adding this helps fix a lot of restart problems, including part of
    // PR 62408 and par version of PR 50660.  The playToAssoc was never
    // removed when the track ended the first time, so the doc renderer
    // (m_pHandler) used to see this track as already added and would
    // erroneously add this restart as a repeat track:
    m_bIsRestarting = TRUE;

    // /Helps fix restart problems related to PR 55253:
    if (bParentIsRestarting)
    {
	LONG32 lNextResolvedEndTime = 0;
	BOOL bHasFutureResolvedEnd = FALSE;
	if (m_bHasExplicitEnd)
	{
	    SmilTimeValue* pNextResolvedEndTimeValue = NULL;
	    HX_RESULT rettimeval = HXR_OK;
	    // /lWhen must be in syncBase time space, not group time space:
	    rettimeval = getNextResolvedTimeValue(pNextResolvedEndTimeValue,
		    SMILTIME_NEGATIVE_INFINITY, lWhen, SmilEndTimeList, NULL);

	    if (SUCCEEDED(rettimeval)  &&  NULL != pNextResolvedEndTimeValue)
	    {
		HX_RESULT rtval = pNextResolvedEndTimeValue->
			getEffectiveResolvedTime(lNextResolvedEndTime);
		if (SUCCEEDED(rtval))
		{
		    bHasFutureResolvedEnd = TRUE;
		    if (lWhen < lNextResolvedEndTime)
		    {
			m_ulDuration = (UINT32)(lNextResolvedEndTime - lWhen);
		    }
		}
	    }
	}
	if (m_bHasExplicitDur)
	{
	    if (!bHasFutureResolvedEnd)
	    {
		m_ulDuration = m_ulAuthoredDur;
	    }
	    else if (lNextResolvedEndTime - lWhen > (LONG32)m_ulAuthoredDur)
	    {
		m_ulDuration = m_ulAuthoredDur;
	    }
	}
	else
	{
	    // /Let SetDelay() and insertTimeline... deal with new duration:
	    m_ulDuration = (UINT32)-1;
	}
    }

    // /Reset the delay since it will be new, obviously, for this restart;
    // helps fix restart problems related to PR 56233:
    m_ulDelay = (UINT32)-1;

    // /Note: we don't want to or need to reset the delay here.  A restart
    // based on either a syncArc or eventArc will take care of that.

    if (m_pTimelineElement)
    {
	m_pTimelineElement->prepForRestart();
    }

    // /Now, call this function for all descendants as well in case this is
    // a time container (or a media object with timeline-element children);
    // (Helps fix restart problems related to PR 55253)
    SMILNode* pChild = m_pNode->getFirstChild();
    while (pChild)
    {
	CSmilElement* pElement = pChild->m_pElement;
	if (pElement  &&  pElement->m_pHandler  &&
		pElement->m_bInsertedIntoTimeline)
	{
	    pElement->prepForRestart(TRUE, lWhen);
	}
	pChild = m_pNode->getNextChild();
    }
}


void
CSmilElement::prepForPauseInExcl(LONG32 lCurTime)
{
    m_bIsPausedInExcl = TRUE;

    // /If we're being set to paused state, then we need to adjust our hide
    // event (removeTime) in concert with adjusting when we will end:
    BOOL bHasFutureResolvedExplicitEnd = FALSE;

    if (m_bHasExplicitEnd)
    {
	SmilTimeValue* pNextResolvedEndTimeValue = NULL;
	HX_RESULT rettimeval = HXR_OK;
	// /lCurTime must be in syncBase time space, not group time space:
	rettimeval = getNextResolvedTimeValue(pNextResolvedEndTimeValue,
		SMILTIME_NEGATIVE_INFINITY, lCurTime, SmilEndTimeList, NULL);

	if (SUCCEEDED(rettimeval)  &&  NULL != pNextResolvedEndTimeValue)
	{
	    LONG32 lNextResolvedEndTime = SMILTIME_NEGATIVE_INFINITY;
	    HX_RESULT rtval = pNextResolvedEndTimeValue->
		    getEffectiveResolvedTime(lNextResolvedEndTime);
	    if (SUCCEEDED(rtval))
	    {
		HX_ASSERT(lCurTime <= lNextResolvedEndTime);
		bHasFutureResolvedExplicitEnd = TRUE;
		updateRemoveTime(lNextResolvedEndTime);
	    }
	}
    }

    // /If we don't have explicit end, then we'll end when we get resumed
    // plus the remainder of our duration:
    if (!bHasFutureResolvedExplicitEnd)
    {
	updateRemoveTime(SMILTIME_PAUSED_INDEFINITELY);
    }
}

void
CSmilElement::prepForResumeInExcl()
{
    HX_ASSERT(m_bIsPausedInExcl);

    LISTPOSITION lPos = NULL;
    HX_ASSERT(m_pBeginTimeList);
    if (m_pBeginTimeList)
    {
	lPos = m_pBeginTimeList->GetHeadPosition();
    }
    while (lPos)
    {
	SmilTimeValue* pTimeVal =
		(SmilTimeValue*)m_pBeginTimeList->GetAt(lPos);

	// /There should be only one resumeEvent:
	if (pTimeVal  &&  pTimeVal->isResumeEvent())
	{
	    // /Fixes PR 62408: remove resumeEvent from element's begin
	    // list since it was an internally-generated single-use begin
	    // value and we don't want it to re-resolve when (if) the
	    // original pausing element restarts and then raises another
	    // resumeEvent when it ends again:
	    m_pBeginTimeList->RemoveAt(lPos);
	    break;
	}
    	m_pBeginTimeList->GetNext(lPos);
    }

    m_bIsPausedInExcl = FALSE;
}

void
CSmilElement::prepForStopInExcl(LONG32 lCurTime)
{
    m_bIsStoppedInExcl = TRUE;
    m_ulStopTimeInExcl = (ULONG32)lCurTime;
}

void
CSmilElement::prepForDeferralInExcl(UINT32 lCurTime)
{
    m_ulTimeDeferralOccurred = lCurTime;
    m_bIsDeferredInExcl = TRUE;
}



HX_RESULT
CSmilElement::setBeginTime(CSmilParser* pParser)
{
    HX_RESULT ret = HXR_OK;
 
    BOOL bBeginTimeHasBeenSet = FALSE;

    UINT32 ulOffset = 0;
    SmilTimeValue* pNextResolvedTimeValue = NULL;

    LISTPOSITION lPos = NULL;

    // /We need this so we can go through
    CHXSimpleList* pListOfAllResolvedBeginTimes = new CHXSimpleList;
    // /Looks at all resolved begin times in the list and
    // returns the SmilTimeValue with the closest time at or
    // later than the time passed in:
    HX_RESULT rettimeval = HXR_OK;
    rettimeval = getNextResolvedTimeValue(pNextResolvedTimeValue,
	    SMILTIME_NEGATIVE_INFINITY, SMILTIME_NEGATIVE_INFINITY,
	    SmilBeginTimeList, pListOfAllResolvedBeginTimes);
    // /NOTE: ret!=HXR_OK is NOT an error condition; it means
    // that no value in the begin time list was found that was
    // resolved.

    BOOL bIsDescendantOfExcl = pParser->hasAncestor(SMILExcl, m_pNode);

    SMILNode* pSyncAncestor = pParser->getSyncAncestor(m_pNode);
    HX_ASSERT(pSyncAncestor);

    // make sure that we do not have an illegal begin value
    // if we have a seq for a parent.
    // /Note: we want to use our sync ancestor, not our parent, because
    // our parent could be a non-time-container like <a>, <switch>,
    // or <priorityClass>:
    if ((pSyncAncestor  &&  SMILSeq == pSyncAncestor->m_tag  &&
	// /OK if parent seq was created to expand for repeat purposes:
	!m_pNode->m_bRepeatHandled)  &&
	( (!SUCCEEDED(rettimeval)  ||  NULL==pNextResolvedTimeValue)  ||
	(pNextResolvedTimeValue->m_type != SmilTimeOffset && 
	pNextResolvedTimeValue->m_type != SmilTimeClockValue) ) )
    {
	// only offsets are legal values...
	ret = HXR_INVALID_PARAMETER;
	goto cleanup;
    }

    // /For excl descendants, we don't want to declare that a begin time
    // has been set, because we need to figure out whether or not to
    // schedule it based on when other excl children begin (which we may
    // not know yet):

    if (SUCCEEDED(rettimeval) && NULL != pNextResolvedTimeValue)
    {
	if (pListOfAllResolvedBeginTimes)
	{
	    // /Go through this list and insert each into the pending list:
	    LISTPOSITION lPosTmp =
		    pListOfAllResolvedBeginTimes->GetHeadPosition();
	    while (lPosTmp)
	    {
		SmilTimeValue* pTmpVal = (SmilTimeValue*)
			pListOfAllResolvedBeginTimes->GetAt(lPosTmp);
		if (pTmpVal  &&  pTmpVal->isTimeResolved()  &&
			pTmpVal->m_pElement)
		{
		    if (pTmpVal != pNextResolvedTimeValue  ||
			    bIsDescendantOfExcl)
		    {
			// /(?XXXEH- TODO: insert into list based on resolved
			// time?  note that begin="20s;0s" works OK, so...?)
			if (pParser->EstablishBeginTimeList())
			{
			    CHXSimpleList* pList =
				    pParser->GetPendingBeginTimeList();
			    // /Don't add if it's a duplicate:
			    if (!pParser->isDuplicateEntry(pList, pTmpVal))
			    {
				pList->AddTail(pTmpVal);
			    }
			}
		    }
		}
		else
		{
		    HX_ASSERT(0  &&  "timeval invalid");
		}
    		pListOfAllResolvedBeginTimes->GetNext(lPosTmp);
	    } // /end while(lPos...).
	}
	HX_DELETE(pListOfAllResolvedBeginTimes);
	
	if (!bIsDescendantOfExcl)
	{
	    bBeginTimeHasBeenSet = TRUE;
	    HX_ASSERT(pNextResolvedTimeValue->isTimeResolved());
	    switch (pNextResolvedTimeValue->m_type)
	    {
		case SmilTimeOffset:
		case SmilTimeClockValue:
		{
		    // /XXXEH- is this what we really want to do?
		    // Alternatively, we could have resolved-to "base" time
		    // as 0 and then m_lOffset is added to get resolved-to
		    // "offset" time:
		    m_lBeginOffset = pNextResolvedTimeValue->getTimeOffset();
		    m_bBeginOffsetSet = TRUE;
		    // /Handle this now for time containers who never get
		    // called with a TrackDurationSet():
		    m_bCurBeginIsOffsetFromSyncBase = TRUE;
		    m_ulBeginOffsetFromSyncBase = m_lBeginOffset;

#if defined(_DEBUG) // /XXXEH- testing!:
		    LONG32 lResolvedToTime = 0;
		    HX_RESULT tmprslt =
			    pNextResolvedTimeValue->getEffectiveResolvedTime(
			    lResolvedToTime);
		    HX_ASSERT(tmprslt==HXR_OK  &&
			    m_lBeginOffset == lResolvedToTime);
#endif
		}
		break;

		case SmilTimeEvent:
#if defined(ENABLE_SYNC_TO_PREV)
		case SmilTimeSyncToPrev:
#endif
		case SmilTimeSyncBase:
		{
		    // /NOTE: this's time must be *resolved* already which
		    // shouldn't happen yet:
		    HX_ASSERT(!pNextResolvedTimeValue->isTimeResolved());
		}
		break;

                case SmilTimeMediaMarker:
                {
		    HX_ASSERT(!pNextResolvedTimeValue->isTimeResolved());
                }
                break;

		case SmilTimeWallclock:
		{
		    // /XXXEH- wouldn't it be better to use
		    // getEffectiveResolvedTime() here(?) so as to treat all time
		    // types consistently?
		    m_lBeginOffset = pNextResolvedTimeValue->getTimeOffset();
		    m_bBeginOffsetSet = TRUE;
		    // /Handle this now for time containers who never get
		    // called with a TrackDurationSet():
		    m_bCurBeginIsOffsetFromSyncBase = TRUE;
		    m_ulBeginOffsetFromSyncBase = m_lBeginOffset;

		    m_bWallClockBegin = TRUE;
		}
		break;

		default:
		    HX_ASSERT(0);
		    break;
	    } // /end of "switch(pNextResolvedTimeValue->m_type)".
	} // /end of "if (!bIsDescendantOfExcl)".
    }

    // /Now, go through all unresolved begin times and add this element to
    // the appropriate list(s) for each unresolved time value found:
    if (m_pBeginTimeList)
    {
	lPos = m_pBeginTimeList->GetHeadPosition();
    }

    if (NULL == lPos) // /list is empty.
    {
	goto cleanup;
    }

    while (lPos  &&  HXR_OK == ret)
    {
	SmilTimeValue* pTmpVal = (SmilTimeValue*)m_pBeginTimeList->GetAt(lPos);

	if (pTmpVal  &&  !pTmpVal->isTimeResolved())
	{
	    switch (pTmpVal->m_type)
	    {
	    case SmilTimeOffset:
	    case SmilTimeClockValue:
		{
		    // /We shouldn't ever get here because clock times are
		    // always resolved:
		    HX_ASSERT(pTmpVal->isTimeResolved());
		}
		break;
	    case SmilTimeSyncBase:
		{
		    pParser->addBeginTimeSyncElement(this);
		    if (!bBeginTimeHasBeenSet)
		    {
			m_BeginEventSourceID = pTmpVal->m_idRef;
			m_nBeginEventSourceTag = pTmpVal->m_position;
			// /NOTE: this is unresolved:
			m_lBeginEventClockValue =
				pTmpVal->getTimeOffset();
		    }
		    // /Fixes BUG-20010521_syncArcToElementWith...smil
		    // where begin="0s;x.end" and x.end was being ignored:
		    else
		    {
			if (!m_BeginEventSourceID.GetLength())
			{
			    m_BeginEventSourceID = pTmpVal->m_idRef;
			    m_nBeginEventSourceTag = pTmpVal->m_position;
			    // /NOTE: this is unresolved:
			    m_lBeginEventClockValue = pTmpVal->getTimeOffset();
			}
			else
			{
			    HX_ASSERT(0  &&
				"XXXEHodge: need to handle multiple sync-arc begins");
			    // /XXXEH- TODO: if m_BeginEventSourceID is already
			    // set due to another sync-arc begin time, we need
			    // to have a list of m_BeginEventSourceID's & ...etc.
			    // Currently, just the first one wins.  We could
			    // treat this sync-arc as a beginEvent event-arc...
			    // (We also need to handle this in SetEndTime().)
			}

		    }
		}
		break;
#if defined(ENABLE_SYNC_TO_PREV)
	    case SmilTimeSyncToPrev:
		{
		    // /XXXEH- shouldn't we treat this just like we treat the
		    // sync base case above by doing the following?:
		    //	    pParser->addBeginTimeSyncElement(this);
		    // /XXXEH- make sure we handle begin="3s; prev.end" where
		    // both are valid times; the following doesn't handle this
		    // case:
		    if (!bBeginTimeHasBeenSet)
		    {
			SMILNode* pSibling =
				m_pNode->m_pParent->getFirstChild();
			// /Spec says to use parent time container's begin if
			// m_pNode is the first child (and has no prev
			// sibling):
			SMILNode* pLastNode = m_pNode->m_pParent;
			while (pSibling)
			{
			    if (pSibling->m_id == m_pNode->m_id)
			    {
				// we found our boy...
				break;
			    }
			    pLastNode = pSibling;
			    // /XXXEH- TODO: skip over children that aren't
			    // *timed* elements, e.g., <a> tags; maybe we
			    // are supposed to use a non-timed-prev-element's
			    // last timed child, if any, and, if none exists,
			    // use it's prev timed sibling???:
			    pSibling = m_pNode->m_pParent->getNextChild();
			}
			HX_ASSERT(pSibling && pLastNode);
			if (pSibling && pLastNode)
			{
			    // /First, if we had no prev sibling, then
			    // we need to use our parent time container's
			    // begin time as our sync base:
			    if (pLastNode == m_pNode->m_pParent)
			    {
				SMILNode* pFirstTimeContainerAncestor =
					pParser->getSyncAncestor(m_pNode);
				// /There should ALWAYS be a time container
				// ancestor, even if it's the implied seq
				// that we create when no time container
				// exists as the 1st timed child of the body:
				HX_ASSERT(pFirstTimeContainerAncestor);
				if (pFirstTimeContainerAncestor)
				{
#if XXXEH_
/* /Need to figure out if this is earlier than any current times and, if
so, call insertElementWithPendingBeginOrEnd() with the earlier time, else
call insertElementWithPendingBeginOrEnd() with this (pTmpVal) time:
*/
#endif
				    // /For parent as prev, we can just treat
				    // this as a SmilTimeOffset value since
				    // they're essentially the same thing:
				    pLastNode = pFirstTimeContainerAncestor;
				    m_lBeginOffset= pTmpVal->getTimeOffset();
				    pTmpVal->m_type = SmilTimeOffset;
				    pTmpVal->setIsTimeResolved(TRUE);
				    m_bBeginOffsetSet = TRUE;
				}
				// /else give up; can't do anything here.
			    }
			    else // /Use prev sibling time:
			    {
				m_BeginEventSourceID = pLastNode->m_id;
				// /XXXEH- wouldn't it be better to use
				// getEffectiveResolvedTime() here(?) so as
				// to treat all time types consistently?
				m_lBeginEventClockValue =
					pTmpVal->getTimeOffset();
				m_nBeginEventSourceTag =
					pTmpVal->m_position;
			    }
			}
			else
			{
			    ret = HXR_FAIL;
			}
		    }
		}
		break;
#endif
	    case SmilTimeEvent:
		{
		    pParser->addBeginEventElement(pTmpVal);
		}
                break;
            case SmilTimeMediaMarker:
                {
                    pParser->addBeginMediaMarkerSyncElement(pTmpVal);
                }
                break;
            case SmilTimeWallclock:
		{
		    // /We shouldn't ever get here because wallclock times
		    // are always resolved:
		    HX_ASSERT(pTmpVal->isTimeResolved());
		}
		break;
	    default:
		HX_ASSERT(0);
		break;
	    } // /end switch(pTmpVal->m_type).
	}
    	m_pBeginTimeList->GetNext(lPos);
    } // /end while (lPos ...).

    // else time must already be set.
cleanup:
    return ret;
}

HX_RESULT
CSmilElement::setEndTime(CSmilParser* pParser)
{
    HX_RESULT ret = HXR_OK;

    BOOL bEndTimeHasBeenSet = FALSE;

    UINT32 ulOffset = 0;
    SmilTimeValue* pNextResolvedTimeValue = NULL;

    LISTPOSITION lPos = NULL;

    CHXSimpleList* pListOfAllResolvedEndTimes = new CHXSimpleList;
    // /Looks at all resolved end times in the list and
    // returns the SmilTimeValue with the closest time at or
    // later than the time passed in:
    HX_RESULT rettimeval = HXR_OK;
    rettimeval = getNextResolvedTimeValue(pNextResolvedTimeValue,
	    SMILTIME_NEGATIVE_INFINITY, SMILTIME_NEGATIVE_INFINITY,
	    SmilEndTimeList, pListOfAllResolvedEndTimes);
    // /NOTE: ret!=HXR_OK is NOT an error condition; it means
    // that no value in the end time list was found that was
    // resolved.
    
    if (SUCCEEDED(rettimeval) && NULL != pNextResolvedTimeValue)
    {
	if (pListOfAllResolvedEndTimes)
	{
	    // /Go through this list and insert each into the pending list:
	    LISTPOSITION lPosTmp =
		    pListOfAllResolvedEndTimes->GetHeadPosition();
	    while (lPosTmp)
	    {
		SmilTimeValue* pTmpVal = (SmilTimeValue*)
			pListOfAllResolvedEndTimes->GetAt(lPosTmp);
		if (pTmpVal  &&  pTmpVal->isTimeResolved()  &&
			pTmpVal->m_pElement)
		{
		    if (pTmpVal != pNextResolvedTimeValue)
		    {
			// /(?XXXEH- TODO: insert into list based on resolved
			// time?  end="20s;0s" works OK, so...?)
			if (pParser->EstablishEndTimeList())
			{
			    CHXSimpleList* pList =
				    pParser->GetPendingEndTimeList();
			    // /Don't add if it's a duplicate:
			    if (!pParser->isDuplicateEntry(pList, pTmpVal))
			    {
				pList->AddTail(pTmpVal);
			    }
			}
		    }
		}
		else
		{
		    HX_ASSERT(0  &&  "timeval invalid");
		}
    		pListOfAllResolvedEndTimes->GetNext(lPosTmp);
	    } // /end while(lPos...).
	}
	HX_DELETE(pListOfAllResolvedEndTimes);

	bEndTimeHasBeenSet = TRUE;
	HX_ASSERT(pNextResolvedTimeValue->isTimeResolved());
	switch (pNextResolvedTimeValue->m_type)
	{
	case SmilTimeOffset:
	case SmilTimeClockValue:
	    {
		// /XXXEH- is this what we really want to do?
		// Alternatively, we could have resolved-to "base" time
		// as 0 and then m_lOffset is added to get resolved-to
		// "offset" time:
		m_lEndOffset = pNextResolvedTimeValue->getTimeOffset();
		m_bEndOffsetSet = TRUE;
#if defined(_DEBUG) // /XXXEH- testing!:
		LONG32 lResolvedToTime = 0;
		HX_RESULT tmprslt =
			pNextResolvedTimeValue->getEffectiveResolvedTime(
			lResolvedToTime);
		HX_ASSERT(tmprslt==HXR_OK  &&
			m_lEndOffset == lResolvedToTime);
#endif
	    }
	    break;
	case SmilTimeSyncBase:
#if defined(ENABLE_SYNC_TO_PREV)
	case SmilTimeSyncToPrev:
#endif
	case SmilTimeEvent:
	    {
		// /NOTE: this's time must be *resolved* already which
		// shouldn't happen yet:
		HX_ASSERT(!pNextResolvedTimeValue->isTimeResolved());
	    }
	    break;
	case SmilTimeMediaMarker:
	    {
		//XXXEH- TODO: handle media markers:
		HX_ASSERT(0);
		//pParser->addEndMediaMarkerSyncElement(this);
	    }
	    break;
	case SmilTimeWallclock:
	    {
		// /XXXEH- wouldn't it be better to use
		// getEffectiveResolvedTime() here(?) so as to treat all time
		// types consistently?
		m_lEndOffset = pNextResolvedTimeValue->getTimeOffset();
		m_bEndOffsetSet = TRUE;
		m_bWallClockEnd = TRUE;
	    }
	    break;
	case SmilTimeNone:
	    break;
	} // /end switch(pNextResolvedTimeValue->m_type).
    }

    // /Now, go through all unresolved end times and add this element to
    // the appropriate list(s) for each unresolved time value found:
    if (m_pEndTimeList)
    {
	lPos = m_pEndTimeList->GetHeadPosition();
    }

    if (NULL == lPos) // /list is empty.
    {
	goto cleanup;
    }

    while (lPos  &&  HXR_OK == ret)
    {
	SmilTimeValue* pTmpVal = (SmilTimeValue*)m_pEndTimeList->GetAt(lPos);

	if (pTmpVal  &&  !pTmpVal->isTimeResolved())
	{
	    switch (pTmpVal->m_type)
	    {
	    case SmilTimeOffset:
	    case SmilTimeClockValue:
		{
		    // /We shouldn't ever get here because clock times are
		    // always resolved:
		    HX_ASSERT(pTmpVal->isTimeResolved());
		}
		break;
	    case SmilTimeSyncBase:
		{
		    pParser->addEndTimeSyncElement(this);
		    // /XXXEH- make sure we handle end="3s; prev.end" where
		    // both are valid times; the following doesn't handle this
		    // case:
		    if (!bEndTimeHasBeenSet)
		    {
			m_EndEventSourceID = pTmpVal->m_idRef;
			m_nEndEventSourceTag = pTmpVal->m_position;
			// /XXXEH- wouldn't it be better to use
			// getEffectiveResolvedTime() here(?) so as to treat
			// all time types consistently?
			m_lEndEventClockValue =
				pTmpVal->getTimeOffset();
		    }
		}
		break;
#if defined(ENABLE_SYNC_TO_PREV)
	    case SmilTimeSyncToPrev:
		{
		    // /XXXEH- shouldn't we treat this just like we treat the
		    // sync base case above by doing the following?:
		    //	    pParser->addEndTimeSyncElement(this);
		    // /XXXEH- make sure we handle end="3s; prev.end" where
		    // both are valid times; the following doesn't handle this
		    // case:
		    if (!bEndTimeHasBeenSet)
		    {
			SMILNode* pSibling =
				m_pNode->m_pParent->getFirstChild();
			SMILNode* pLastNode = pSibling;
			while (pSibling)
			{
			    if (pSibling->m_id == m_pNode->m_id)
			    {
				// we found our boy...
				break;
			    }
			    pLastNode = pSibling;
			    pSibling = m_pNode->m_pParent->getNextChild();
			}
			HX_ASSERT(pSibling && pLastNode);
			if (pSibling && pLastNode)
			{
			    m_EndEventSourceID = pLastNode->m_id;
			    // /XXXEH- wouldn't it be better to use
			    // getEffectiveResolvedTime() here(?) so as to
			    // treat all time types consistently?
			    m_lEndEventClockValue =
				    pTmpVal->getTimeOffset();
			    m_nEndEventSourceTag =
				    pTmpVal->m_position;
			}
			else
			{
			    ret = HXR_FAIL;
			}
		    }
		}
		break;
#endif
	    case SmilTimeEvent:
		{
		    pParser->addEndEventElement(pTmpVal);
		}
                break;
            case SmilTimeMediaMarker:
                {
                    pParser->addEndMediaMarkerSyncElement(pTmpVal);
                }
                break;
	    case SmilTimeWallclock:
		{
		    // /We shouldn't ever get here because wallclock times
		    // are always resolved:
		    HX_ASSERT(pTmpVal->isTimeResolved());
		}
		break;
	    case SmilTimeNone:
		break;
	    } // /end switch(pTmpVal->m_type).
	}
    	m_pEndTimeList->GetNext(lPos);
    } // /end while (lPos ...).
    // else time must allready be set.
cleanup:
    return ret;
}

HX_RESULT
CSmilElement::getEndTimeValue(REF(SmilTimeValue*) pValue)
{
    HX_RESULT ret = HXR_OK;
    // This function just returns the first value in the end-time list,
    // even if that value is not yet resolved to a clock time:
    // If you want to use the "right" value for a given time t, then
    // call "getNextResolvedTimeValue()"
    if (m_pEndTimeList)
    {
	LISTPOSITION pos = m_pEndTimeList->GetHeadPosition();
	if (pos)
	{
	    pValue = (SmilTimeValue*)m_pEndTimeList->GetAt(pos);
	}
    }
    return ret;
}

HX_RESULT
CSmilElement::getBeginTimeValue(REF(SmilTimeValue*) pValue)
{
    HX_RESULT ret = HXR_OK;
    // This function just returns the first value in the begin-time list,
    // even if that value is not yet resolved to a clock time:
    // If you want to use the "right" value for a given time t, then
    // call "getNextResolvedTimeValue()"
    if (m_pBeginTimeList)
    {
	LISTPOSITION pos = m_pBeginTimeList->GetHeadPosition();
	if (pos)
	{
	    pValue = (SmilTimeValue*)m_pBeginTimeList->GetAt(pos);
	}
    }
    return ret;
}


// /Returns HXR_OK except if the element has not ever been inserted in the
// timeline, in which case the return value is HXR_NOT_INITIALIZED.
// In this case, the value placed into ulActualStartTime will remain whatever
// is set in the delay value of the element.  If the element has an
// indefinite begin time, SMIL_TIME_INFINITY is returned in ulActualStartTime
// with HXR_OK as the return value.  Note that SMIL_TIME_INFINITY is not
// the same value as ((UINT32)-1) which is a possible return value and means
// that the value is not initialized:
HX_RESULT
CSmilElement::getCurrentScheduledStartTime(REF(ULONG32) ulActualStartTime)
{
    HX_RESULT ret = HXR_OK;
    
    ulActualStartTime = m_ulDelay;

    BOOL bIsMediaObject = CSmilParser::isMediaObject(m_pNode);

    if (!m_bHasBeenScheduled && bIsMediaObject)
    {
	ret = HXR_NOT_INITIALIZED;
    }
    else if (m_bIndefiniteBegin)
    {
	ulActualStartTime = SMILTIME_INFINITY;
    }
    else if (!bIsMediaObject && ulActualStartTime == ((UINT32) -1))
    {
	ret = HXR_NOT_INITIALIZED;
    }
// /#define XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTARTTIME
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTARTTIME)
    {
	static BOOL bFirstTime = TRUE;
	FILE* f1 = ::fopen("c:\\smil2.txt", bFirstTime?"w":"a+");
	::fprintf(f1, "\n[%s]->getCurrentScheduledStartTime() "
		"ulActualStopTime := %lu (delay=%lu, dur=%lu, "
		"beginOffsetFromSyncBase=%lu)  \n", (const char*)m_pNode->m_id,
		ulActualStartTime, m_ulDelay, m_ulDuration, m_ulBeginOffsetFromSyncBase);
	::fclose(f1);
	bFirstTime=FALSE;
    }
#endif
    return ret;
}

// /Returns HXR_OK except if the element has not ever been inserted in the
// timeline, in which case the return value is HXR_NOT_INITIALIZED.
// In this case, the value placed into ulActualStopTime will remain whatever
// is set in the delay value of the element.  If the element has an
// indefinite end time, SMIL_TIME_INFINITY is returned in ulActualStopTime
// with HXR_OK as the return value.  Note that SMIL_TIME_INFINITY is not
// the same value as ((UINT32)-1) which is a possible return value and means
// that the value is not initialized:
HX_RESULT
CSmilElement::getCurrentScheduledStopTime(REF(ULONG32) ulActualStopTime)
{
    HX_RESULT ret = HXR_OK;

    // /Fixes PR 63733: initialize to invalid time in case caller did not:
    ulActualStopTime = (UINT32)-1;

    if (m_bIndefiniteDuration  ||  m_bIndefiniteEnd)
    {
	ulActualStopTime = SMILTIME_INFINITY;
    }
    // /Helps fix PR 53531 (unwinding after string of pauses) when
    // m_ulDuration has not been updated to reflect the paused time so far:
    else if (isPausedInExcl())
    {
#if XXXEH_HANDLE_CASE_WHERE_RESOLVED_END_IS_NOW_IN_EFFECT
#else
	ulActualStopTime = SMILTIME_PAUSED_INDEFINITELY;
#endif
    }
    else if (isStoppedInExcl())
    {
	ulActualStopTime = m_ulStopTimeInExcl;
    }
    else if (isDeferredInExcl())
    {
	ulActualStopTime = SMILTIME_DEFERRED_INDEFINITELY;
    }
    else if (m_ulDelay    != ((UINT32) -1) &&
             m_ulDuration != ((UINT32) -1))
    {
        if (m_bBeginOffsetSet)
        {
	    LONG32 lBeginOffsetPast0 = m_lBeginOffset;
	    if (m_lBeginOffset < 0)
	    {
		lBeginOffsetPast0 = 0;
	    }
            ulActualStopTime = m_ulDelay + m_ulDuration - lBeginOffsetPast0;
        }
        else
        {
            if (m_bCurBeginIsOffsetFromSyncBase)
            {
		ulActualStopTime = m_ulDelay + m_ulDuration;
		// /Adding this if() check fixes hide-site problems exposed
		// in the repro case for PR 53531 once 53531 had been fixed;
		// this makes sure that the beginOffsetFromSyncBase is
		// accounted for only when it should be. Fixes
		// BUG-20010619_lastExclChildGetsItsOffsetFromSyncbaseIgnored.smil
		if (m_bDurationIncludesDelayBeyondSyncbase)
		{
		    ulActualStopTime -= m_ulBeginOffsetFromSyncBase;
		}
            }
            else
            {
                ulActualStopTime = m_ulDelay + m_ulDuration;
            }
        }
    }
    else if (m_ulDelay != (UINT32)-1  &&  m_bCurrentSourceIsLive)
    {
	// /Fix for PR 57230:
	// live sources that have 0 dur, which means they play
	// forever, should have infinity, not 0, used as
	// their dur when calculating whether or not they're
	// visible at the current time:
	ulActualStopTime = (UINT32)SMILTIME_INFINITY;
    }
    else
    {
        ret = HXR_NOT_INITIALIZED;
    }

// /#define XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTOPTIME
#if defined(_DEBUG)  &&  defined(XXXEHODGE_DEBUG_PRINTF_CURRENTSCHEDULEDSTOPTIME)
    {
	static BOOL bFirstTime = TRUE;
	FILE* f1 = ::fopen("c:\\smil2.txt", bFirstTime?"w":"a+");
	::fprintf(f1, "\n[%s]->getCurrentScheduledStopTime() "
		"ulActualStopTime := %lu (delay=%lu, dur=%lu, "
		"beginOffsetFromSyncBase=%lu)  \n", (const char*)m_pNode->m_id,
		ulActualStopTime, m_ulDelay, m_ulDuration, m_ulBeginOffsetFromSyncBase);
	::fclose(f1);
	bFirstTime=FALSE;
    }
#endif

    return ret;
}


// /This function takes a time as input and returns in pValue the begin
// or end time (depending on listType) that makes the most
// sense given the time passed in; it assumes times that are
// resolvable have already been resolved (and thus their m_bIsResolved
// is TRUE), and it thus ignores unresolved times.  It returns
// HXR_FAILED and sets pValue to NULL if no now-or-future resolved begin
// time is found.
// NOTE!!!: this will NOT return the currently-active time if the element
// is already playing at lCurTime since it would be in the past.  The
// currently-active begin and end times can be found in this->m_lBeginOffset
// or this->m_lEndOffset, respectively, and that those values are only
// valid if m_bBeginOffsetSet and m_bEndOffsetSet, respectively, are TRUE:
// Also Note: pValue, as well as the two lists, is|are filled with either
// begin or end time|times, depending on the listType value:
HX_RESULT
CSmilElement::getNextResolvedTimeValue(REF(SmilTimeValue*) pValue,
				INT32 lCurTimeInGroupTime,
				INT32 lCurTimeInSyncBaseTime,
				SmilTimingListType listType,
				CHXSimpleList* pListOfAllResolvedTimes)
{
    HX_RESULT retval = HXR_OK;
    SmilTimeValue* pTmpVal = NULL;
    SmilTimeValue* pTimeValFoundSoFar = NULL;
    CHXSimpleList* pWhichList = NULL;

    LONG32 lCurTimeInSameTimeSpace = lCurTimeInSyncBaseTime;
    LONG32 lCurUsableTimeInGroupTime = lCurTimeInGroupTime;
    // /If group time is not known, use sync-base time:
    if (SMILTIME_NEGATIVE_INFINITY == lCurTimeInGroupTime)
    {
	lCurUsableTimeInGroupTime = lCurTimeInSyncBaseTime;
    }

    LISTPOSITION lPos = NULL;
    if (SmilBeginTimeList == listType) // /get begin time:
    {
	pWhichList = m_pBeginTimeList;
    }
    else if (SmilEndTimeList == listType)// /get end time:
    {
	pWhichList = m_pEndTimeList;
    }
    else
    {
	HX_ASSERT(SmilBeginTimeList == listType  ||
		SmilEndTimeList == listType);
    }
    if (NULL == pWhichList)
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    lPos = pWhichList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    // /The following loop simply finds the time that is nearest
    // to, but not earlier than, lCurTime:
    // *Which* curTime depends on the type; events resolve to group times,
    // while clockValues resolve to syncBase times:
    while (lPos  &&  HXR_OK == retval)
    {
	pTmpVal = (SmilTimeValue*)pWhichList->GetAt(lPos);

	if (pTmpVal)
	{
	    if (!pTmpVal->isTimeResolved())
	    {
    		pWhichList->GetNext(lPos);
		continue;
	    }

	    // /It's resolved, so insert it:
	    if (pListOfAllResolvedTimes)
	    {
		pListOfAllResolvedTimes->AddTail(pTmpVal);
	    }

	    LONG32 lResolvedToTimeOfTimeValFoundSoFar =SMILTIME_INFINITY;
	    HX_RESULT tmprslt = HXR_OK;
	    if (NULL != pTimeValFoundSoFar)
	    {
		// /We want to use pTimeValFoundSoFar, not pTmpVal here!
		// Fixes case where there are two or more resolved times and
		// the first, lexically, is higher than any of the others:
		tmprslt = pTimeValFoundSoFar->getEffectiveResolvedTime(
			lResolvedToTimeOfTimeValFoundSoFar);
		HX_ASSERT(HXR_OK == tmprslt);
		if (!SUCCEEDED(tmprslt))
		{
		    HX_ASSERT(SUCCEEDED(tmprslt));
		    continue;
		}
	    }
	    switch (pTmpVal->getTimeType())
	    {
		case SmilTimeMediaMarker:
		case SmilTimeWallclock:
		case SmilTimeEvent:
		    lCurTimeInSameTimeSpace = lCurUsableTimeInGroupTime;
		    break;

		case SmilTimeOffset:
		case SmilTimeClockValue:
		case SmilTimeSyncBase:
		default:
		    lCurTimeInSameTimeSpace = lCurTimeInSyncBaseTime;
		    break;
	    }
	    switch (pTmpVal->getTimeType())
	    {
		case SmilTimeNone:
		    break; // /Skip this time.

		case SmilTimeWallclock:
		case SmilTimeOffset:
		case SmilTimeClockValue:
		{
		    LONG32 lEffectiveResolvedToTime = 0;
		    tmprslt = pTmpVal->getEffectiveResolvedTime(
			    lEffectiveResolvedToTime);
#if defined(_DEBUG)	// /XXXEH- testing!: 
		    LONG32 lTimeOffset = pTmpVal->getTimeOffset();
		    HX_ASSERT(tmprslt==HXR_OK  &&
			    lTimeOffset == lEffectiveResolvedToTime);
#endif
// /XXXEH- TODO: add bool (& time val?) that says if (& when) this begin time
// was already used; if already used, don't use it again, and seeks and
// repeats must erase this bool (& time val); this is especially needed
// if we start treating all times as events, i.e., consistently:
		    // /Only use times that are now or later:
		    if (lEffectiveResolvedToTime >= lCurTimeInSameTimeSpace)
		    {
			if (NULL == pTimeValFoundSoFar  ||
				lEffectiveResolvedToTime <
				lResolvedToTimeOfTimeValFoundSoFar)
			{
			    pTimeValFoundSoFar = pTmpVal;
			}
		    }
		}
		break;

		case SmilTimeMediaMarker:
		case SmilTimeSyncBase:
#if defined(ENABLE_SYNC_TO_PREV)
		case SmilTimeSyncToPrev:
#endif
		case SmilTimeEvent:
		{
// /XXXEH- TODO: finish this, and make sure m_lResolvedToTime and m_lWhenTimeWasResolved are being set:
		    LONG32 lEffectiveResolvedToTime = 0;
		    HX_RESULT tmprslt2 = pTmpVal->
			    getEffectiveResolvedTime(
			    lEffectiveResolvedToTime);
		    HX_ASSERT(SUCCEEDED(tmprslt2));// /Should be resovled.
		    if (SUCCEEDED(tmprslt2)  &&
			    lEffectiveResolvedToTime >= lCurTimeInSameTimeSpace)
		    {
			LONG32 lTimeOffset = pTmpVal->getTimeOffset();
			if (lEffectiveResolvedToTime <
// /XXXEH- TODO: add +offset since this is an event-based time; need
// "timecmp(x,y)" function that compares two SmilTimeValues (taking into
// account that negative offsets shouldn't be compared if this is an event
// time) and returns -1 if x is greater, 0 if same, +1 if y is later:
// /XXXEH- TODO: argue the following with W3C SYMM Smil Boston Working
// Group: if we treat event-based times with negative offset as "now"
// w/clip-begin, then why not same for sync-base (and other) values whose
// times are in the past when they first become resolved (e.g., clipped by
// parent, or sync-arc to event-arc'd time)?  For example:
// <par>
//   <ref id="x" begin="foo.activate" />
//   <par begin="10s">
//     <!-- resolves 3s after it's intended value: -->
//     <ref begin="x.begin-3s"/>
				lResolvedToTimeOfTimeValFoundSoFar)
			{
			    pTimeValFoundSoFar = pTmpVal;
			}
			// /XXXEH- TODO: verify that this is how the
			// SMIL Boston draft ends up dealing with the
			// begin="x.focusInEvent-5s; x.focusInEvent-3s"
			// issue:
			// /Take the one with the earliest offset if
			// the resolved time is the same, so
			// "x.focusInEvent-5s" beats out "x.focusInEvent",
			// and "x.focusInEvent" beats out "x.focusInEvent+2s"
			// /XXXEH- I think restart might play into this
			// so that x.focusInEvent-5s would be used over
			// x.focusInEvent-3s if and only if (restart=="never"
			// OR (restart!="never" AND this's dur <=2s)):
			else if (lEffectiveResolvedToTime ==
				lResolvedToTimeOfTimeValFoundSoFar  &&
				lTimeOffset <
				pTimeValFoundSoFar->getTimeOffset())
			{
			    pTimeValFoundSoFar = pTmpVal;
			}
		    }
		}
		break;
	    }
	}
    	pWhichList->GetNext(lPos);
    } // /end while(lPos...).

    pValue = pTimeValFoundSoFar;

    if (NULL == pValue)
    {
	retval = HXR_FAILED;
    }

cleanup:
    return retval;
}

HX_RESULT
CSmilElement::resolveEventTimeValues(INT32 lCurTime,
				const char* pEventName,
				const char* pEventElementId,
				SmilTimingListType listType,
				REF(BOOL) bATimeWasResolved)
{
    HX_RESULT retval = HXR_OK;
    SmilTimeValue* pValue = NULL;
    CHXSimpleList* pWhichList = NULL;
    bATimeWasResolved = FALSE;
    LISTPOSITION lPos = NULL;

    if (NULL == pEventName  ||
	    // /pEventElementId can be NULL but not non-NULL and empty:
	    (pEventElementId  &&  '\0' == *pEventElementId) )
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    if (SmilBeginTimeList == listType) // /get begin time:
    {
	pWhichList = m_pBeginTimeList;
    }
    else if (SmilEndTimeList == listType)// /get end time:
    {
	pWhichList = m_pEndTimeList;
    }
    else
    {
	HX_ASSERT(SmilBeginTimeList == listType  ||
		SmilEndTimeList == listType);
    }
    if (NULL == pWhichList)
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    // /XXXEH- is there any reason we should start from tail instead?
    lPos = pWhichList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    while (lPos  &&  HXR_OK == retval)
    {
	pValue = (SmilTimeValue*)pWhichList->GetAt(lPos);

	// /If this begin or end time is a time event based on this event
	// from the element with pEventElementId, then we can (and do)
	// resolve it here:
	if(NULL!= pValue  &&  (SmilTimeEvent == pValue->m_type  ||
		(pValue->m_bTreatSyncArcAsEvent  &&
		pValue->isSyncBaseTimeVal()) ) )
	{
	    // /Now, see if we've got an element that is waiting for this
	    // event from the pEventElement:
	    if (!pValue->getIdRef()  ||  !pValue->getEventName())
	    {
		HX_ASSERT(pValue->getIdRef()  && pValue->getEventName());
		pWhichList->GetNext(lPos);
		continue;
	    }
	    if ((!pEventElementId  ||  strcmp(pValue->getIdRef(),
		    pEventElementId) == 0)  &&
		    strcmp(pValue->getEventName(), pEventName) == 0 )
	    {
		BOOL bIsResolvedAlready = pValue->isTimeResolved();
		INT32 lProirResolvedToTime = (bIsResolvedAlready?
			pValue->getResolvedToTimeWithoutOffset():-1);
		pValue->setIsTimeResolved(TRUE);
		pValue->setResolvedToTime(lCurTime);
		pValue->setWhenTimeWasResolved(lCurTime);
		// /[Optimization]: In case we get called twice with the
		// same event at the same resolved time, let's not force
		// a stop/start of the element:
		if (!bIsResolvedAlready  ||  lCurTime !=lProirResolvedToTime)
		{
		    bATimeWasResolved = TRUE;
		}
	    }
	}
    	pWhichList->GetNext(lPos);
    } // /end while(lPos...).

cleanup:
    return retval;
}


// /This method goes through the m_pEndTimeList to see if there's an
// event-arc end time or other unresolved end time; this is important
// for use in the algorithm for determining whether or not something
// should restart (see SMIL 2.0 Spec Timing Module's getNextInterval()
// pseudocode):
BOOL
CSmilElement::hasUnresolvedEndTime()
{
    BOOL bHasUnresolvedEndTime = FALSE;

    SmilTimeValue* pValue = NULL;
    LISTPOSITION lPos = NULL;

    if (NULL == m_pEndTimeList)
    {
	goto cleanup;
    }

    // /XXXEH- is there any reason we should start from tail instead?
    lPos = m_pEndTimeList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
	goto cleanup;
    }

    while (lPos  &&  !bHasUnresolvedEndTime)
    {
	pValue = (SmilTimeValue*)m_pEndTimeList->GetAt(lPos);

	if(NULL == pValue)
	{
	    m_pEndTimeList->GetNext(lPos);
	    continue;
	}
	if (SmilTimeEvent == pValue->m_type)
	{
	    bHasUnresolvedEndTime = TRUE;
	}
	// /XXXEH- TODO: need to go through sync-arc element and see
	// if *it* has any unresolved time(s) (begin or end, depending
	// on this sync-arc time's position value) and, if not, then
	// DON'T claim bHasUnresolvedEndTime is TRUE:
	else if (pValue->m_bTreatSyncArcAsEvent  &&
		pValue->isSyncBaseTimeVal())
	{
	    if (!pValue->isTimeResolved())
	    {
		bHasUnresolvedEndTime = TRUE;
	    }
	}
    	m_pEndTimeList->GetNext(lPos);
    } // /end while(lPos...).

cleanup:
    return bHasUnresolvedEndTime;
}



// /This method returns the pure duration from the media's begin to its
// end, w/o any delay included:
ULONG32
CSmilElement::getPureDuration()
{
    HX_ASSERT(!m_bDurationIncludesDelayBeyondSyncbase  ||
	    m_bCurBeginIsOffsetFromSyncBase);
    if (m_bDurationIncludesDelayBeyondSyncbase)
    {
	return (m_ulDuration - m_ulBeginOffsetFromSyncBase);
    }
    else
    {
	return m_ulDuration;
    }
/*
    return (m_bDurationIncludesDelayBeyondSyncbase?
	    m_ulDuration-m_ulBeginOffsetFromSyncBase : m_ulDuration);
*/
}


HX_RESULT
CSmilElement::resolveSyncArcTimeValues(INT32 lResolvedToTime,
				const char* pSyncBaseElementId,
				SmilTimingListType listType,
				REF(BOOL) bATimeWasResolved,
				BOOL bMoveNewlyResolvedsToPendingTimesList,
				CSmilParser* pParser)
{
    HX_RESULT retval = HXR_OK;
    SmilTimeValue* pValue = NULL;
    CHXSimpleList* pWhichList = NULL;
    bATimeWasResolved = FALSE;
    LISTPOSITION lPos = NULL;

    if (NULL == pSyncBaseElementId)
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    if (SmilBeginTimeList == listType) // /get begin time:
    {
	pWhichList = m_pBeginTimeList;
    }
    else if (SmilEndTimeList == listType)// /get end time:
    {
	pWhichList = m_pEndTimeList;
    }

    else
    {
	HX_ASSERT(SmilBeginTimeList == listType  ||
		SmilEndTimeList == listType);
    }
    if (NULL == pWhichList)
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    // /XXXEH- is there any reason we should start from tail instead?
    lPos = pWhichList->GetHeadPosition();

    if (NULL == lPos) // /list is empty.
    {
	retval = HXR_FAILED;
	goto cleanup;
    }

    while (lPos  &&  HXR_OK == retval)
    {
	pValue = (SmilTimeValue*)pWhichList->GetAt(lPos);

	// /If this begin or end time is a time event based on this event
	// from the element with pSyncBaseElementId, then we can (and do)
	// resolve it here:
	if(NULL!= pValue  &&  pValue->isSyncBaseTimeVal())
	{
	    // /Now, see if we've got an element that is waiting for this
	    // event from the pEventElement:
	    if (!pValue->getIdRef())
	    {
		HX_ASSERT(pValue->getIdRef());
    		pWhichList->GetNext(lPos);
		continue;
	    }
	    if (strcmp(pValue->getIdRef(), pSyncBaseElementId) == 0)
	    {
		// /Helps fix SMIL 2.0 Interop Timing #15.13:
		// if we're resuming a paused element and its duration thus
		// got extended, don't re-resolve (and re-insert) other
		// elements who are sync-arc'd to the *begin* time of this
		// resumed element:
		if (pValue->isTimeResolved()  &&  lResolvedToTime ==
			pValue->getResolvedToTimeWithoutOffset()  &&
			SmilBeginTimeList == listType)
		{
		    bMoveNewlyResolvedsToPendingTimesList = FALSE;
		}

		pValue->setIsTimeResolved(TRUE);
		pValue->setResolvedToTime(lResolvedToTime);

		// /XXXEH- this shouldn't matter what we put here, but
		// we *should* fill this with the current time:
		pValue->setWhenTimeWasResolved(0);
		bATimeWasResolved = TRUE;

		if (pParser  &&  bMoveNewlyResolvedsToPendingTimesList)
		{
		    if (SmilBeginTimeList == listType)
		    {
			if (pParser->EstablishBeginTimeList())
			{
			    CHXSimpleList* pList =
				    pParser->GetPendingBeginTimeList();
			    // /Don't add if it's a duplicate:
			    if (!pParser->isDuplicateEntry(pList, pValue))
			    {
				pList->AddTail(pValue);
			    }
			}
		    }
		    else
		    {
			if (pParser->EstablishEndTimeList())
			{
			    CHXSimpleList* pList =
				    pParser->GetPendingEndTimeList();
			    // /Don't add if it's a duplicate:
			    if (!pParser->isDuplicateEntry(pList, pValue))
			    {
				pList->AddTail(pValue);
			    }
			}
		    }
		}
	    }
	}
    	pWhichList->GetNext(lPos);
    } // /end while(lPos...).

cleanup:
    return retval;
}

// /Returns HXR_FAILED if element or its parent don't exist.  The spec says
// that "always" is what is to be used for restart and restartDefault for the
// element if it has no parent or the parent has no restartDefault value:
HX_RESULT
CSmilElement::getParentRestartDefault()
{
    HX_RESULT ret = HXR_OK;
    if (NULL == m_pNode)
    {
	ret = HXR_FAILED;
    }
    else
    {
	SMILNode* pParent = m_pNode->m_pParent;
	if (!pParent  ||  !pParent->m_pElement)
	{
	    ret = HXR_FAILED;
	}
	else
	{
	    switch (pParent->m_pElement->m_restartDefaultBehavior)
	    {
		case SmilRestartNever:
		case SmilRestartWhenNotActive:
		case SmilRestartAlways:
		    // /Both restartDefault and restart behavior are assigned
		    // to parent's restartDefault behavior:
		    m_restartDefaultBehavior =
			    m_restartBehavior =
			    pParent->m_pElement->m_restartDefaultBehavior;
		    break;

		case SmilRestartInherit:
		    m_restartDefaultBehavior =
			    m_restartBehavior = SmilRestartAlways;
		    break;

		default:
		    HX_ASSERT(0);
		    break;
	    }
	}
    }

    return ret;
}

void CSmilElement::updateRemoveTime(UINT32 ulRemoveTime)
{
    m_ulRemoveTime = ulRemoveTime;
    if (m_pHandler)
    {
        m_pHandler->handleRemoveTimeUpdate(this, ulRemoveTime);
    }
}

void CSmilElement::handleXMMF(const char* pszID, const char* pszXMMFile, const char* pszSrc)
{
    if (m_pHandler)
    {
        m_pHandler->handleExternalMediaMarkerFile(this,
                                                  pszID,
                                                  pszXMMFile,
                                                  pszSrc);
    }
}

BOOL CSmilElement::hasEventBasedBeginTime()
{
    BOOL bRet = FALSE;

    if (m_pBeginTimeList)
    {
        LISTPOSITION pos = m_pBeginTimeList->GetHeadPosition();
        while (pos)
        {
            SmilTimeValue* pValue =
                (SmilTimeValue*) m_pBeginTimeList->GetNext(pos);
            if (pValue &&
                pValue->m_type == SmilTimeEvent)
            {
                bRet = TRUE;
                break;
            }
        }
    }
    // We also have to check our sync ancestor because
    // if our sync ancestor has an event based begin
    // time, then so do we. However, if we already
    // know we have an event based time, then we don't
    // need to check
    if (!bRet)
    {
        CSmilElement* pSyncAnc = getSyncAncestorElement();
        if (pSyncAnc)
        {
            bRet = pSyncAnc->hasEventBasedBeginTime();
        }
    }

    return bRet;
}

CSmilElement* CSmilElement::getSyncAncestorElement()
{
    CSmilElement* pRet = NULL;

    if (m_pNode)
    {
        BOOL bLegalMediaObjectChild = FALSE;
        if (m_pNode->m_tag == SMILArea         ||
            m_pNode->m_tag == SMILAnchor       ||
            m_pNode->m_tag == SMILAnimate      ||
            m_pNode->m_tag == SMILSet          ||
            m_pNode->m_tag == SMILAnimateColor ||
            m_pNode->m_tag == SMILAnimateMotion)
        {
            bLegalMediaObjectChild = TRUE;
        }
        SMILNode* pAnc = m_pNode->m_pParent;
        while (pAnc)
        {
            if (pAnc->m_tag == SMILPar  ||
                pAnc->m_tag == SMILExcl ||
                pAnc->m_tag == SMILSeq)
            {
                pRet = pAnc->m_pElement;
                break;
            }
            else if ((pAnc->m_tag == SMILRef        ||
                      pAnc->m_tag == SMILText       ||
                      pAnc->m_tag == SMILImg        ||
                      pAnc->m_tag == SMILAudio      ||
                      pAnc->m_tag == SMILVideo      ||
                      pAnc->m_tag == SMILAnimation  ||
                      pAnc->m_tag == SMILTextstream ||
                      pAnc->m_tag == SMILBrush      ||
                      pAnc->m_tag == SMILPrefetch) &&
                     bLegalMediaObjectChild)
            {
                pRet = pAnc->m_pElement;
                break;
            }
            pAnc = pAnc->m_pParent;
        }
    }

    return pRet;
}

BOOL CSmilElement::hasEventBasedEndTime()
{
    BOOL bRet = FALSE;

    if (m_pEndTimeList)
    {
        LISTPOSITION pos = m_pEndTimeList->GetHeadPosition();
        while (pos)
        {
            SmilTimeValue* pValue =
                (SmilTimeValue*) m_pEndTimeList->GetNext(pos);
            if (pValue &&
                pValue->m_type == SmilTimeEvent)
            {
                bRet = TRUE;
                break;
            }
        }
    }

    return bRet;
}

HX_RESULT	
CSmilElement::GetElementProperties(REF(IHXValues*) pProperties)
{
    HX_RESULT		rc = HXR_OK;
    BOOL		bDone = FALSE;
    ElementWithinTag	elementWithinTag = WithinUnknown;
    SMILNode*		pNode = m_pNode;

    HX_ASSERT(pNode);
    while (pNode->m_pParent && !bDone)
    {	
	switch (pNode->m_pParent->m_tag)
	{
	case SMILPar:
	    if (elementWithinTag == WithinSeq)
	    {
		elementWithinTag = WithinSeqInPar;
	    }
	    else
	    {
		elementWithinTag = WithinPar;
	    }
	    bDone = TRUE;
	    break;
	case SMILSeq:
	    elementWithinTag = WithinSeq;
	    break;
	default:
	    break;
	}

	pNode = pNode->m_pParent;
    }

    pProperties = new CHXHeader();
    pProperties->AddRef();

    pProperties->SetPropertyULONG32("ElementWithinTag", elementWithinTag);
    pProperties->SetPropertyULONG32("Delay", m_ulDelay);

    if (m_bHasExplicitDur)
    {
	pProperties->SetPropertyULONG32("Duration", m_ulDuration);
    }

    return rc;
}


void
CSmilElement::checkElementFillBehavior()
{
    if (m_pTimelineElement)
    {
	m_pTimelineElement->checkElementFillBehavior();
    }
}



/*
 * CSmilRootLayout methods
 */
CSmilRootLayout::CSmilRootLayout(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_dWidth               = 0.0;
    m_eWidthType           = CSS2TypeAuto;
    m_dHeight              = 0.0;
    m_eWidthType           = CSS2TypeAuto;
    m_ulBackgroundColor    = 0;
    m_eBackgroundColorType = CSS2TypeTransparent;
    m_eResizeBehavior      = ResizeZoom;
    m_eContextWindow       = ContextWindowAuto;
}

CSmilRootLayout::~CSmilRootLayout()
{
}

HX_RESULT
CSmilRootLayout::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleRootLayout(this);
    }
    return rc;
}


/*
 * CSmilRegion methods
 */

CSmilRegion::CSmilRegion(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_Rect.m_dLeft         = 0.0;
    m_Rect.m_eLeftType     = CSS2TypeAuto;
    m_Rect.m_dTop          = 0.0;
    m_Rect.m_eTopType      = CSS2TypeAuto;
    m_Rect.m_dRight        = 0.0;
    m_Rect.m_eRightType    = CSS2TypeAuto;
    m_Rect.m_dBottom       = 0.0;
    m_Rect.m_eBottomType   = CSS2TypeAuto;
    m_Rect.m_dWidth        = 0.0;
    m_Rect.m_eWidthType    = CSS2TypeAuto;
    m_Rect.m_dHeight       = 0.0;
    m_Rect.m_eHeightType   = CSS2TypeAuto;
    m_lZIndex              = 0;
    m_eZIndexType          = CSS2TypeAuto;
    m_eFit                 = FitHidden;
    m_eShowBackground      = ShowBackgroundAlways;
    m_ulBackgroundColor    = 0xFF000000;
    m_eBackgroundColorType = CSS2TypeTransparent;
    m_dSoundLevel          = 100.0;
    m_bRegionNameSpecified = FALSE;
}

CSmilRegion::~CSmilRegion()
{
}

HX_RESULT
CSmilRegion::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleRegion(this);
    }
    return rc;
}

CSmilRegPoint::CSmilRegPoint(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_RegPoint.m_dLeft       = 0.0;
    m_RegPoint.m_eLeftType   = CSS2TypeAuto;
    m_RegPoint.m_dTop        = 0.0;
    m_RegPoint.m_eTopType    = CSS2TypeAuto;
    m_RegPoint.m_dRight      = 0.0;
    m_RegPoint.m_eRightType  = CSS2TypeAuto;
    m_RegPoint.m_dBottom     = 0.0;
    m_RegPoint.m_eBottomType = CSS2TypeAuto;
    m_RegPoint.m_eRegAlign   = RegAlignTopLeft;
}

CSmilRegPoint::~CSmilRegPoint()
{
}

HX_RESULT
CSmilRegPoint::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleRegPoint(this);
    }
    return rc;
}

#if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT)

CSmilViewport::CSmilViewport(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_ulBackgroundColor    = 0;
    m_eBackgroundColorType = CSS2TypeTransparent;
    m_dWidth               = 0.0;
    m_eWidthType           = CSS2TypeAuto;
    m_dHeight              = 0.0;
    m_eHeightType          = CSS2TypeAuto;
    m_eOpen                = ViewportOpenOnStart;
    m_eClose               = ViewportCloseOnRequest;
    m_eResizeBehavior      = ResizeZoom;
    m_eContextWindow       = ContextWindowAuto;
}

CSmilViewport::~CSmilViewport()
{
}

HX_RESULT
CSmilViewport::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleViewport(this);
    }
    return rc;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_MULTIWINDOWLAYOUT) */

/*
 * CSmilMeta methods
 */
CSmilMeta::CSmilMeta(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilMeta::~CSmilMeta()
{
}

HX_RESULT
CSmilMeta::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleMeta(this);
    }
    return rc;
}

/*
 * CSmilMetadata methods
 */
CSmilMetadata::CSmilMetadata(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilMetadata::~CSmilMetadata()
{
}

HX_RESULT
CSmilMetadata::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleMetadata(this);
    }
    return rc;
}

/*
 * CSmilRendererPreFetch methods
 */
CSmilRendererPreFetch::CSmilRendererPreFetch(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilRendererPreFetch::~CSmilRendererPreFetch()
{
}

HX_RESULT
CSmilRendererPreFetch::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleRendererPreFetch(this);
    }
    return rc;
}

/*
 * CSmilEndLayout methods
 */
CSmilEndLayout::CSmilEndLayout():
    CSmilElement(NULL)
{
}

CSmilEndLayout::~CSmilEndLayout()
{
}


HX_RESULT
CSmilEndLayout::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleEndLayout(this);
    }
    return rc;
}

/*
 * CSmilMetaValues methods
 */
CSmilMetaValues::CSmilMetaValues():
    CSmilElement(NULL),
    m_pValues(0)
{
}

CSmilMetaValues::~CSmilMetaValues()
{
    HX_RELEASE(m_pValues);
}


/*
 * CSmilCustomTest methods:
 */
CSmilCustomTest::CSmilCustomTest(SMILNode* pNode) :
    CSmilElement(pNode)
    , m_bDefaultState(FALSE)
    , m_bOverrideVisible(FALSE)
{
}

CSmilCustomTest::~CSmilCustomTest()
{
}


/*
 * CSmilSource methods
 */

CSmilSource::CSmilSource(SMILNode* pNode):
    CSmilElement(pNode)
{
    m_ulColor                       = 0;
    m_eColorType                    = CSS2TypeTransparent;
    m_Rect.m_dLeft                  = 0.0;
    m_Rect.m_eLeftType              = CSS2TypeAuto;
    m_Rect.m_dTop                   = 0.0;
    m_Rect.m_eTopType               = CSS2TypeAuto;
    m_Rect.m_dRight                 = 0.0;
    m_Rect.m_eRightType             = CSS2TypeAuto;
    m_Rect.m_dBottom                = 0.0;
    m_Rect.m_eBottomType            = CSS2TypeAuto;
    m_Rect.m_dWidth                 = 0.0;
    m_Rect.m_eWidthType             = CSS2TypeAuto;
    m_Rect.m_dHeight                = 0.0;
    m_Rect.m_eHeightType            = CSS2TypeAuto;
    m_lZIndex                       = 0;
    m_eZIndexType                   = CSS2TypeAuto;
    m_eFit                          = FitHidden;
    m_bFitSpecified                 = FALSE;
    m_ulBackgroundColor             = 0;
    m_eBackgroundColorType          = CSS2TypeTransparent;
    m_bRegPointIsPredef             = TRUE;
    m_ePredefRegPoint               = RegAlignTopLeft;
    m_eRegAlign                     = RegAlignTopLeft;
    m_bRegAlignSpecified            = FALSE;
    m_bBackgroundOpacitySpecified   = FALSE;
    m_ulBackgroundOpacity           = 255;
    m_bMediaOpacitySpecified        = FALSE;
    m_ulMediaOpacity                = 255;
    m_bChromaKeySpecified           = FALSE;
    m_ulChromaKey                   = 0;
    m_ulChromaKeyTolerance          = 0;
    m_ulChromaKeyOpacity            = 0;
    m_bAudioDeviceReflushHintNeeded = FALSE;
    m_eHandledBy                    = HandledByAuto;
    m_bBackgroundColorSpecified     = FALSE;
    m_ulLinkTargetDestnLevel_pct    = 100;
    m_ulLinkTargetSourceLevel_pct   = 100;
    m_ulPrefetchAmount              = 0;
    m_typeOfPrefetchAmount          = PrefetchUnknown;

}

CSmilSource::~CSmilSource()
{
}

HX_RESULT
CSmilSource::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleSource(this);
    }
    return rc;
}

void
CSmilSource::setRange(const char* pRange)
{
    // parse smpte/npt ranges:
    //    smpte:12:03:14.21-12:15:26.03
    //	  npt:3:02.5-4:38.6
    //

    char* pPtr = (char *)strstr(pRange, "npt:");
    if(pPtr)
    {
	pPtr += 4;	// point to beginning of range
	char* pHyphen = (char *)strchr(pRange, '-');	// find '-'
	if(pHyphen > pPtr)
	{
	    NPTime beginTime((const char*)CHXString(pPtr, pHyphen-pPtr));
	    m_ulAuthoredClipBegin = (UINT32)beginTime;
	    m_ulClipBegin = m_ulAuthoredClipBegin;
	    if(strlen(pHyphen+1) > 0)
	    {
		NPTime endTime((const char*)CHXString(pHyphen+1));
		m_ulClipEnd = (UINT32)endTime;
	    }
	}
	else if(pHyphen)
	{
	    NPTime endTime((const char*)CHXString(pHyphen+1));
	    m_ulClipEnd = (UINT32)endTime;
	}
    }
    else
    {
	pPtr = (char *)strstr(pRange, "smpte:");
	if(pPtr)
	{
	    pPtr += 6;	// point to beginning of range
	    char* pHyphen = (char *)strchr(pRange, '-');	// find '-'
	    if(pHyphen > pPtr)
	    {
		SMPTETimeCode beginTime = 
		    ((const char*)CHXString(pPtr, pHyphen-pPtr));
		m_ulAuthoredClipBegin = (UINT32)beginTime;
		m_ulClipBegin = m_ulAuthoredClipBegin;
		if(strlen(pHyphen+1) > 0)
		{
		    SMPTETimeCode endTime =
			((const char*)CHXString(pPtr, pHyphen-pPtr));
		    m_ulClipEnd = (UINT32)endTime;
		}
	    }
	    else if(pHyphen)
	    {
		SMPTETimeCode endTime((const char*)CHXString(pHyphen+1));
		m_ulClipEnd = (UINT32)endTime;
	    }
	}
    }
}

/*
 * CSmilSourceUpdate methods
 */

CSmilSourceUpdate::CSmilSourceUpdate():
    CSmilElement(NULL)
{
    m_updateTag = UpdateUnknown;
    m_ulUpdatedDuration = 0;
    m_bDurationIsPureOfDelay = TRUE;
    m_ulUpdatedDelay = 0;
}

CSmilSourceUpdate::~CSmilSourceUpdate()
{
}

HX_RESULT
CSmilSourceUpdate::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleSourceUpdate(this);
    }
    return rc;
}

#if defined(HELIX_FEATURE_SMIL2_ANIMATION)

// CSmilAnimateElement methods
CSmilAnimateElement::CSmilAnimateElement(SMILNode* pNode)
    : CSmilElement(pNode)
{
    // Set defaults
    m_ulSimpleDuration          = ((UINT32) -1);
    m_ulActiveDuration          = ((UINT32) -1);
    m_ulADNoSpeed               = ((UINT32) -1);
    m_pTargetElementID          = NULL;
    m_eTargetElementTag         = SMILUnknown;
    m_ulNumValues               = 0;
    m_ppValue                   = NULL;
    m_ucAttributeName           = kAttrNameUnknown;
    m_ucCalcMode                = kCalcModeLinear;
    m_ucAccumulate              = kAccumulateNone;
    m_ucAdditive                = kAdditiveReplace;
    m_ucAnimationType           = kAnimTypeValues;
    m_dRepeatCount              = 1.0;
    m_ulRepeatDur               = ((UINT32) -1);
    m_bRepeatDurIsIndefinite    = FALSE;
    m_dAccelerate               = 0.0;
    m_dDecelerate               = 0.0;
    m_bAutoReverse              = FALSE;
    m_dSpeed                    = 1.0;
    m_bIndefiniteSimpleDuration = FALSE;
    m_bIndefiniteActiveDuration = FALSE;
    m_bCancelAnimation          = FALSE;
#if defined(XXXMEH_SPLINE_ANIMATION)
    m_ulNumKeyTimes             = 0;
    m_pdKeyTime                 = NULL;
    m_ulNumKeySplines           = 0;
    m_pKeySpline                = NULL;
    m_ulNumPathCmds             = 0;
    m_ppPathCmd                 = NULL;
#endif
}

CSmilAnimateElement::~CSmilAnimateElement()
{
    HX_DELETE(m_pTargetElementID);
    if (m_ppValue)
    {
        for (UINT32 i = 0; i < m_ulNumValues; i++)
        {
            HX_DELETE(m_ppValue[i]);
        }
        HX_VECTOR_DELETE(m_ppValue);
    }
#if defined(XXXMEH_SPLINE_ANIMATION)
    HX_VECTOR_DELETE(m_pdKeyTime);
    HX_VECTOR_DELETE(m_pKeySpline);
    if (m_ppPathCmd && m_ulNumPathCmds)
    {
        for (UINT32 i = 0; i < m_ulNumPathCmds; i++)
        {
            HX_DELETE(m_ppPathCmd[i]);
        }
    }
    HX_VECTOR_DELETE(m_ppPathCmd);
#endif
}

HX_RESULT CSmilAnimateElement::handleElement()
{
    HX_RESULT retVal = HXR_OK;

    if(m_pHandler)
    {
	retVal = m_pHandler->handleAnimate(this);
    }

    return retVal;
}

HX_RESULT CSmilAnimateElement::getCurrentScheduledStopTime(REF(ULONG32) ulActualStopTime)
{
    HX_RESULT ret = HXR_OK;

    if (m_ulDelay != ((UINT32) -1))
    {
        if (m_bIndefiniteActiveDuration)
        {
            ulActualStopTime = SMILTIME_INFINITY;
        }
        else
        {
            ulActualStopTime = m_ulDelay + m_ulActiveDuration;
        }
    }
    else
    {
        ret = HXR_NOT_INITIALIZED;
    }

    return ret;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_ANIMATION) */

CSmilParamElement::CSmilParamElement(SMILNode* pNode)
    : CSmilElement(pNode)
{
    m_pName     = NULL;
    m_pValue    = NULL;
    m_eDelivery = DeliveryClient;
}

CSmilParamElement::~CSmilParamElement()
{
    HX_RELEASE(m_pName);
    HX_RELEASE(m_pValue);
}

/*
 * CSmilParElement methods
 */

CSmilParElement::CSmilParElement(SMILNode* pNode):
    CSmilElement(pNode)
{
}

CSmilParElement::~CSmilParElement()
{
}

/*
 * CSmilSeqElement methods
 */
CSmilSeqElement::CSmilSeqElement(SMILNode*  pNode):
    CSmilElement(pNode)
{
}

CSmilSeqElement::~CSmilSeqElement()
{
}

/*
 * CSmilExclElement methods
 */

CSmilExclElement::CSmilExclElement(SMILNode* pNode):
    CSmilElement(pNode)
    , m_pPauseStack(NULL)
{
}

CSmilExclElement::~CSmilExclElement()
{
    HX_DELETE(m_pPauseStack);
}



/*
 * CSmilPriorityClassElement methods
 */

CSmilPriorityClassElement::CSmilPriorityClassElement(SMILNode* pNode):
	CSmilElement(pNode)
	, m_pauseDisplay(SMILPriorityClassPauseDisplayShow)
	, m_peers(SMILPriorityClassStop)
	, m_higher(SMILPriorityClassPause)
	, m_lower(SMILPriorityClassDefer)
{
}

CSmilPriorityClassElement::~CSmilPriorityClassElement()
{
}



/*
 * CSmilAAnchor methods
 */

CSmilAAnchorElement::CSmilAAnchorElement(SMILNode* pNode):
    CSmilElement(pNode),
    m_show("replace")
    , m_ulSourceLevel_pct(100)
    , m_ulDestinationLevel_pct(100)
    // /XXXEH- TODO: make sure that, if show="new", this changes to play:
    , m_sourcePlaystate(SMILLinkPlaystatePause)
    , m_destinationPlaystate(SMILLinkPlaystatePlay)
    , m_bExternal(FALSE)
    , m_actuate("onRequest")
    , m_accesskey("") // /(an ISO10646 character.  Default unspecified)
    , m_ulTabindex ((UINT32)-1)
    , m_target("")
    , m_bTargetIsARegion(FALSE)
    , m_bTimeValueSet(FALSE)
    , m_lAnchorBeginOffset(0)
    , m_bAnchorBeginOffsetSet(FALSE)
    , m_lAnchorEndOffset(0)
    , m_bAnchorEndOffsetSet(FALSE)
    , m_ulAnchorDuration((UINT32)-1)
    , m_ulAnchorMinActiveDur(0)
    , m_ulAnchorMaxActiveDur((UINT32)-1)
{
}

CSmilAAnchorElement::~CSmilAAnchorElement()
{
}

BOOL
CSmilAAnchorElement::isCurrentLink(UINT32 ulTimeOffset,
				   UINT32 ulXOffset, UINT32 ulYOffset,
				   HXxRect regionRect,
				   // /These 3 are ignored for <a> links:
				   BOOL bResizeBehaviorIsZoom,
				   double dZoomX, double dZoomY)
{
    BOOL rc = TRUE;
    ULONG32 ulBeginRelativeToSyncBase = (UINT32)-1;

    // coords only - check for out of bounds region
    UINT32 ulRegionWidth = regionRect.right - regionRect.left;
    UINT32 ulRegionHeight = regionRect.bottom - regionRect.top;

    if(ulXOffset > ulRegionWidth ||
	ulYOffset > ulRegionHeight)
    {
	if (ANCHOR_POS_DONTCARE != ulXOffset  &&
		ANCHOR_POS_DONTCARE != ulYOffset)
	{
	    rc = FALSE;
	    goto exit;
	}
    }

    if(m_href.GetLength() == 0)
    {
	rc = FALSE;
    }
    // /In SMIL 2.0, "a" links can also be timed, so check time offset:
    else if (!isLinkActiveAtTime(ulTimeOffset, ulBeginRelativeToSyncBase))
    {
	rc = FALSE;
    }

exit:
    return rc;
}


BOOL
CSmilAAnchorElement::isLinkActiveAtTime(UINT32 ulTimeOffset,
					REF(ULONG32)rulAnchorRelativeStartTime)
{
    BOOL bIsActiveAtTimeOffset = TRUE;
    rulAnchorRelativeStartTime = (UINT32)-1;
    if(m_bTimeValueSet)
    {
	bIsActiveAtTimeOffset = FALSE;

	BOOL bValidDelay = m_ulDelay != (UINT32)-1;

	if(m_bAnchorBeginOffsetSet  ||  bValidDelay)
	{
	    if((m_bAnchorBeginOffsetSet  &&
		    (INT32)ulTimeOffset >= m_lAnchorBeginOffset)  ||
		    (bValidDelay  &&  ulTimeOffset >= m_ulDelay) ) 
	    {
		// /For PR 67170 fix, caller needs anchor's begin time to tell
		// which of multiple, active onLoad anchors to use after a seek:
		if (m_bAnchorBeginOffsetSet  &&
		    (INT32)ulTimeOffset >= m_lAnchorBeginOffset)
		{
		    rulAnchorRelativeStartTime = m_lAnchorBeginOffset;
		}
		else
		{
		    rulAnchorRelativeStartTime = m_ulDelay;
		}

		LONG32 lAnchorEndTime = -1;
		if (m_bAnchorEndOffsetSet)
		{
		    lAnchorEndTime = m_lAnchorEndOffset;
		}
		else if ((UINT32)-1 != m_ulAnchorDuration)
		{
		    lAnchorEndTime = (INT32)m_ulAnchorDuration;
		    if (m_bAnchorBeginOffsetSet)
		    {
			lAnchorEndTime += m_lAnchorBeginOffset;
		    }
		}

		if(-1 != lAnchorEndTime)
		{
		    if((INT32)ulTimeOffset <= lAnchorEndTime)
		    {
			bIsActiveAtTimeOffset = TRUE;
		    }
		}
		else
		{
		    bIsActiveAtTimeOffset = TRUE;
		}
	    }
	}
	else if(m_bAnchorEndOffsetSet)
	{
	    if((INT32)ulTimeOffset <= m_lAnchorEndOffset)
	    {
		bIsActiveAtTimeOffset = TRUE;
	    }
	}
    }
    return bIsActiveAtTimeOffset;
}


BOOL
CSmilAnchorElement::isLinkActiveAtTime(UINT32 ulTimeOffset,
				       REF(ULONG32)rulAnchorRelativeStartTime)
{
    BOOL bIsActiveAtTimeOffset = TRUE;
    if(m_bTimeValueSet)
    {
	bIsActiveAtTimeOffset = FALSE;

	//[SMIL 1.0 compliance] the following variable and its use, below,
	// helps fix PR 26471.  We may have had only our delay set:
	BOOL bValidDelay = m_ulDelay != (UINT32)-1;

	if(m_bBeginOffsetSet  ||  bValidDelay)
	{
	    // /First, we need to make sure we're in the same time-coordinate
	    // system as our syncbase (parent):
	    ULONG32 ulTimeOffsetFromSyncBase = m_ulDelay;
	    if (m_bCurBeginIsOffsetFromSyncBase)
	    {
		ulTimeOffsetFromSyncBase =
			m_ulBeginOffsetFromSyncBase;
	    }

	    if ((m_bBeginOffsetSet  &&
		(INT32)ulTimeOffset >= m_lBeginOffset)  ||
		(bValidDelay  &&  ulTimeOffset >= ulTimeOffsetFromSyncBase) )
	    {
		// /For PR 67170 fix, caller needs anchor's begin time to tell
		// which of multiple, active onLoad anchors to use after a seek:
		if (m_bBeginOffsetSet  &&
		    (INT32)ulTimeOffset >= m_lBeginOffset)
		{
		    rulAnchorRelativeStartTime = m_lBeginOffset;
		}
		else
		{
		    rulAnchorRelativeStartTime = ulTimeOffsetFromSyncBase;
		}

		LONG32 lAnchorEndTime = -1;
		if (m_bEndOffsetSet)
		{
		    lAnchorEndTime = m_lEndOffset;
		}
		else if ((UINT32)-1 != m_ulDuration)
		{
		    lAnchorEndTime = (INT32)m_ulDuration;
		    if (m_bBeginOffsetSet)
		    {
			lAnchorEndTime += m_lBeginOffset;
		    }
		}

		if(-1 != lAnchorEndTime)
		{
		    if((INT32)ulTimeOffset <= lAnchorEndTime)
		    {
			bIsActiveAtTimeOffset = TRUE;
		    }
		}
		else
		{
		    bIsActiveAtTimeOffset = TRUE;
		}
	    }
	}
	else if(m_bEndOffsetSet)
	{
	    if((INT32)ulTimeOffset <= m_lEndOffset)
	    {
		bIsActiveAtTimeOffset = TRUE;
	    }
	}
    }
    return bIsActiveAtTimeOffset;
}


void
CSmilAAnchorElement::rescale(double dXScale, double dYScale,
			     BOOL bResetOriginalCoords)
{
    // nothing to do
}


/*
 * CSmilAnchor methods
 */

CSmilAnchorElement::CSmilAnchorElement(SMILNode* pNode):
    CSmilAAnchorElement(pNode),
    m_zIndex(0),
    m_bCoordsSet(FALSE),
    m_ulLeftX(0),
    m_ulOriginalLeftX(0),
    m_bLeftXIsPercent(FALSE),
    m_ulTopY(0),
    m_ulOriginalTopY(0),
    m_bTopYIsPercent(FALSE),
    m_ulRightX((UINT32)-1),
    m_ulOriginalRightX((UINT32)-1),
    m_bRightXIsPercent(FALSE),
    m_ulBottomY((UINT32)-1),
    m_ulOriginalBottomY((UINT32)-1),
    m_bBottomYIsPercent(FALSE),
    m_ulRadius((UINT32)-1),
    m_ulOriginalRadius((UINT32)-1),
    m_bRadiusIsPercent(FALSE)
    , m_pVertexArray(NULL)
    , m_pOriginalVertexArray(NULL)
    , m_uiNumPoints(0)
{
}

CSmilAnchorElement::~CSmilAnchorElement()
{
    deleteVertexArrays();
}

void CSmilAnchorElement::deleteVertexArrays()
{
    if (m_pVertexArray)
    {
	delete [] m_pVertexArray;
	m_pVertexArray = NULL;
    }
    if (m_pOriginalVertexArray)
    {
	delete [] m_pOriginalVertexArray;
	m_pOriginalVertexArray = NULL;
    }
}

void
CSmilAnchorElement::rescale(double dXScale, double dYScale,
			     BOOL bResetOriginalCoords)
{
    if(bResetOriginalCoords)
    {
	m_ulLeftX = m_ulOriginalLeftX;
	m_ulRightX = m_ulOriginalRightX;
	m_ulTopY = m_ulOriginalTopY;
	m_ulBottomY = m_ulOriginalBottomY;
	m_ulRadius = m_ulOriginalRadius;

	if (m_pVertexArray)// /is poly shape:
	{
	    for (int i=0; i < m_uiNumPoints; i++)
	    {
		m_pVertexArray[i].m_lX = m_pOriginalVertexArray[i].m_lX;
		m_pVertexArray[i].m_lY = m_pOriginalVertexArray[i].m_lY;
	    }
	}
    }
    else if (!m_pVertexArray)
    {
	if(!m_bLeftXIsPercent)
	{
	    m_ulLeftX = (UINT32)(dXScale * (double)m_ulLeftX);
	}
	if(!m_bRightXIsPercent)
	{
	    m_ulRightX = (UINT32)(dXScale * (double)m_ulRightX);
	}
	if(!m_bTopYIsPercent)
	{
	    m_ulTopY = (UINT32)(dYScale * (double)m_ulTopY);
	}
	if(!m_bBottomYIsPercent)
	{
	    m_ulBottomY = (UINT32)(dYScale * (double)m_ulBottomY);
	}
	if(!m_bRadiusIsPercent)
	{
	    // /XXXEH- TODO: don't just average x&y scaling, but
	    // make an elipse by keeping an x-radius and a y-radius val
	    // and then use a formula for an elipse in the hit-test code
	    // in isCurrentLink():
	    m_ulRadius = (UINT32)(((dXScale+dYScale)/2.0) *
		    (double)m_ulRadius);
	}
    }
    else // /is "poly" (polygon) shape:
    {
	for (int i=0; i < m_uiNumPoints; i++)
	{
	    m_pVertexArray[i].m_lX =
		    (LONG32)((double)((float)m_pVertexArray[i].m_lX) * dXScale);
	    m_pVertexArray[i].m_lY =
		    (LONG32)((double)((float)m_pVertexArray[i].m_lY) * dYScale);
	}
    }
}

BOOL
CSmilAnchorElement::isCurrentLink(UINT32 ulTimeOffset,
				   UINT32 ulXOffset, UINT32 ulYOffset,
				   HXxRect regionRect,
				   BOOL bResizeBehaviorIsZoom,
				   double dZoomX, double dZoomY)
{
    BOOL bRetval = TRUE;

    UINT32 ulRegionWidth = regionRect.right - regionRect.left;
    UINT32 ulRegionHeight = regionRect.bottom - regionRect.top;

    if(m_href.GetLength() == 0)
    {
	return FALSE;
    }

    ULONG32 ulBeginRelativeToSyncBase = (UINT32)-1;
    // first check time offset:
    if (!isLinkActiveAtTime(ulTimeOffset, ulBeginRelativeToSyncBase))
    {
	return FALSE;
    }

    if(m_bCoordsSet  &&  (!m_shape.GetLength()  ||  m_shape=="rect"))
    {
	UINT32 ulLeftX = m_ulLeftX;
	UINT32 ulRightX = m_ulRightX;
	UINT32 ulTopY = m_ulTopY;
	UINT32 ulBottomY = m_ulBottomY;

	// /Doing this resizing of absolute (not percent) coordinate values
	// fixes PR 77231; the hotspot should scale along with the media
	// when the player is resized:
	// (NOTE: This only fixes "rect" version of PR 77231):
	if (bResizeBehaviorIsZoom  &&  dZoomX != 1.0  && dZoomY != 1.0)
	{
	    if (!m_bLeftXIsPercent)
	    {
		ulLeftX *= dZoomX;
	    }
	    if (!m_bRightXIsPercent)
	    {
		ulRightX *= dZoomX;
	    }
	    if (!m_bTopYIsPercent)
	    {
		ulTopY *= dZoomX;
	    }
	    if (!m_bBottomYIsPercent)
	    {
		ulBottomY *= dZoomX;
	    }
	}

	// normalize to pixel coords
	if(m_bLeftXIsPercent)
	{
	    ulLeftX = (UINT32)(((double)m_ulLeftX/100.0) * ulRegionWidth);
	}
	if(m_bRightXIsPercent)
	{
	    ulRightX = (UINT32)(((double)m_ulRightX/100.0) * ulRegionWidth);
	}
	if(m_bTopYIsPercent)
	{
	    ulTopY = (UINT32)(((double)m_ulTopY/100.0) * ulRegionHeight);
	}
	if(m_bBottomYIsPercent)
	{
	    ulBottomY = (UINT32)(((double)m_ulBottomY/100.0) * ulRegionHeight);
	}

	if(ulXOffset >= ulLeftX &&
	   ulXOffset <= ulRightX &&
	   ulYOffset >= ulTopY &&
	   ulYOffset <= ulBottomY)
	{
	    return TRUE;
	}
	else
	{
	    if (ANCHOR_POS_DONTCARE != ulXOffset  &&
		    ANCHOR_POS_DONTCARE != ulYOffset)
	    {
		return FALSE;
	    }
	    return TRUE;
	}
    }
    else if(m_bCoordsSet  &&  (m_shape=="circle"))
    {
	UINT32 ulCenterX = m_ulLeftX;
	UINT32 ulCenterY = m_ulTopY;
	UINT32 ulRadius = m_ulRadius;

	// /Doing this resizing of absolute (not percent) coordinate values
	// fixes "circle" version of PR 77231; the hotspot should scale along
	// with the media when the player is resized:
	if (bResizeBehaviorIsZoom  &&  dZoomX != 1.0  && dZoomY != 1.0)
	{
	    if (!m_bLeftXIsPercent)
	    {
		ulCenterX *= dZoomX;
	    }
	    if (!m_bTopYIsPercent)
	    {
		ulCenterY *= dZoomX;
	    }
	    if (!m_bRadiusIsPercent)
	    {
		ulRadius *= dZoomY;
	    }
	}

	// normalize to pixel coords
	if(m_bLeftXIsPercent)
	{
	    ulCenterX = (UINT32)(((double)ulCenterX/100.0) * ulRegionWidth);
	}
	if(m_bTopYIsPercent)
	{
	    ulCenterY = (UINT32)(((double)ulCenterY/100.0) * ulRegionHeight);
	}
	if(m_bRadiusIsPercent)
	{
	    ulRadius = (UINT32)(((double)m_ulRadius/100.0) * ulRegionHeight);
	}

	double dXDistanceToCenter = double( (ulXOffset>ulCenterX)?
		ulXOffset-ulCenterX : ulCenterX-ulXOffset);
	double dYDistanceToCenter = double( (ulYOffset>ulCenterY)?
		ulYOffset-ulCenterY : ulCenterY-ulYOffset);
	// /Compare squared distances rather than doing expensive sqare root:
	double dDistanceToCenterSquared =
		(dXDistanceToCenter*dXDistanceToCenter) +
		(dYDistanceToCenter*dYDistanceToCenter);
	double dRadiusSquared = double(ulRadius*ulRadius);
	// /It's within the circle if it's within the radius:
	if (dDistanceToCenterSquared <= dRadiusSquared)
	{
	    return TRUE;
	}
	else
	{
	    if (ANCHOR_POS_DONTCARE != ulXOffset  &&
		    ANCHOR_POS_DONTCARE != ulYOffset)
	    {
		return FALSE;
	    }
	    return TRUE;
	}
    }
    else if (m_bCoordsSet  &&  (m_shape=="poly"))
    {
	bRetval = isPointInPolygon(ulXOffset, ulYOffset, regionRect,
		bResizeBehaviorIsZoom? dZoomX : 1.0,
		bResizeBehaviorIsZoom? dZoomY : 1.0);
    }
    else if(ulXOffset > ulRegionWidth ||
	    ulYOffset > ulRegionHeight)	    
    // assume time OK, coords OK if within bounds of region
    {
	if (ANCHOR_POS_DONTCARE != ulXOffset  &&
		ANCHOR_POS_DONTCARE != ulYOffset)
	{
	    return FALSE;
	}
    }

    return bRetval;
}


BOOL
CSmilAnchorElement::isPointInPolygon(INT32 lXOffset, INT32 lYOffset,
				     HXxRect regionRect,
				     double dZoomX, double dZoomY)
{
    INT32 xnew,ynew;
    INT32 xold,yold;
    INT32 x1,y1;
    INT32 x2,y2;
    BOOL    bIsInside=FALSE;

    UINT32 ulRegionWidth = regionRect.right - regionRect.left;
    UINT32 ulRegionHeight = regionRect.bottom - regionRect.top;

    // We must have at least 3 points to have a polygon
    if (m_uiNumPoints >= 3)
    {
        xold = m_pVertexArray[m_uiNumPoints-1].m_lX;
        yold = m_pVertexArray[m_uiNumPoints-1].m_lY;

	// /Convert percentages to coords:
	if(m_pVertexArray[m_uiNumPoints-1].m_bXIsPercent)
	{
	    xold = (UINT32)(((double)xold/100.0) * ulRegionWidth);
	}
	// /NOTE: to fix PR 77231 (poly version) it would have been nice to
	// just apply 1/dZoomX and 1/dZoomY to lXOffset and lYOffset but we
	// can't because some vertices could be percentages while others not,
	// so we're stuck multiplying each non-percent value, one at a time:
	else if (dZoomX != 1.0)
	{
	    xold *= dZoomX; // /Helps fix poly version of PR 77231.
	}
	if(m_pVertexArray[m_uiNumPoints-1].m_bYIsPercent)
	{
	    yold = (UINT32)(((double)yold/100.0) * ulRegionHeight);
	}
	else if (dZoomY != 1.0)
	{
	    yold *= dZoomY; // /Helps fix poly version of PR 77231.
	}

        for (UINT16 i=0; i < m_uiNumPoints; i++)
        {
            xnew = m_pVertexArray[i].m_lX;
            ynew = m_pVertexArray[i].m_lY;

	    // /Convert percentages to coords:
	    if(m_pVertexArray[i].m_bXIsPercent)
	    {
		xnew = (UINT32)(((double)xnew/100.0) * ulRegionWidth);
	    }
	    else if (dZoomX != 1.0)
	    {
		xnew *= dZoomX; // /Helps fix poly version of PR 77231.
	    }
	    if(m_pVertexArray[i].m_bYIsPercent)
	    {
		ynew = (UINT32)(((double)ynew/100.0) * ulRegionHeight);
	    }
	    else if (dZoomY != 1.0)
	    {
		ynew *= dZoomY; // /Helps fix poly version of PR 77231.
	    }

            if (xnew > xold)
            {
                x1 = xold;
                x2 = xnew;
                y1 = yold;
                y2 = ynew;
            }
            else
            {
                x1 = xnew;
                x2 = xold;
                y1 = ynew;
                y2 = yold;
            }

            if ((xnew < lXOffset) == (lXOffset <= xold) &&
                    ((LONG32)lYOffset - (LONG32)y1) * (LONG32)(x2-x1) < 
                    ((LONG32)y2 - (LONG32)y1) * (LONG32)(lXOffset - x1))
            {
                bIsInside = !bIsInside;
            }

            xold = xnew;
            yold = ynew;
        }
    }

    return bIsInside;
}


HX_RESULT
CSmilAnchorElement::convertRawPolyData(const char* pCoords)
{
    HX_RESULT   retval = HXR_OK;

    const char*	pchData = pCoords;
    UINT16	uiNumValues=0;
    UINT16	uiLength=0;
    char*	szBuffer=NULL;
    UINT16	uiX=0;
    UINT16	uiY=0;

    if (!pCoords  ||  strlen(pCoords) < 3)
    {
	retval = HXR_INVALID_PARAMETER;
    }
    else
    {
	UINT16 uiNumCommas = 0;
	// /First, get number of points:
	while (*pchData)
	{
	    if (',' == *pchData)
	    {
		uiNumCommas++;
	    }
	    pchData++;
	};

	// /There *must* be an odd # of commas in order to have x,y pairs:
	if (0 == (uiNumCommas % 2))
	{
	    retval = HXR_INVALID_PARAMETER;
	}
	// /Can't have poly with fewer than 3 pts.  It's not an error, but
	// it's useless so we ignore it:
	else if (uiNumCommas < 5)
	{
	    retval = HXR_OK;
	}
	else
	{
	    deleteVertexArrays();

	    // /Get polygon values: (x1,y1,x2,y2,...xn,yn)
	    m_uiNumPoints = (uiNumCommas+1) / 2;
	    m_pVertexArray = new CSmilAnchorVertex[m_uiNumPoints];
	    m_pOriginalVertexArray = new CSmilAnchorVertex[m_uiNumPoints];

	    if (!m_pVertexArray  ||  !m_pOriginalVertexArray)
	    {
		retval = HXR_OUTOFMEMORY;
	    }
	    else
	    {
		retval = HXR_OK;
		int i = 0;
		char* pCoordCopy = new_string(pCoords);
		char* pTok = strtok(pCoordCopy, ",");
		for(i=0; i<m_uiNumPoints,pTok; ++i)
		{
		    char* pEndPtr = 0;
		    // /First, get x coord:
		    double dVal = strtod(pTok, &pEndPtr);
		    m_pOriginalVertexArray[i].m_lX = m_pVertexArray[i].m_lX =
			    (INT32)((float)dVal);
		    m_pOriginalVertexArray[i].m_bXIsPercent =
			    m_pVertexArray[i].m_bXIsPercent =
			    (*pEndPtr == '%') ? TRUE: FALSE;

		    pTok = strtok(NULL, ",");

		    // /Next, get y coord:
		    dVal = strtod(pTok, &pEndPtr);
		    m_pOriginalVertexArray[i].m_lY = m_pVertexArray[i].m_lY =
			    (INT32)((float)dVal);
		    m_pOriginalVertexArray[i].m_bYIsPercent =
			    m_pVertexArray[i].m_bYIsPercent =
			    (*pEndPtr == '%') ? TRUE: FALSE;

		    pTok = strtok(NULL, ",");
		} // end for(i...
		delete [] pCoordCopy;
	    }
	}
    }

    return retval;
}





/*
 * CSmilAddGroup methods
 */

CSmilAddGroup::CSmilAddGroup():
    CSmilElement(NULL),
    m_nGroup(0),
    m_pValues(NULL),
    m_nTotalTracks(0),
    m_nInitTracks(0),
    m_ulDuration((UINT32)-1)
{
}

CSmilAddGroup::~CSmilAddGroup()
{
    HX_RELEASE(m_pValues);
}

HX_RESULT
CSmilAddGroup::handleElement()
{
    HX_RESULT rc = HXR_OK;
    if(m_pHandler)
    {
	rc = m_pHandler->handleAddGroup(this);
    }
    return rc;
}

#if defined(HELIX_FEATURE_SMIL2_TRANSITIONS)

CSmilTransition::CSmilTransition(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_dStartProgress = 0.0;
    m_dEndProgress   = 1.0;
    m_eDirection     = TransitionDirectionForward;
    m_ulFadeColor    = 0x00000000; // black
    m_ulHorzRepeat   = 1;
    m_ulVertRepeat   = 1;
    m_ulBorderWidth  = 0;
    m_ulBorderColor  = 0;
    m_bBlendBorder   = FALSE;
}

CSmilTransition::~CSmilTransition()
{
}

HX_RESULT CSmilTransition::handleElement()
{
    HX_RESULT retVal = HXR_OK;

    if(m_pHandler)
    {
        retVal = m_pHandler->handleTransition(this);
    }

    return retVal;
}

#endif /* #if defined(HELIX_FEATURE_SMIL2_TRANSITIONS) */

CSmilBodyElement::CSmilBodyElement(SMILNode* pNode) :
    CSmilElement(pNode)
{
    m_eAccessErrorBehavior = AccessErrorBehaviorContinue;
}

CSmilBodyElement::~CSmilBodyElement()
{
}
