///////////////////////////////////////////////////////////////
//
// CCompressorJobQueueImpl::RemoveUnwantedResults
//
// Check result queue items for match with ignore list items.
// * Must be called from inside a locked section *
//
///////////////////////////////////////////////////////////////
void CCompressorJobQueueImpl::RemoveUnwantedResults ( void )
{
    if ( m_IgnoreResultList.empty () )
        return;

again:
    for ( std::list < CCompressJobData* >::iterator iter = shared.m_ResultQueue.begin () ; iter != shared.m_ResultQueue.end () ; )
    {
        CCompressJobData* pJobData = *iter;
        if ( MapContains ( m_IgnoreResultList, pJobData ) )
        {
            // Found result to ignore, remove from result and ignore lists, add to finished list
            iter = shared.m_ResultQueue.erase ( iter );
            MapRemove ( m_IgnoreResultList, pJobData );
            pJobData->stage = EJobStage::FINISHED;
            MapInsert ( m_FinishedList, pJobData );

            // Do callback incase any cleanup is needed
            if ( pJobData->HasCallback () )
            {
                shared.m_Mutex.Unlock ();
                pJobData->ProcessCallback ();
                shared.m_Mutex.Lock ();
                goto again;
            }
        }
        else
            ++iter;
    }
}
////////////////////////////////////////////////////////////////
//
// CScreenGrabber::ProcessScreenShotQueue
//
// Process queued requests
//
////////////////////////////////////////////////////////////////
void CScreenGrabber::ProcessScreenShotQueue ( void )
{
    // Check if busy
    if ( m_pCompressJobData )
    {
        // Previous job complete?
        if ( !m_pCompressorJobQueue->PollCommand ( m_pCompressJobData, 0 ) )
            return;

        // Compression done
        if ( m_pCompressJobData->HasCallback () )
            m_pCompressJobData->ProcessCallback ();
        m_pCompressJobData = NULL;
    }

    // Anything new?
    if ( m_ScreenShotQueue.empty ()  )
        return;

    // Limit queue size
    while( m_ScreenShotQueue.size() >= 20 )
    {
        SScreenShotQueueItem item = m_ScreenShotQueue.front ();
        m_ScreenShotQueue.pop_front ();
        item.pfnScreenShotCallback( NULL, 0, "Too many queued screenshots" );
    }

    // Get new args
    SScreenShotQueueItem item = m_ScreenShotQueue.front ();
    uint uiSizeX = item.uiSizeX;
    uint uiSizeY = item.uiSizeY;
    uint uiQuality = item.uiQuality;
    uint uiTimeSpentInQueue = GetTickCount32 () - item.uiTimeQueued;
    PFN_SCREENSHOT_CALLBACK pfnScreenShotCallback = item.pfnScreenShotCallback;
    m_ScreenShotQueue.pop_front ();

    CBuffer buffer;
    SString strError;
    if ( GetBackBufferPixels ( uiSizeX, uiSizeY, buffer, strError ) )
    {
        // Start compression
        m_pCompressJobData = m_pCompressorJobQueue->AddCommand ( uiSizeX, uiSizeY, uiQuality, uiTimeSpentInQueue, pfnScreenShotCallback, buffer );
    }
    else
    {
        // Pass on error
        pfnScreenShotCallback( NULL, 0, strError );
    }
}
///////////////////////////////////////////////////////////////
//
// CCompressorJobQueueImpl::AddCommand
//
// AddCommand to queue
// Can't fail
//
///////////////////////////////////////////////////////////////
CCompressJobData* CCompressorJobQueueImpl::AddCommand ( uint uiSizeX, uint uiSizeY, uint uiQuality, uint uiTimeSpentInQueue, PFN_SCREENSHOT_CALLBACK pfnScreenShotCallback, const CBuffer& buffer )
{
    // Create command
    CCompressJobData* pJobData = new CCompressJobData ();
    pJobData->command.uiSizeX = uiSizeX;
    pJobData->command.uiSizeY = uiSizeY;
    pJobData->command.uiQuality = uiQuality;
    pJobData->command.buffer = buffer;

    pJobData->SetCallback ( pfnScreenShotCallback, uiTimeSpentInQueue );

    // Add to queue
    shared.m_Mutex.Lock ();
    pJobData->stage = EJobStage::COMMAND_QUEUE;
    shared.m_CommandQueue.push_back ( pJobData );
    shared.m_Mutex.Signal ();
    shared.m_Mutex.Unlock ();

    return pJobData;
}
///////////////////////////////////////////////////////////////
//
// CCompressorJobQueueImpl::DoPulse
//
// Check if any callback functions are due
//
///////////////////////////////////////////////////////////////
void CCompressorJobQueueImpl::DoPulse ( void )
{
    shared.m_Mutex.Lock ();

again:
    // Delete finished
    for ( std::set < CCompressJobData* >::iterator iter = m_FinishedList.begin () ; iter != m_FinishedList.end () ; )
    {
        CCompressJobData* pJobData = *iter;
        m_FinishedList.erase ( iter++ );
        // Check not refed
        assert ( !ListContains ( shared.m_CommandQueue, pJobData ) );
        assert ( !ListContains ( shared.m_ResultQueue, pJobData ) );
        assert ( !MapContains ( m_IgnoreResultList, pJobData ) );
        assert ( !MapContains ( m_FinishedList, pJobData ) );

        assert ( !pJobData->HasCallback () );
        SAFE_DELETE( pJobData );
    }

    // Remove ignored
    RemoveUnwantedResults ();

    // Do pending callbacks
    for ( std::list < CCompressJobData* >::iterator iter = shared.m_ResultQueue.begin () ; iter != shared.m_ResultQueue.end () ; ++iter )
    {
        CCompressJobData* pJobData = *iter;

        if ( pJobData->HasCallback () )
        {
            shared.m_Mutex.Unlock ();
            pJobData->ProcessCallback ();
            shared.m_Mutex.Lock ();

            // Redo from the top to ensure everything is consistent
            goto again;
        }
    }

    shared.m_Mutex.Unlock ();
}