/**************************************************************************
*
*  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY
*  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
*  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR
*  PURPOSE.
*
*  Copyright (c) 1993 - 1998  Microsoft Corporation.  All Rights Reserved.
*
**************************************************************************/

//
// DDrawObj.cpp: DDrawObject class implementation
//

#include <streams.h>
#include <windows.h>

#include "DDrawObj.h"
#include "VidPlay.h"
#include "DXDebug.h"



inline void restoreIfLost (LPDIRECTDRAWSURFACE pSurface)
{
	if (pSurface == 0) return;

    // Keep trying to flip the buffers until successful
    while (1)
    {
		HRESULT hr = pSurface->IsLost () ;
        
        if (SUCCEEDED (hr))
            break ;

		OutputDebugString ("Restoring Surface...\n") ;

        if (DDERR_SURFACELOST == hr)   // surface lost; try to restore
        {
			REPORTERR (hr) ;

            hr = pSurface->Restore () ;
            if (FAILED (hr))  // couldn't restore surface
            {
				OutputDebugString ("  Restoring failed...\n") ;
				REPORTERR (hr) ;
                break ;
            }
        }
        else
        {	// Shouldn't happen on a restore op unless there's a parameter error...
        	ASSERT (0) ;
        }
	} // while
}	


// CDDrawObject constructor
//
CDDrawObject::CDDrawObject (HWND hWndApp)
{
    DbgLog ((LOG_TRACE, 3, TEXT ("CDDrawObject c-tor entered"))) ;

    m_pDDObject = NULL ;
    m_pPrimary  = NULL ;
    m_pBackBuff = NULL ;

    // Default colors to be used for filling
    m_dwScrnColor	  = RGB (0, 0, 0) ;
    m_dwVideoKeyColor = RGB (255, 0, 255) ;	// magenta

    // Rects for the screen area and video area
    SetRect (&m_RectScrn, 0, 0, 0, 0) ;
    SetRect (&m_RectVideo, 0, 0, 0, 0) ;

    // some flags, count, messages for buffer flipping
    m_bInExclMode = FALSE ;
    m_iCount 	  = 0 ;
    m_bFrontBuff  = TRUE ;
    m_szFrontMsg  = "Front Buffer" ;
    m_szBackMsg   = "Back Buffer" ;
    m_szDirection = "Press the 'Space' to pause, 'Esc' to stop playback." ;

    HRESULT  hr ;
    m_pOverlayCallback = (IDDrawExclModeVideoCallback *) new COverlayCallback (this, hWndApp, &hr) ;
    ASSERT (SUCCEEDED (hr)) ;
    m_bOverlayVisible = FALSE ;  // don't draw color key until it's OK-ed by OverlayMixer
	m_bReleaseDDraw = true ;
    
#ifdef SHOW_BALL
    // Create brush and pen for drawing the filled ball
    LOGBRUSH  lb ;
    lb.lbStyle = BS_HATCHED ;
    lb.lbColor = RGB (0, 255, 0) ;
    lb.lbHatch = HS_CROSS ;
    m_hBrush = CreateBrushIndirect (&lb) ;
    ASSERT (NULL != m_hBrush) ;

    m_hPen = CreatePen (PS_SOLID, 2, RGB (0, 0, 255)) ;
    ASSERT (NULL != m_hPen) ;
#endif // SHOW_BALL
}


//
// CDDrawObject destructor
//
CDDrawObject::~CDDrawObject (void)
{
    DbgLog ((LOG_TRACE, 3, TEXT ("CDDrawObject d-tor entered"))) ;

    // Let go of the overlay notification callback object now
    if (m_pOverlayCallback)
        m_pOverlayCallback->Release () ;  // done with callback object now

#ifdef SHOW_BALL
    // delete the pen and brush we created for drawing the ball
    if (m_hPen)
        DeleteObject (m_hPen) ;
    if (m_hBrush)
        DeleteObject (m_hBrush) ;
#endif 

    if (m_pBackBuff)
        m_pBackBuff->Release () ;
    if (m_pPrimary)
        m_pPrimary->Release () ;

    if (m_pDDObject && m_bReleaseDDraw)
        m_pDDObject->Release () ;

    DbgLog ((LOG_TRACE, 3, TEXT ("CDDrawObject d-tor exiting..."))) ;
}


//
// CDDrawObject::Initialize(): Just creates a DDraw object.
//
BOOL CDDrawObject::Initialize (LPDIRECTDRAW pDDraw/*=0*/)
{
	if (m_pDDObject) return (TRUE);

	if (pDDraw)	
	{
		m_pDDObject 	= pDDraw ;
		m_bReleaseDDraw = false ;	// don't free ddraw
        return(TRUE);
	}
    
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::Initialize() entered"))) ;

    HRESULT hr = DirectDrawCreate (NULL, &m_pDDObject, NULL) ;
    if (FAILED (hr) || NULL == m_pDDObject)
    {
		REPORTERR (hr) ;
        return FALSE ;
    }

	m_bReleaseDDraw = true ;
    return TRUE;
}


//#define SCRN_WIDTH    800
//#define SCRN_HEIGHT   600
//#define SCRN_BITDEPTH  16

//
// CDDrawObject::StartExclusiveMode(): Gets into fullscreen exclusive mode, sets
// display mode to 800x600x8, creates a flipping primary surface with one back 
// buffer, gets the surface size and inits some variables.
//
// NOTE: Please don't bother about the "#ifndef NOFLIP"s.  They were there
// for testing.
//
HRESULT CDDrawObject::StartExclusiveMode (HWND hWndApp, int nWidth, int nHeight, int nBitDepth)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::StartExclusiveMode() entered"))) ;

    HRESULT         hr ;
    DDSURFACEDESC   ddsd ;
    DDSCAPS         ddscaps ;

    if (! Initialize (0) )
        return E_FAIL ;

    // Get into fullscreen exclusive mode
#ifndef NOFLIP
    hr = m_pDDObject->SetCooperativeLevel (hWndApp, DDSCL_EXCLUSIVE | DDSCL_FULLSCREEN | DDSCL_ALLOWREBOOT) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
        MessageBox (hWndApp, 
                TEXT ("SetCooperativeLevel() failed to go into exclusive mode"), 
                TEXT ("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
#else
    hr = m_pDDObject->SetCooperativeLevel (hWndApp, DDSCL_NORMAL) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
        MessageBox (hWndApp, 
                TEXT ("SetCooperativeLevel() failed to go into normal mode"), 
                TEXT ("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
#endif // NOFLIP

#ifndef NOFLIP
    hr = m_pDDObject->SetDisplayMode (nWidth, nHeight, nBitDepth) ; //SCRN_WIDTH, SCRN_HEIGHT, SCRN_BITDEPTH) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
        DbgLog ((LOG_ERROR, 0, TEXT ("ERROR: SetDisplayMode(800x600x8) failed (Error 0x%lx)"), hr)) ;
        MessageBox (hWndApp, TEXT ("SetDisplayMode(640, 480, 8) failed"), 
                TEXT ("Error"), MB_OK | MB_ICONSTOP) ;
        return hr ;
    }
#endif // NOFLIP

    // Create the primary surface with 1 back buffer
    ZeroMemory (&ddsd, sizeof(ddsd)) ;
    ddsd.dwSize = sizeof(ddsd) ;

#ifndef NOFLIP
    ddsd.dwFlags	       = DDSD_CAPS | DDSD_BACKBUFFERCOUNT ;
    ddsd.ddsCaps.dwCaps    = DDSCAPS_PRIMARYSURFACE | DDSCAPS_FLIP | DDSCAPS_COMPLEX ;
    ddsd.dwBackBufferCount = 1 ;
#else
    ddsd.dwFlags = DDSD_CAPS ;
    ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE ;
#endif  // NOFLIP

    hr = m_pDDObject->CreateSurface (&ddsd, &m_pPrimary, NULL) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
#ifndef NOFLIP
        MessageBox (hWndApp, TEXT ("CreateSurface(Primary, flip(1), complex) failed"), 
                TEXT ("Error"), MB_OK | MB_ICONSTOP) ;
#else
        MessageBox (hWndApp, TEXT ("CreateSurface(Primary, noflip, no backbuffer) failed"), 
                TEXT ("Error"), MB_OK | MB_ICONSTOP) ;
#endif // NOFLIP
        return hr ;
    }

    hr = clearSurface (m_pPrimary) ;

#ifndef NOFLIP
    // Get a pointer to the back buffer
    ddscaps.dwCaps = DDSCAPS_BACKBUFFER ;
    hr = m_pPrimary->GetAttachedSurface (&ddscaps, &m_pBackBuff) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
        return hr ;
    }

    hr = clearSurface (m_pBackBuff) ;
#endif // NOFLIP

    // Get the screen size and save it as a rect
    ZeroMemory (&ddsd, sizeof(ddsd)) ;
    ddsd.dwSize = sizeof(ddsd) ;
    hr = m_pPrimary->GetSurfaceDesc (&ddsd) ;
    if (! (SUCCEEDED (hr) && (ddsd.dwFlags & DDSD_WIDTH) && (ddsd.dwFlags & DDSD_HEIGHT)) )
    {
		REPORTERR (hr) ;
        return hr ;
    }

    SetRect (&m_RectScrn, 0, 0, ddsd.dwWidth, ddsd.dwHeight) ;

    // Reset some buffer drawing flags, values etc.
    m_iCount = 0 ;
    m_bFrontBuff = TRUE ;
    m_bInExclMode = TRUE ;

    return S_OK ;
}


//
// CDDrawObject::drawOnSurface(): Private method to draw stuff on back buffer
//
void CDDrawObject::drawOnSurface (LPDIRECTDRAWSURFACE pSurface)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::drawOnSurface() entered"))) ;

    HDC     hDC ;

    if (DD_OK == pSurface->GetDC (&hDC))
    {
        // TODO: Write dialog captions here...
#if 0
        SetBkColor (hDC, RGB (0, 0, 0)) ;
        SetTextColor (hDC, RGB (0, 255, 255)) ;
        TextOut (hDC, m_RectScrn.left + 100, m_RectScrn.bottom - 20, m_szDirection, lstrlen (m_szDirection)) ;
#endif

#if 0
	    TCHAR   achBuffer[30] ;
        // write fonr/back to show buffer flipping
        SetBkColor (hDC, RGB (0, 0, 255)) ;
        SetTextColor (hDC, RGB (255, 255, 0)) ;
        wsprintf (achBuffer, " %-12s: %6.6d ", m_bFrontBuff ? m_szFrontMsg : m_szBackMsg, m_iCount) ;
        TextOut (hDC, 20, 10, achBuffer, lstrlen (achBuffer)) ;
#endif

#ifdef SHOW_BALL
        // Draw the ball on screen now
        HBRUSH hBrush = (HBRUSH) SelectObject (hDC, m_hBrush) ; // select special brush
        HPEN   hPen   = (HPEN) SelectObject (hDC, m_hPen) ;     // select special pen 
        Ellipse (hDC, m_iBallCenterX - BALL_RADIUS, m_iBallCenterY - BALL_RADIUS, 
                m_iBallCenterX + BALL_RADIUS, m_iBallCenterY + BALL_RADIUS) ;
        SelectObject (hDC, hBrush) ;  // restore original brush
        SelectObject (hDC, hPen) ;    // restore original pen
#endif // SHOW_BALL

        pSurface->ReleaseDC (hDC) ;  // done now; let go of the DC
    }
}


//
// CDDrawObject::StopExclusiveMode(): Releases primary buffer, gets out exclusive
// mode, resets some variables/flags.
//
HRESULT CDDrawObject::StopExclusiveMode (HWND hWndApp)
{
    OutputDebugString ("CDDrawObject::StopExclusiveMode()") ;

    HRESULT hr ;

    if (m_pBackBuff)
    {
        hr = m_pBackBuff->Release () ;  // release back buffer
        m_pBackBuff = NULL ;  // no back buffer anymore
    }

    // Release the primary surface (and attached buffer with it)
    if (m_pPrimary)
    {
        hr = m_pPrimary->Release () ;  // release primary surface
        m_pPrimary = NULL ;
    }

    // Get out of fullscreen exclusive mode
    if (m_pDDObject)
    {
        hr = m_pDDObject->SetCooperativeLevel (hWndApp, DDSCL_NORMAL) ;
        if (FAILED (hr))
        {
			REPORTERR (hr) ;
            return hr ;
        }

        m_pDDObject->Release () ;  // now release the DDraw object
        m_pDDObject = NULL ;      // no more DDraw object
    }

    // Reset the color key and screen, video rects.
    m_dwVideoKeyColor = RGB (255, 0, 255) ;
    SetRect (&m_RectScrn, 0, 0, 0, 0) ;
    SetRect (&m_RectVideo, 0, 0, 0, 0) ;

        m_bInExclMode = FALSE ;

    return S_OK ;
}


//
// CDDrawObject::convertColorRefToPhysColor(): Converts a COLORREF to a
// DWORD physical color value based on current display mode.
//
// NOTE: This method is useful ONLY if you want to get the COLORREF from
// OverlayMixer (rather than the physical color as a DWORD) and convert
// it yourself.  But OverlayMixer does it for you anyway; so the utility
// of this method is quite minimal.
//
HRESULT CDDrawObject::convertColorRefToPhysColor (COLORREF rgb, DWORD *pdwPhysColor)
{
    HRESULT         hr ;
    COLORREF        rgbCurr ;
    HDC             hDC ;
    DWORD           dw = CLR_INVALID ;
    DDSURFACEDESC   ddsd ;

    ASSERT (rgb != CLR_INVALID) ;

    //  use GDI SetPixel to color match for us
    hr = m_pBackBuff->GetDC (&hDC) ;
    if (DD_OK != hr)
    {
		REPORTERR (hr) ;
        DbgLog ((LOG_ERROR, 0, TEXT ("m_pBackBuff->GetDC() failed (Error 0x%lx)"), hr)) ;
        return hr ;
    }
    rgbCurr = GetPixel (hDC, 0, 0) ;             // save current pixel value
    SetPixel (hDC, 0, 0, rgb) ;               // set our value
    m_pBackBuff->ReleaseDC (hDC) ;

    // now lock the surface so we can read back the converted color
    ZeroMemory (&ddsd, sizeof(ddsd)) ;
    ddsd.dwSize = sizeof(ddsd) ;
    while (DDERR_WASSTILLDRAWING == (hr = m_pBackBuff->Lock (NULL, &ddsd, 0, NULL)))  // until unlocked...
        ;               // ...just wait

    if (DD_OK == hr)
    {
        // get DWORD
        *pdwPhysColor = *(DWORD *) ddsd.lpSurface ;

        // mask it to bpp
        if (ddsd.ddpfPixelFormat.dwRGBBitCount < 32)
            *pdwPhysColor &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1 ;
        m_pBackBuff->Unlock (NULL) ;
    }
    else
    {
		REPORTERR (hr) ;
        DbgLog ((LOG_ERROR, 0, TEXT ("m_pBackBuff->Lock() failed (Error 0x%lx)"), hr)) ;
        return hr ;
    }

    //  now restote the pixel to the color that was there
    hr = m_pBackBuff->GetDC (&hDC) ;
    if (DD_OK != hr)
    {
		REPORTERR (hr) ;
        DbgLog ((LOG_ERROR, 0, TEXT ("m_pBackBuff->GetDC() failed (Error 0x%lx)"), hr)) ;
        return hr ;
    }
    SetPixel (hDC, 0, 0, rgbCurr) ;
    m_pBackBuff->ReleaseDC (hDC) ;

    return S_OK ;
}


#if 0
//
// CDDrawObject::SetColorKey(): Creates a DWORD physical color out COLORREF.
// 
// NOTE: This method is useful only if you want to do the conversion. We have
// defined an inline version in ddrawobj.h file.
//
HRESULT CDDrawObject::SetColorKey (COLORKEY *pColorKey)
{
    if (NULL == pColorKey)
        return E_INVALIDARG ;

        DWORD    dwPhysColor ;
    HRESULT  hr = convertColorRefToPhysColor (pColorKey->LowColorValue, &dwPhysColor) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
        return hr ;
    }

        m_dwVideoKeyColor = dwPhysColor ;
    return S_OK ;
}
#endif // #if 0


HRESULT CDDrawObject::clearSurface (IDirectDrawSurface *pDDSurface, RECT* pRect/*=0*/,DWORD dwFillColor/*=RGB (0, 0, 0)*/)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::fillSurface() entered"))) ;

    if (NULL == pDDSurface)
        return E_INVALIDARG ;

    HRESULT  hr ;
    DDBLTFX  ddFX ;
    ZeroMemory (&ddFX, sizeof(ddFX)) ;
    ddFX.dwSize      = sizeof(ddFX) ;
    ddFX.dwFillColor = dwFillColor ; //m_dwScrnColor ;

    hr = pDDSurface->Blt (pRect, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddFX) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
        DbgLog ((LOG_ERROR, 0, TEXT ("pDDSurface->Blt for screen failed (Error 0x%lx)"), hr)) ;
        DbgLog ((LOG_ERROR, 0, TEXT ("Destination Rect: (%ld, %ld, %ld, %ld), Color = 0x%lx"), 
                m_RectScrn.left, m_RectScrn.top, m_RectScrn.right, m_RectScrn.bottom, dwFillColor)) ;
        return hr ;
    }

    return S_OK ;
}


void CDDrawObject::ClearSurfaces ()
{
	HRESULT hr ;

	restoreIfLost (m_pPrimary) ;
    
    hr = clearSurface (m_pPrimary) ;

#ifndef NOFLIP
    hr = clearSurface (m_pBackBuff) ;
#endif
}


// CDDrawObject::fillSurface(): Private method to fill the back buffer with black
// color and put the color key in teh video area.
//
// NOTE: Re-filling the back buffer every time is required ONLY IF the video 
// position keeps changing. Otherwise setting it once should be fine.
// 
HRESULT CDDrawObject::fillSurface (IDirectDrawSurface *pDDSurface)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::fillSurface() entered"))) ;

    if (NULL == pDDSurface)
        return E_INVALIDARG ;

    // Repaint the whole specified surface
    
    HRESULT  hr ;

    if (m_iCount < 2)
    {
        hr = clearSurface (pDDSurface, 0, m_dwScrnColor) ;
        if (FAILED (hr))
        {
			REPORTERR (hr) ;
            DbgLog ((LOG_ERROR, 0, TEXT ("pDDSurface->Blt for screen failed (Error 0x%lx)"), hr)) ;
            DbgLog ((LOG_ERROR, 0, TEXT ("Destination Rect: (%ld, %ld, %ld, %ld), Color = 0x%lx"), 
                    m_RectScrn.left, m_RectScrn.top, m_RectScrn.right, m_RectScrn.bottom, m_dwScrnColor)) ;
            return hr ;
        }
    }

    // Draw color key on the video area of given surface, ONLY IF we are supposed
    // to paint color key in the video area.
    if (m_bOverlayVisible)
    {
        DDBLTFX  ddFX ;
        ZeroMemory (&ddFX, sizeof(ddFX)) ;
        ddFX.dwSize      = sizeof(ddFX) ;
        ddFX.dwFillColor = m_dwVideoKeyColor ;
        hr = pDDSurface->Blt (&m_RectVideo, NULL, NULL, DDBLT_COLORFILL | DDBLT_WAIT, &ddFX) ;

        if (FAILED (hr))
        {
			REPORTERR (hr) ;
            DbgLog ((LOG_ERROR, 0, TEXT ("pDDSurface->Blt for video failed (Error 0x%lx)"), hr)) ;
            DbgLog ((LOG_ERROR, 0, TEXT ("Destination Rect: (%ld, %ld, %ld, %ld), Color = 0x%lx"), 
                    m_RectVideo.left, m_RectVideo.top, m_RectVideo.right, m_RectVideo.bottom, 
                    m_dwVideoKeyColor)) ;
            return hr ;
        }
    }
    else
        DbgLog ((LOG_TRACE, 5, TEXT ("Color keying of video area skipped"))) ;

    return S_OK ;
}


//
// CDDrawObject::UpdateAndFlipSurfaces(): Prepares the back buffer and flips.
//
HRESULT CDDrawObject::UpdateAndFlipSurfaces (void)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::UpdateAndFlipSurfaces() entered"))) ;
    HRESULT hr ;

	restoreIfLost (m_pPrimary) ;
    
    // Draw screen and color key on the current back buffer
    hr = fillSurface (m_pBackBuff) ;
    if (FAILED (hr))
    {
		REPORTERR (hr) ;
        DbgLog ((LOG_ERROR, 1, TEXT ("UpdateAndFlipSurfaces() skipped as fillSurface() failed"), hr)) ;
        return hr ;   // or return S_OK??
    }

    incCount () ;                    // increment flip count first
    m_bFrontBuff = !m_bFrontBuff ;  // toggle flag
    drawOnSurface (m_pBackBuff) ;  // draw next text on the back buffer

	//OutputDebugString (".") ;
    
    // Keep trying to flip the buffers until successful
    while (1)
    {
        hr = m_pPrimary->Flip (NULL, 0) ;  // flip the surfaces
        if (DD_OK == hr)  // success!!
        {
            break ;
        }
        if (DDERR_SURFACELOST == hr)   // surface lost; try to restore
        {
			REPORTERR (hr) ;
            DbgLog ((LOG_TRACE, 5, TEXT ("DirectDraw surface was lost. Trying to restore..."))) ;
            hr = m_pPrimary->Restore () ;
            if (DD_OK != hr)  // couldn't restore surface
            {
                DbgLog ((LOG_ERROR, 0, TEXT ("IDirectDrawSurface::Restore() failed (Error 0x%lx)"), hr)) ;
                break ;
            }
        }
        if (DDERR_WASSTILLDRAWING != hr)  // some weird error -- bail out
        {
			REPORTERR (hr) ;
            DbgLog ((LOG_ERROR, 0, TEXT ("IDirectDrawSurface::Flip() failed (Error 0x%lx)"), hr)) ;
            break ;
        }
    }

    return hr ;
}


//
// CDDrawObject::SetVideoPosition(): Receives the video position in terms of screen
// coordinates.
//
void CDDrawObject::SetVideoPosition (DWORD dwVideoLeft, DWORD dwVideoTop, 
                                    DWORD dwVideoWidth, DWORD dwVideoHeight)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::SetVideoPosition() entered"))) ;

    SetRect (&m_RectVideo, dwVideoLeft, dwVideoTop, dwVideoLeft + dwVideoWidth,
            dwVideoTop + dwVideoHeight) ;
}


// Overlay Callback object implementation
//
COverlayCallback::COverlayCallback (CDDrawObject *pDDrawObj, HWND hWndApp, HRESULT *phr) :
CUnknown ("Overlay Callback Object", NULL, phr),
m_pDDrawObj (pDDrawObj),
m_hWndApp (hWndApp)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("COverlayCallback::COverlayCallback() entered"))) ;
    ASSERT (m_pDDrawObj) ;
    AddRef () ;    // increment ref count for itself
}


COverlayCallback::~COverlayCallback ()
{
    DbgLog ((LOG_TRACE, 5, TEXT ("COverlayCallback::~COverlayCallback() entered"))) ;
}


HRESULT COverlayCallback::OnUpdateOverlay (BOOL  bBefore,
                                          DWORD dwFlags,
                                          BOOL  bOldVisible,
                                          const RECT *prcSrcOld,
                                          const RECT *prcDestOld,
                                          BOOL  bNewVisible,
                                          const RECT *prcSrcNew,
                                          const RECT *prcDestNew)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("COverlayCallback::OnUpdateOverlay(%s, ...) entered"),
            bBefore ? "TRUE" : "FALSE")) ;

    if (NULL == m_pDDrawObj)
    {
        DbgLog ((LOG_ERROR, 1, TEXT ("ERROR: NULL DDraw object pointer was specified"))) ;
        return E_POINTER ;
    }

    if (bBefore)  // overlay is going to be updated
    {
        DbgLog ((LOG_TRACE, 5, TEXT ("Just turn off color keying and return"))) ;
        m_pDDrawObj->SetOverlayState (FALSE) ;   // don't paint color key in video's position
#ifndef NOFLIP
        m_pDDrawObj->UpdateAndFlipSurfaces () ;  // flip the surface so that video doesn't show anymore
#endif // NOFLIP
        return S_OK ;
    }

    //
    // After overlay has been updated. Turn on/off overlay color keying based on 
    // flags and use new source/dest position etc.
    //
    if (dwFlags & (AM_OVERLAY_NOTIFY_VISIBLE_CHANGE |
                   AM_OVERLAY_NOTIFY_SOURCE_CHANGE  |
	        	   AM_OVERLAY_NOTIFY_DEST_CHANGE))   // it's a valid flag
        m_pDDrawObj->SetOverlayState (bNewVisible) ;  // paint/don't paint color key based on param

    if (dwFlags & AM_OVERLAY_NOTIFY_VISIBLE_CHANGE)  // overlay visibility state change
    {
        DbgLog ((LOG_TRACE, 5, TEXT (".._NOTIFY_VISIBLE_CHANGE from %s to %s"), 
                bOldVisible ? "TRUE" : "FALSE", bNewVisible ? "TRUE" : "FALSE")) ;
    }

    if (dwFlags & AM_OVERLAY_NOTIFY_SOURCE_CHANGE)   // overlay source rect change
    {
        DbgLog ((LOG_TRACE, 5, TEXT (".._NOTIFY_SOURCE_CHANGE from (%ld, %ld, %ld, %ld) to (%ld, %ld, %ld, %ld)"), 
                prcSrcOld->left, prcSrcOld->top, prcSrcOld->right, prcSrcOld->bottom,
                prcSrcNew->left, prcSrcNew->top, prcSrcNew->right, prcSrcNew->bottom)) ;
    }

    if (dwFlags & AM_OVERLAY_NOTIFY_DEST_CHANGE)     // overlay destination rect change
    {
        DbgLog ((LOG_TRACE, 5, TEXT (".._NOTIFY_DEST_CHANGE from (%ld, %ld, %ld, %ld) to (%ld, %ld, %ld, %ld)"), 
                prcDestOld->left, prcDestOld->top, prcDestOld->right, prcDestOld->bottom,
                prcDestNew->left, prcDestNew->top, prcDestNew->right, prcDestNew->bottom)) ;
        m_pDDrawObj->SetVideoPosition (prcDestNew->left, prcDestNew->top, 
						               RECTWIDTH (*prcDestNew), RECTHEIGHT (*prcDestNew)) ;
    }

    return S_OK ;
}


HRESULT COverlayCallback::OnUpdateColorKey (COLORKEY const *pKey,
                                           DWORD           dwColor)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("COverlayCallback::OnUpdateColorKey(.., 0x%lx) entered"), dwColor)) ;

    return S_OK ;
}


HRESULT COverlayCallback::OnUpdateSize (DWORD dwWidth, DWORD dwHeight, 
                                       DWORD dwARWidth, DWORD dwARHeight)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("COverlayCallback::OnUpdateSize(%lu, %lu, %lu, %lu) entered"), 
            dwWidth, dwHeight, dwARWidth, dwARHeight)) ;

    PostMessage (m_hWndApp, WM_SIZE_CHANGE, 0, 0) ;

    return S_OK ;
}



// --------------

#ifdef SHOW_BALL
// CDDrawObject::SetBallPosition(): Uses the video position to initialize the ball's
// position to the center of the video.
//
void CDDrawObject::SetBallPosition (DWORD dwVideoLeft, DWORD dwVideoTop, 
                                   DWORD dwVideoWidth, DWORD dwVideoHeight)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::SetBallPosition() entered"))) ;

    // Place the ball at the middle of video on start
    m_iBallCenterX = dwVideoLeft + dwVideoWidth  / 2 ;
    m_iBallCenterY = dwVideoTop  + dwVideoHeight / 2 ;
}


//
// CDDrawObject::MoveBallPosition(): Moves the ball L/R/U/D and keeps it within
// the video area..
//
void CDDrawObject::MoveBallPosition (int iDirX, int iDirY)
{
    DbgLog ((LOG_TRACE, 5, TEXT ("CDDrawObject::MoveBallPosition(%d, %d) entered"),
            iDirX, iDirY)) ;

        m_iBallCenterX += iDirX ;
    m_iBallCenterY += iDirY ;

    // Keep the ball inside the video rect
    if (m_iBallCenterX < m_RectVideo.left + BALL_RADIUS)
        m_iBallCenterX = m_RectVideo.left + BALL_RADIUS ;
    else if (m_iBallCenterX > m_RectVideo.right - BALL_RADIUS)
        m_iBallCenterX = m_RectVideo.right - BALL_RADIUS ;

    if (m_iBallCenterY < m_RectVideo.top + BALL_RADIUS)
        m_iBallCenterY = m_RectVideo.top + BALL_RADIUS ;
    else if (m_iBallCenterY > m_RectVideo.bottom - BALL_RADIUS)
        m_iBallCenterY = m_RectVideo.bottom - BALL_RADIUS ;
}

#endif // SHOW_BALL
