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

// include
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxcom.h"
#include "ihxpckts.h"
#include "hxcomm.h"

// pnmisc
#include "baseobj.h"
#include "unkimp.h"

// pncont
#include "hxstring.h"
#include "hxmap.h"
#include "hxslist.h"
#include "hxbuffer.h"

// pxcomlib
#include "pxcolor.h"
#include "pxrect.h"
#include "pxeffect.h"
#include "rpfile.h"
#include "wirefmgr.h"

// pxff2
#include "pxschedu.h"

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

BEGIN_INTERFACE_LIST(PXScheduler)
END_INTERFACE_LIST

PXScheduler::PXScheduler()
{
    m_pRealPixFile       = NULL;
    m_pWireFormatManager = NULL;
    m_ulMinimumPreroll   = 0;
    m_pEffectItr         = NULL;
    m_ulPacketTypeState  = 0;
    m_lCurrentTimeStamp  = 0;
    m_ulSessionHandle    = 0;
    m_ulNumPackets       = 0;
    m_ulPacketIndex      = 0;
    m_ulKeyScreenTime    = 0;
    m_bSeeking           = FALSE;
}

PXScheduler::~PXScheduler()
{
    Deallocate();
}

void PXScheduler::Deallocate()
{
    HX_RELEASE(m_pRealPixFile);
    HX_RELEASE(m_pWireFormatManager);
}

HX_RESULT PXScheduler::Init(PXRealPixFile*       pRealPixFile,
                            PXWireFormatManager* pWireFormatManager)
{
    HX_RESULT retVal = HXR_OK;

    if (pRealPixFile && pWireFormatManager)
    {
        // Check to make sure the bitrate is non-zero
        if (pRealPixFile->GetBitrate())
        {
            // Clear out everything
            Deallocate();
            m_ulMinimumPreroll   = 0;
            m_pEffectItr         = NULL;
            m_ulPacketTypeState  = 0;
            m_lCurrentTimeStamp  = 0;
            m_ulSessionHandle    = 0;
            m_ulNumPackets       = 0;
            m_ulPacketIndex      = 0;
            m_ulKeyScreenTime    = 0;
            m_bSeeking           = FALSE;

            // Save a copy of the file and wire format objects
            m_pRealPixFile = pRealPixFile;
            m_pRealPixFile->AddRef();
            m_pWireFormatManager = pWireFormatManager;
            m_pWireFormatManager->AddRef();

            // Run forward through the effects, computing the send time
            // for each. If the effect has a target image AND it's the
            // first time this image is used, then the send time is 
            // the time for the effect packet, the image header packet,
            // and the image data packets. Otherwise, the send time
            // is just the time for the effect.
            void* pItr = NULL;
            retVal     = m_pRealPixFile->GetEffectHeadIterator(pItr);
            if (SUCCEEDED(retVal))
            {
                PXEffect* pEffect = NULL;
                HX_RESULT rv      = m_pRealPixFile->GetNextEffect(pItr, pEffect);
                while (SUCCEEDED(retVal) && SUCCEEDED(rv))
                {
                    // Init the send time
                    UINT32 ulTotalSendTime = 0;
                    // Add time for sending the effect itself
                    ulTotalSendTime += GetSendTime(m_pWireFormatManager->GetEffectWireSize(pEffect),
                                                   m_pRealPixFile->GetBitrate());
                    // Do we need to send the image with this effect?
                    if (pEffect->HasTarget() && pEffect->GetFirstUse())
                    {
                        // Yes, we need to send image, so we need to add the size of the image
                        IHXBuffer* pStreamMimeStr = NULL;
                        retVal = m_pRealPixFile->GetImageStreamMimeType(pEffect->GetTarget(), pStreamMimeStr);
                        if (SUCCEEDED(retVal))
                        {
                            UINT32 ulImageFileSize = 0;
                            retVal = m_pRealPixFile->GetImageSize(pEffect->GetTarget(), ulImageFileSize);
                            if (SUCCEEDED(retVal))
                            {
                                ulTotalSendTime += GetSendTime(m_pWireFormatManager->GetImageWireSize(pStreamMimeStr, ulImageFileSize),
                                                               m_pRealPixFile->GetBitrate());
                            }
                        }
                        HX_RELEASE(pStreamMimeStr);
                    }
                    // Save the send time to the effect
                    pEffect->SetSendTime(ulTotalSendTime);
                    // Initialize the start send time for the effect to be the start time for
                    // the effect minus the send time for the effect (and possibly image)
                    pEffect->SetStartSendTime(((INT32) pEffect->GetStart()) - ((INT32) ulTotalSendTime));
                    // Go to the next effect
                    HX_RELEASE(pEffect);
                    rv = m_pRealPixFile->GetNextEffect(pItr, pEffect);
                }
                HX_RELEASE(pEffect);

                // Now we need to run BACKWARDS through the effects and make sure
                // there is no overlap between send times for effects
                if (SUCCEEDED(retVal))
                {
                    retVal = m_pRealPixFile->GetEffectTailIterator(pItr);
                    if (SUCCEEDED(retVal))
                    {
                        PXEffect* pLaterEffect = NULL;
                        rv                     = m_pRealPixFile->GetPrevEffect(pItr, pLaterEffect);
                        if (SUCCEEDED(rv))
                        {
                            HX_RELEASE(pEffect);
                            rv = m_pRealPixFile->GetPrevEffect(pItr, pEffect);
                            while (SUCCEEDED(rv) && SUCCEEDED(retVal))
                            {
                                // Adjust the start send time if necessary
                                INT32 lEndSendTime = pEffect->GetStartSendTime() + ((INT32) pEffect->GetSendTime());
                                if (lEndSendTime > pLaterEffect->GetStartSendTime())
                                {
                                    INT32 lNewStartSendTime = pLaterEffect->GetStartSendTime() - ((INT32) pEffect->GetSendTime());
                                    pEffect->SetStartSendTime(lNewStartSendTime);
                                }
                                // Shift down from later to current
                                HX_RELEASE(pLaterEffect);
                                pLaterEffect = pEffect;
                                pLaterEffect->AddRef();
                                // Get a new effect
                                HX_RELEASE(pEffect);
                                rv = m_pRealPixFile->GetPrevEffect(pItr, pEffect);
                            }
                            HX_RELEASE(pEffect);
                        }

                        if (SUCCEEDED(retVal))
                        {
                            // Set the minimum preroll
                            m_ulMinimumPreroll      = 0;
                            INT32 lFirstEffectStart = pLaterEffect->GetStartSendTime();
                            if (lFirstEffectStart < 0)
                            {
                                lFirstEffectStart  = -lFirstEffectStart;
                                m_ulMinimumPreroll = (UINT32) lFirstEffectStart;
                            }

                            // Initialize the current time stamp
                            m_lCurrentTimeStamp = pLaterEffect->GetStartSendTime();

                            // Initialize the effect iterator and packet type state
                            retVal = Rewind();
                        }
                        HX_RELEASE(pLaterEffect);
                    }
                }
            }
        }
        else
        {
            retVal = HXR_INVALID_PARAMETER;
        }
    }
    else
    {
        retVal = HXR_INVALID_PARAMETER;
    }

    return retVal;
}

BOOL PXScheduler::IsStreamDone() const
{
    BOOL bDone = FALSE;

    if (!m_pEffectItr)
    {
        bDone = TRUE;
    }

    return bDone;
}

HX_RESULT PXScheduler::GetNextPacketInfo(REF(UINT32) rulPacketType)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pEffectItr)
    {
        rulPacketType = m_ulPacketTypeState;
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT PXScheduler::GetEffectInfo(REF(PXEffect*) rpEffect,
                                     REF(UINT32)    rulTimeStamp)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pEffectItr && m_pRealPixFile)
    {
        HX_RELEASE(rpEffect);
        retVal = m_pRealPixFile->GetCurrentEffect(m_pEffectItr, rpEffect);
        if (SUCCEEDED(retVal))
        {
            rulTimeStamp = (UINT32) (m_lCurrentTimeStamp >= 0 ? m_lCurrentTimeStamp : 0);
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT PXScheduler::GetImageHeaderInfo(REF(UINT32)      rulHandle,
                                          REF(UINT32)      rulImageSize,
                                          REF(IHXBuffer*) rpFileMimeStr,
                                          REF(IHXBuffer*) rpFileNameStr,
                                          REF(IHXBuffer*) rpStreamMimeStr,
                                          REF(UINT32)      rulTimeStamp)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pEffectItr && m_pRealPixFile)
    {
        PXEffect* pEffect = NULL;
        retVal            = m_pRealPixFile->GetCurrentEffect(m_pEffectItr, pEffect);
        if (SUCCEEDED(retVal))
        {
            if (pEffect->HasTarget() && pEffect->GetFirstUse())
            {
                UINT32      ulFileSize     = 0;
                IHXBuffer* pFileNameStr   = NULL;
                IHXBuffer* pFileMimeStr   = NULL;
                IHXBuffer* pStreamMimeStr = NULL;
                retVal                     = m_pRealPixFile->GetAllImageInfo(pEffect->GetTarget(),
                                                                             ulFileSize,
                                                                             pFileNameStr,
                                                                             pFileMimeStr,
                                                                             pStreamMimeStr);
                if (SUCCEEDED(retVal))
                {
                    rulHandle    = pEffect->GetTarget();
                    rulImageSize = ulFileSize;
                    HX_RELEASE(rpFileMimeStr);
                    if (pFileMimeStr)
                    {
                        rpFileMimeStr = pFileMimeStr;
                        rpFileMimeStr->AddRef();
                    }
                    HX_RELEASE(rpFileNameStr);
                    if (pFileNameStr)
                    {
                        rpFileNameStr = pFileNameStr;
                        rpFileNameStr->AddRef();
                    }
                    HX_RELEASE(rpStreamMimeStr);
                    if (pStreamMimeStr)
                    {
                        rpStreamMimeStr = pStreamMimeStr;
                        rpStreamMimeStr->AddRef();
                    }
                    rulTimeStamp = (UINT32) (m_lCurrentTimeStamp >= 0 ? m_lCurrentTimeStamp : 0);
                }
                HX_RELEASE(pFileNameStr);
                HX_RELEASE(pFileMimeStr);
                HX_RELEASE(pStreamMimeStr);
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        HX_RELEASE(pEffect);
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT PXScheduler::SetImageDataInfo(UINT32 ulNumPackets, UINT32 ulSessionHandle)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pEffectItr && m_pRealPixFile)
    {
        PXEffect* pEffect = NULL;
        retVal            = m_pRealPixFile->GetCurrentEffect(m_pEffectItr, pEffect);
        if (SUCCEEDED(retVal))
        {
            if (pEffect->HasTarget() && pEffect->GetFirstUse())
            {
                m_ulNumPackets    = ulNumPackets;
                m_ulSessionHandle = ulSessionHandle;
                m_ulPacketIndex   = 0;
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        HX_RELEASE(pEffect);
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT PXScheduler::GetImageDataInfo(REF(UINT32) rulHandle, REF(IHXBuffer*) rpStreamMimeStr,
                                        REF(UINT32) rulSessionHandle, REF(UINT32) rulPacketIndex,
                                        REF(UINT32) rulNumPackets, REF(UINT32) rulTimeStamp)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pEffectItr && m_pRealPixFile)
    {
        PXEffect* pEffect = NULL;
        retVal            = m_pRealPixFile->GetCurrentEffect(m_pEffectItr, pEffect);
        if (SUCCEEDED(retVal))
        {
            if (pEffect->HasTarget() && pEffect->GetFirstUse())
            {
                IHXBuffer* pStreamMimeStr = NULL;
                retVal                     = m_pRealPixFile->GetImageStreamMimeType(pEffect->GetTarget(),
                                                                                    pStreamMimeStr);
                if (SUCCEEDED(retVal))
                {
                    rulHandle        = pEffect->GetTarget(),
                    HX_RELEASE(rpStreamMimeStr);
                    rpStreamMimeStr  = pStreamMimeStr;
                    rpStreamMimeStr->AddRef();
                    rulSessionHandle = m_ulSessionHandle;
                    rulPacketIndex   = m_ulPacketIndex;
                    rulNumPackets    = m_ulNumPackets;
                    rulTimeStamp     = (UINT32) (m_lCurrentTimeStamp >= 0 ? m_lCurrentTimeStamp : 0);
                }
                HX_RELEASE(pStreamMimeStr);
            }
            else
            {
                retVal = HXR_FAIL;
            }
        }
        HX_RELEASE(pEffect);
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT PXScheduler::PacketSent(UINT32 ulPacketSize)
{
    HX_RESULT retVal = HXR_OK;

    if (m_pEffectItr && m_pRealPixFile)
    {
        if (!m_bSeeking)
        {
            if (m_ulPacketTypeState == PXWireFormatManager::kPacketTypeImageHeader)
            {
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                OutputDebugString("m_bSeeking = FALSE, image header -> image data\n");
#endif
                // Move to the kPacketTypeImageData state
                m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageData;
                // Increment time stamp by size of data sent
                m_lCurrentTimeStamp += (INT32) GetSendTime(ulPacketSize, m_pRealPixFile->GetBitrate());
            }
            else if (m_ulPacketTypeState == PXWireFormatManager::kPacketTypeImageData)
            {
                if (m_ulPacketIndex < m_ulNumPackets - 1)
                {
                    // We are not through sending all the packets of this image, so
                    // we continue to send this image and simply update the packet index.
                    m_ulPacketIndex++;
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                    OutputDebugString("m_bSeeking = FALSE, image data -> image data\n");
#endif
                }
                else
                {
                    // We just got through sending the last packet of this image (i.e. - packet N-1
                    // of packets numbered 0,1,...,N-1). Therefore, we transition to the
                    // kPacketTypeEffect state.
                    m_ulPacketTypeState = PXWireFormatManager::kPacketTypeEffect;
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                    OutputDebugString("m_bSeeking = FALSE, image data -> effect\n");
#endif
                }
                // We update the timestamp based on the size of the data sent.
                m_lCurrentTimeStamp += (INT32) GetSendTime(ulPacketSize, m_pRealPixFile->GetBitrate());
            }
            else if (m_ulPacketTypeState == PXWireFormatManager::kPacketTypeEffect)
            {
                // We just got through sending an effect packet, so we increment
                // the effect iterator and look at the next effect.
                PXEffect* pEffect = NULL;
                retVal            = m_pRealPixFile->GetAtNextEffect(m_pEffectItr, pEffect);
                if (SUCCEEDED(retVal))
                {
                    if (pEffect->HasTarget() && pEffect->GetFirstUse())
                    {
                        // The next effect has a first-use image associated with it. Therefore,
                        // we need to transition to a kPacketTypeImageHeader state.
                        m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageHeader;
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                        OutputDebugString("m_bSeeking = FALSE, effect -> image header\n");
#endif
                    }
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                    else
                    {
                        OutputDebugString("m_bSeeking = FALSE, effect -> effect\n");
                    }
#endif
                    // We set the time stamp to the StartSendTime() for this effect.
                    m_lCurrentTimeStamp = pEffect->GetStartSendTime();
                }
                HX_RELEASE(pEffect);
            }
        }
        else
        {
            if (m_ulPacketTypeState == PXWireFormatManager::kPacketTypeImageHeader)
            {
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                OutputDebugString("m_bSeeking = TRUE, image header -> image data\n");
#endif
                // Move to the kPacketTypeImageData state
                m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageData;
                // Increment time stamp by size of data sent
                m_lCurrentTimeStamp += (INT32) GetSendTime(ulPacketSize, m_pRealPixFile->GetBitrate());
            }
            else if (m_ulPacketTypeState == PXWireFormatManager::kPacketTypeImageData)
            {
                if (m_ulPacketIndex < m_ulNumPackets - 1)
                {
                    // We are not through sending all the packets of this image, so
                    // we continue to send this image and simply update the packet index.
                    m_ulPacketIndex++;
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                    OutputDebugString("m_bSeeking = TRUE, image data -> image data\n");
#endif
                }
                else
                {
                    // We just got through sending the last packet of this image (i.e. - packet N-1
                    // of packets numbered 0,1,...,N-1). We need to find the next effect
                    // that either: a) uses a cached image for the first time; or b)
                    // has a start time which is >= m_ulKeyScreenTime.
                    PXEffect* pEffect = NULL;
                    HX_RESULT rv      = m_pRealPixFile->GetAtNextEffect(m_pEffectItr, pEffect);
                    while (SUCCEEDED(rv))
                    {
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                        char szDbgStr[128]; /* Flawfinder: ignore */
                        sprintf(szDbgStr, "m_bSeeking = TRUE, looking at effect with start=%lu (key=%lu)\n", /* Flawfinder: ignore */
                                pEffect->GetStart(), m_ulKeyScreenTime);
                        OutputDebugString(szDbgStr);
#endif
                        if (pEffect->GetStart() >= m_ulKeyScreenTime)
                        {
                            m_bSeeking = FALSE;
                            if (pEffect->HasTarget() && pEffect->GetFirstUse())
                            {
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                                OutputDebugString("m_bSeeking = TRUE, switching off m_bSeeking, image data -> image header\n");
#endif
                                m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageHeader;
                            }
                            else
                            {
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                                OutputDebugString("m_bSeeking = TRUE, switching off m_bSeeking, image data -> effect\n");
#endif
                                m_ulPacketTypeState = PXWireFormatManager::kPacketTypeEffect;
                            }
                            m_lCurrentTimeStamp = pEffect->GetStartSendTime();
                            break;
                        }
                        else
                        {
                            if (pEffect->HasTarget() && pEffect->GetFirstUse() &&
                                IsImageCachedAtTime(pEffect->GetTarget(), m_ulKeyScreenTime))
                            {
                                m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageHeader;
                                m_lCurrentTimeStamp = pEffect->GetStartSendTime();
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
                                OutputDebugString("m_bSeeking = TRUE, image data -> image header\n");
#endif
                                break;
                            }
                        }

                        HX_RELEASE(pEffect);
                        rv = m_pRealPixFile->GetAtNextEffect(m_pEffectItr, pEffect);
                    }
                    HX_RELEASE(pEffect);
                }
            }
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT PXScheduler::SeekSetup(UINT32 ulSeekTime)
{
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
    char szDbgStr[128]; /* Flawfinder: ignore */
    sprintf(szDbgStr, "SeekSetup(%lu)\n", ulSeekTime); /* Flawfinder: ignore */
    OutputDebugString(szDbgStr);
#endif
    HX_RESULT retVal = HXR_OK;

    if (m_pRealPixFile)
    {
        // Calculate time of latest full-screen draw effect which is 
        // not later than ulSeekToTime. To do this, we run backwards
        // through the effects, and start looking for the first full-screen
        // write before ulSeekToTime.
        m_ulKeyScreenTime = 0;
        void* pItr        = NULL;
        retVal            = m_pRealPixFile->GetEffectTailIterator(pItr);
        if (SUCCEEDED(retVal))
        {
            PXEffect* pEffect = NULL;
            HX_RESULT rv      = m_pRealPixFile->GetPrevEffect(pItr, pEffect);
            while (SUCCEEDED(rv))
            {
                if (pEffect->GetStart()      <= ulSeekTime                         &&
                    (!pEffect->GetDstWidth() ||
                     (pEffect->GetDstWidth() == m_pRealPixFile->GetDisplayWidth()))  &&
                    (!pEffect->GetDstHeight() ||
                     (pEffect->GetDstHeight()  == m_pRealPixFile->GetDisplayHeight())) &&
                    pEffect->GetEffectType() != PXEffect::kEffectTypeViewChange    &&
                    (pEffect->GetEffectType() != PXEffect::kEffectTypeFadeOut ||
                     (pEffect->GetEffectType() == PXEffect::kEffectTypeFadeOut &&
                      pEffect->GetEnd()        <  ulSeekTime)))
                {
                    m_ulKeyScreenTime = pEffect->GetStart();
                    break;
                }
                HX_RELEASE(pEffect);
                rv = m_pRealPixFile->GetPrevEffect(pItr, pEffect);
            }
            HX_RELEASE(pEffect);
#ifdef XXXMEH_OUTPUT_DEBUG_STRING
            char szDbgStr[128]; /* Flawfinder: ignore */
            sprintf(szDbgStr, "   m_ulKeyScreenTime=%lu\n", m_ulKeyScreenTime); /* Flawfinder: ignore */
            OutputDebugString(szDbgStr);
#endif

            // Rewind the packet type state and effect iterator
            retVal = Rewind();
            if (SUCCEEDED(retVal))
            {
                m_bSeeking = TRUE;
                HX_RELEASE(pEffect);
                rv = m_pRealPixFile->GetCurrentEffect(m_pEffectItr, pEffect);
                while (SUCCEEDED(rv))
                {
                    if (pEffect->GetStart() >= m_ulKeyScreenTime)
                    {
                        m_bSeeking = FALSE;
                        if (pEffect->HasTarget() && pEffect->GetFirstUse())
                        {
                            m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageHeader;
                        }
                        else
                        {
                            m_ulPacketTypeState = PXWireFormatManager::kPacketTypeEffect;
                        }
                        m_lCurrentTimeStamp = pEffect->GetStartSendTime();
                        break;
                    }
                    else
                    {
                        if (pEffect->HasTarget() &&
                            IsImageCachedAtTime(pEffect->GetTarget(), m_ulKeyScreenTime))
                        {
                            m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageHeader;
                            m_lCurrentTimeStamp = pEffect->GetStartSendTime();
                            break;
                        }
                    }
                    HX_RELEASE(pEffect);
                    rv = m_pRealPixFile->GetAtNextEffect(m_pEffectItr, pEffect);
                }
                HX_RELEASE(pEffect);
            }
        }
    }
    else
    {
        retVal = HXR_UNEXPECTED;
    }

    return retVal;
}

HX_RESULT PXScheduler::Rewind()
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_pRealPixFile)
    {
        // Initialize the effect iterator
        retVal = m_pRealPixFile->GetEffectHeadIterator(m_pEffectItr);
        if (SUCCEEDED(retVal))
        {
            // Initialize the packet type state
            PXEffect* pCurEffect = NULL;
            retVal               = m_pRealPixFile->GetCurrentEffect(m_pEffectItr, pCurEffect);
            if (SUCCEEDED(retVal))
            {
                if (pCurEffect->HasTarget() && pCurEffect->GetFirstUse())
                {
                    m_ulPacketTypeState = PXWireFormatManager::kPacketTypeImageHeader;
                }
                else
                {
                    m_ulPacketTypeState = PXWireFormatManager::kPacketTypeEffect;
                }
                // Set the time stamp
                m_lCurrentTimeStamp = pCurEffect->GetStartSendTime();
            }
            HX_RELEASE(pCurEffect);
        }
    }

    return retVal;
}

BOOL PXScheduler::IsImageCachedAtTime(UINT32 ulHandle, UINT32 ulTime)
{
    BOOL bRet = FALSE;

    void*     pItr   = NULL;
    HX_RESULT retVal = m_pRealPixFile->GetEffectHeadIterator(pItr);
    if (SUCCEEDED(retVal))
    {
        BOOL      bSawFirstUse = FALSE;
        BOOL      bSawLastUse  = FALSE;
        PXEffect* pEffect      = NULL;
        retVal                 = m_pRealPixFile->GetNextEffect(pItr, pEffect);
        while (SUCCEEDED(retVal))
        {
            if (pEffect->GetStart() >= ulTime)
            {
                break;
            }

            if (pEffect->HasTarget() && pEffect->GetTarget() == ulHandle)
            {
                if (pEffect->GetFirstUse())
                {
                    bSawFirstUse = TRUE;
                }
                if (pEffect->GetLastUse())
                {
                    bSawLastUse = TRUE;
                }
            }

            HX_RELEASE(pEffect);
            retVal = m_pRealPixFile->GetNextEffect(pItr, pEffect);
        }
        HX_RELEASE(pEffect);

        if (bSawFirstUse && !bSawLastUse)
        {
            bRet = TRUE;
        }
    }

    return bRet;
}

