/* ***** BEGIN LICENSE BLOCK *****
 * Source last modified: $Id: winroot.cpp,v 1.5.4.2 2004/07/26 10:28:17 pankajgupta 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 "hxcom.h"
#include "hxtypes.h"
#include "hxwintyp.h"
#include "hxvsurf.h"
#include "hxslist.h"
#include "hxerror.h"
#include "hxstrutl.h"
#include "hxver.h"

//#include "surfroot.h"
//#include "surfwles.h"
//#include "sitewles.h"
#include "winroot.h" // this muyst be included before colormap.h
#include "colormap.h"
#include "hxprefs.h"
#include "hxtick.h"
#include "ddpdb.h"

#include "winsite.h"

#ifndef _WIN32
#error This is the Macintosh platform specific implementation.
#endif

typedef HRESULT (HXEXPORT_PTR FPDIRECTDRAWCREATE)(GUID FAR * lpGUID,
                                                  LPDIRECTDRAW FAR * lplpDD, IUnknown FAR * pUnkOuter);

#define _DEBUGGING_WINDOWED_RENDERERS

CWinBaseRootSurface::CWinBaseRootSurface(IUnknown* pContext, CHXBaseSite* pSite) :
CBaseRootSurface(pContext, pSite),
    m_bWindrawOpened(FALSE),
    m_bWindrawCapsOpened(FALSE),
    m_bBltLock(FALSE),
    m_pScratchSurface(0),
    m_nScratchSurfaceCID(0),
    m_bScratchSurfaceCreated(FALSE),
    m_bShowingText(FALSE),
    m_hBrush(0),
    m_hPen(0),
    m_textColor(0),
    m_nTextBackgroundColor(0),
    m_hFont(0),
    m_hOldFont(0),
    m_hLastClipper(0),
    m_pErrMsg(NULL),
    m_pYUVScratchBits(NULL)
{
    memset( &m_windraw, 0, sizeof( m_windraw) );
    memset( &m_windrawCaps, 0, sizeof( m_windrawCaps) );
    memset( &m_compositionSurface, 0, sizeof( m_compositionSurface) );
}

CWinBaseRootSurface::~CWinBaseRootSurface()
{
	_DestroyCompositionSurface();

#ifdef _WORKAROUND_YUV_RGB_16BIT_HXCOLOR_BUG
    DestroyScratchSurface();
#endif /*_WORKAROUND_YUV_RGB_16BIT_HXCOLOR_BUG*/

    if (m_bWindrawOpened)
    {
        m_overlayMgr.RemoveAll();
        WinDraw2_Close(&m_windraw);
        m_bWindrawOpened = FALSE;
    }

    if (m_bWindrawCapsOpened)
    {
        WinDraw2_Close(&m_windrawCaps);
        m_bWindrawCapsOpened = FALSE;
    }

    HX_RELEASE(m_pErrMsg);
}

static void OpenDDPBD()
{
    HKEY    hKey;
    long    lSize = _MAX_PATH-1;
    char    szDBFileName[_MAX_PATH]; /* Flawfinder: ignore */

    const char* szKeyName = "Software\\"HXVER_COMMUNITY"\\Preferences\\DT_Plugins";
    if (RegOpenKeyEx(HKEY_CLASSES_ROOT, szKeyName, 0, KEY_READ, &hKey) == ERROR_SUCCESS)
    {
        /* retrieve path name: */
        memset(szDBFileName, 0, _MAX_PATH);
        if (RegQueryValue(hKey, "", szDBFileName, &lSize) == ERROR_SUCCESS) {

            /* convert it at a string: */
            SafeStrCat(szDBFileName, "\\pvdu3260.bin", _MAX_PATH);

            /*
			* Open Database file to check for buggy video drivers
			* If the file has not changed since the last time we read this
			* information, this call is basically a NO-OP
			*/
            DDPDB_Open(szDBFileName);
        }

        /* close hKey: */
        RegCloseKey(hKey);
    }
}

HX_RESULT CWinBaseRootSurface::_DebugBlt(UCHAR*                 /*IN*/  pImageData,
                                         HXBitmapInfoHeader*   /*IN*/  pBitmapInfo,
                                         REF(HXxRect)           /*IN*/  rDestRect,
                                         REF(HXxRect)           /*IN*/  rSrcRect)
{
    if (m_pSite)
    {
        HXxWindow* pWindow = m_pSite->GetWindow();
        if (pWindow && pWindow->window)
        {
            HWND hwnd = (HWND) pWindow->window;
            HDC hDC = GetDC(hwnd);

            HBRUSH      hBrush          = CreateSolidBrush(0x00008000);
            HPEN        hPen            = CreatePen(PS_SOLID, 1, 0x00FF0000);
            HBRUSH      hOldBrush       = (HBRUSH)SelectObject(hDC, hBrush);
            HPEN        hOldPen         = (HPEN)SelectObject(hDC, hPen);

            Rectangle(hDC,rDestRect.left,rDestRect.top,rDestRect.right,rDestRect.bottom);

            SelectObject(hDC,hOldBrush);
            SelectObject(hDC,hOldPen);
            DeleteObject(hBrush);
            DeleteObject(hPen);
            ReleaseDC(hwnd, hDC);
        }
    }

    return HXR_OK;
}

void CWinBaseRootSurface::_GetYUVScratchWidthHeight(UINT32* pWidth, UINT32* pHeight)
{
    *pWidth     = m_YUVScratchbmi.bmiHeader.biWidth;
    *pHeight    = m_YUVScratchbmi.bmiHeader.biHeight;
}

void CWinBaseRootSurface::_CreateYUVScratchSurface(UINT32 width, UINT32 height)
{
    HX_VECTOR_DELETE(m_pYUVScratchBits);
    m_pYUVScratchBits = NULL;
    memset(&m_YUVScratchbmi, 0, sizeof(BITMAPINFO));
    if (MakeBitmap(&m_YUVScratchbmi, sizeof(BITMAPINFO), 0, width, height, NULL, NULL))
    {
	m_pYUVScratchBits = new UCHAR[m_YUVScratchbmi.bmiHeader.biSizeImage];
	m_nYUVPitch = GetBitmapPitch(&m_YUVScratchbmi);
    }
}

void CWinBaseRootSurface::_GetYUVScratchSurfacePointer(UCHAR** pYUVBits, INT32* YUVPitch)
{
    *pYUVBits = m_pYUVScratchBits;
    *YUVPitch = m_nYUVPitch;
}

void CWinBaseRootSurface::_BltFromScratchToComposition(REF(HXxRect) rDestRect, REF(HXxRect) rSrcRect)
{
    WinDrawSurface_Unlock(&m_windraw, &m_compositionSurface, 0);
    WinDrawSurface_BltIndirect(&m_windraw, &m_compositionSurface, &m_scratchSurface, (RECT*)&rDestRect, (RECT*)&rSrcRect);
    WinDrawSurface_Lock(&m_windraw, &m_compositionSurface, 0,
		(void**) &m_pCompositionSurface, (LONG*)&m_nCompositionPitch);
}

HX_RESULT CWinBaseRootSurface::_LockComposition(UCHAR** pBits, INT32* pPitch)
{
    if (NOERROR == WinDrawSurface_Lock(&m_windraw, &m_compositionSurface, 0,
        (void**) pBits, (LONG*)pPitch))
    {
        return HXR_OK;
    }
    return HXR_FAIL;
}

HX_RESULT CWinBaseRootSurface::OpenWindraw()
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_bWindrawOpened)
    {
        return HXR_OK;
    }

    OpenDDPBD();

    if (m_pSite)
    {
        if (!m_pErrMsg)
            m_pContext->QueryInterface(IID_IHXErrorMessages, (void**)&m_pErrMsg);

        HXxWindow* pWindow = m_pSite->GetWindow();
        if (pWindow && pWindow->window)
        {
            memset(&m_windraw, 0, sizeof(WINDRAW));

            HRESULT failed = TRUE;

            if (m_fSurfaceType & HX_ACCELERATION_ON)
            {
                failed = WinDraw2_Open(&m_windraw, (HWND) pWindow->window, WINDRAW_DIRECTDRAW, NULL, NULL);
                // do any required cleanup
                if (failed)
                {
                    WinDraw2_Close(&m_windraw);
                }
                else
                {
                    // Store the accelerated windraw
                    m_windrawCaps = m_windraw;
                    m_windraw.pErrMsg = m_pErrMsg;

                    WinDraw2_GetMonitorProperties(&m_windraw);
                }
            }

            /* go to GDI mode */
            if (failed)
            {
                m_fSurfaceType  &= ~HX_ACCELERATION_ON;
                m_fSurfaceType  &= ~HX_USE_VIDEO_MEMORY;
                m_fSurfaceType  &= ~HX_USE_SYSTEM_MEMORY;
                failed = WinDraw2_Open(&m_windraw, (HWND) pWindow->window, NULL, NULL, NULL);
            }

            if (!failed)
            {
                retVal = HXR_OK;
                m_bWindrawOpened = TRUE;
            }
        }
    }
    return retVal;
}

void CWinBaseRootSurface::_MinimalBlt(HXxRect& src, HXxRect& dst)
{
}

HX_RESULT CWinBaseRootSurface::_CreateCompositionSurface()
{
    HX_RESULT retVal = HXR_FAIL;

    OpenWindraw();
    if (m_bCompositionSurfaceCreated)
    {
        return HXR_OK;
    }

    if (!m_bWindrawOpened)
    {
        return retVal;
    }

    // Can we restore hw acceleratoin (don't do it during a video surface switch)
    if((m_fSurfaceType & HX_ACCELERATION_ON) == HX_ACCELERATION_ON)
    {
        if(!_IsHardwareAccelerationAvail())
        {
            _LoseHardwareAcceleration();
            return HXR_OK;
        }
    }
    else if( m_bWindrawCapsOpened )
    {
        if( _IsHardwareAccelerationAvail() )
            _AcquireHardwareAcceleration();
        return HXR_OK;
    }

    // get primary surface CID.
    BMI primaryBMI;
    memset(&primaryBMI, 0, sizeof(BMI));
    WinDraw2_GetDisplayFormat(&m_windraw, &primaryBMI);
    m_nCompositionSurfaceCID = GetBitmapColor((LPBITMAPINFO)&primaryBMI);

    // create a BMI to describe the composition surface
    memset(&primaryBMI, 0, sizeof(BMI));
    memcpy(&m_compositionSize, &m_pSite->m_size, sizeof(HXxSize)); /* Flawfinder: ignore */

    // find out how big we want to allocate.
    if (m_pSite->IsFullScreen())
    {
        m_allocatedCompositionSize.cx = GetSystemMetrics(SM_CXSCREEN);
        m_allocatedCompositionSize.cy = GetSystemMetrics(SM_CYSCREEN);
    }
    else
    {
        m_allocatedCompositionSize.cx = m_compositionSize.cx;
        m_allocatedCompositionSize.cy = m_compositionSize.cy;
    }

    if (m_compositionSize.cx > m_allocatedCompositionSize.cx  || m_compositionSize.cy > m_allocatedCompositionSize.cy)
    {
        m_allocatedCompositionSize.cx = m_compositionSize.cx;
        m_allocatedCompositionSize.cy = m_compositionSize.cy;
    }

    MakeBitmap((LPBITMAPINFO)&primaryBMI, sizeof(BMI), m_nCompositionSurfaceCID, m_allocatedCompositionSize.cx, m_allocatedCompositionSize.cy, NULL, NULL);

    // now attempt to create the surface
    memset(&m_compositionSurface, 0, sizeof(WINDRAWSURFACE));

    /*
	*  Now are we in 8 bit color mode? If so we will need to make a palette.
	*/

    if (m_nCompositionSurfaceCID == CID_RGB8)
    {
        HDC hDC;
        int n = 0;

        hDC = GetDC (NULL);
        n = GetSystemPaletteEntries (hDC, 0, 256, (LPPALETTEENTRY) primaryBMI.dwPalette);
        ReleaseDC (NULL,hDC);

    }

    if (NOERROR == WinDraw2_CreateSurface(&m_windraw, &m_compositionSurface, &primaryBMI, m_fSurfaceType, 0, 0))
    {
        retVal = HXR_OK;
        m_bCompositionSurfaceCreated = TRUE;
    }

    primaryBMI.bmiHeader.biWidth    = 1;
    primaryBMI.bmiHeader.biHeight   = 1;

    return retVal;
}

HX_RESULT CWinBaseRootSurface::_DestroyCompositionSurface()
{
    HX_RESULT retVal = HXR_OK;

    if (m_bCompositionSurfaceCreated)
    {
		if (NOERROR == WinDraw2_ReleaseSurface(&m_windraw, &m_compositionSurface))
		{
			m_bCompositionSurfaceCreated = FALSE;
			retVal = HXR_OK;
		}
		else
		{
			retVal = HXR_FAIL;
		}
    }
    return retVal;
}

HX_RESULT CWinBaseRootSurface::_MinimalUnlock(HXxWindow* pWindow)
{
    if( m_pSite->IsCompositionLocked() )
    {
#if 0
        if (m_pUnlockingSite && m_pUnlockingSite->HasFocusRect())
        {
            DrawFocusRect(&m_compositionSurface,
                          m_nCompositionSurfaceCID,
                          &m_allocatedCompositionSize,
                          m_pCompositionSurface,
                          m_pUnlockingSite);
        }
#endif

        WinDrawSurface_Unlock(&m_windraw, &m_compositionSurface, 0);
        return HXR_OK;
    }

#if 0
    if (m_pUnlockingSite && m_pUnlockingSite->HasFocusRect())
    {
        DrawFocusRect(&m_compositionSurface,
                      m_nCompositionSurfaceCID,
                      &m_allocatedCompositionSize,
                      m_pCompositionSurface,
                      m_pUnlockingSite);
    }
#endif

    HX_RESULT retVal = HXR_FAIL;
    if (NOERROR == WinDrawSurface_Unlock(&m_windraw, &m_compositionSurface, 0))
    {
        m_pCompositionSurface = NULL;

        if (!m_bBltLock)
        {
            // Assuming that no windowed renderer uses video services this will work.
            // Should fix this so that windowed renderers can use video services.

/*
            if (m_hLastClipper != (HWND) pWindow->window)
            {
                m_hLastClipper = (HWND) pWindow->window;
                WinDrawSurface_SetClipper(&m_windraw, (HWND) pWindow->window);
            }
*/

/*           if( m_boundsRect.bottom > m_boundsRect.top && m_boundsRect.left   < m_boundsRect.right)
            {
                //              {
                //                 char szBuff[256];
                //                 sprintf( szBuff,"Bounding Rect: (%d, %d) --- (%d, %d)\n",
                //                          m_boundsRect.left, m_boundsRect.top,
                //                          m_boundsRect.right, m_boundsRect.bottom);
                //                 _DumpString(szBuff);
                //              }
                if (NOERROR == WinDrawSurface_Blt(&m_windraw, &m_compositionSurface, (RECT*)&m_boundsRect, (RECT*)&m_boundsRect))
                {
                   retVal = HXR_OK;
                }
            }  */
            if (m_pBltRects.GetCount())
            {
                while(m_pBltRects.GetCount())
                {
                    HXxRect* pRect = (HXxRect*) m_pBltRects.RemoveHead();

                    if (NOERROR == WinDrawSurface_Blt(&m_windraw, &m_compositionSurface, (RECT*)pRect, (RECT*)pRect))
                    {
                        retVal = HXR_OK;
                    }
                    HX_DELETE(pRect);

                }
            }
        }
    }
    return retVal;
}

HX_RESULT CWinBaseRootSurface::ScratchLock(UCHAR** pBits, INT32* pPitch)
{
    HX_RESULT retVal = HXR_FAIL;

    if (NOERROR == WinDrawSurface_Lock(&m_windraw, &m_scratchSurface, 0,
		(void**) &m_pScratchSurface, (LONG*)&m_nScratchPitch))
    {
	    retVal = HXR_OK;
	    HX_ASSERT(m_pScratchSurface);
	    *pBits = m_pScratchSurface;
	    *pPitch = m_nScratchPitch;
    }

    return retVal;
}

HX_RESULT CWinBaseRootSurface::ScratchUnlock(UCHAR* pBits)
{
    HX_RESULT retVal = HXR_FAIL;
    if (NOERROR == WinDrawSurface_Unlock(&m_windraw, &m_scratchSurface, 0))
    {
		m_pScratchSurface = NULL;
    }
    return retVal;
}

HX_RESULT CWinBaseRootSurface::CreateScratchSurface(int nCompositionSurfaceCID, HXxSize* pSize)
{
    HX_RESULT retVal = HXR_FAIL;

    if (m_bScratchSurfaceCreated &&
		m_nScratchSurfaceCID == nCompositionSurfaceCID &&
		pSize->cx <= m_allocatedScratchSize.cx &&
		pSize->cy <= m_allocatedScratchSize.cy)
    {
		if (m_scratchSize.cx != pSize->cx || m_scratchSize.cy != pSize->cy)
		{
			memcpy(&m_scratchSize, pSize, sizeof(HXxSize)); /* Flawfinder: ignore */
		}

		return HXR_OK;
    }
    else
    {
		DestroyScratchSurface();
    }

    HX_ASSERT(m_bScratchSurfaceCreated == FALSE);

    m_nScratchSurfaceCID = nCompositionSurfaceCID;

    // create a BMI to describe the composition surface
    memcpy(&m_scratchSize, pSize, sizeof(HXxSize)); /* Flawfinder: ignore */
    memcpy(&m_allocatedScratchSize, pSize, sizeof(HXxSize)); /* Flawfinder: ignore */

    BMI primaryBMI;
    memset(&primaryBMI, 0, sizeof(BMI));
    MakeBitmap((LPBITMAPINFO)&primaryBMI, sizeof(BMI), m_nScratchSurfaceCID, m_allocatedScratchSize.cx, m_allocatedScratchSize.cy, NULL, NULL);

    // now attempt to create the surface
    memset(&m_scratchSurface, 0, sizeof(WINDRAWSURFACE));
    if (NOERROR == WinDraw2_CreateSurface(&m_windraw, &m_scratchSurface, &primaryBMI, m_fSurfaceType, 0, 0))
    {
		retVal = HXR_OK;
		m_bScratchSurfaceCreated = TRUE;
    }

    return retVal;
}

HX_RESULT CWinBaseRootSurface::DestroyScratchSurface()
{
    HX_RESULT retVal = HXR_OK;

    if (m_bScratchSurfaceCreated)
    {
		if (NOERROR == WinDraw2_ReleaseSurface(&m_windraw, &m_scratchSurface))
		{
			m_bScratchSurfaceCreated = FALSE;
			retVal = HXR_OK;
		}
		else
		{
			retVal = HXR_FAIL;
		}
    }
    return retVal;
}

void CWinBaseRootSurface::SetFullScreen()
{
    m_bBltLock = FALSE;

    RECT boundRect = {0,0,0,0};

    boundRect.right	= GetSystemMetrics(SM_CXSCREEN);
    boundRect.bottom    = GetSystemMetrics(SM_CYSCREEN);
    WinDrawSurface_Blt(&m_windraw, &m_compositionSurface, (RECT*)&boundRect, (RECT*)&boundRect);
}

void CWinBaseRootSurface::PrepareExitFullScreen()
{
    memcpy(&m_compositionSize, &m_pSite->m_size, sizeof(HXxSize)); /* Flawfinder: ignore */
}

void CWinBaseRootSurface::RestoreResolution()
{
    WinDraw2_RestoreResolution(&m_windraw);
}

void CWinBaseRootSurface::_AdjustLastAppendedRectangle(REF(CHXSimpleList) rectList)
{
}

void CWinBaseRootSurface::_PreFlightBlt(HXxRect& dst)
{
}

void CWinBaseRootSurface::PrepareFullScreen()
{
    HDC hDC;

    WinDrawSurface_GetDC(&m_windraw, &m_compositionSurface, &hDC);
    HBRUSH blackBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
    HBRUSH oldBrush = (HBRUSH)SelectObject(hDC, blackBrush);
    HPEN nullPen = (HPEN)GetStockObject(NULL_PEN);
    HPEN oldPen = (HPEN)SelectObject(hDC, nullPen);
    Rectangle(hDC, 0,0, m_allocatedCompositionSize.cx, m_allocatedCompositionSize.cy);
    SelectObject(hDC, oldBrush);
    SelectObject(hDC, oldPen);
    WinDrawSurface_ReleaseDC(&m_windraw, &m_compositionSurface, hDC);
    HXxWindow* pWindow = m_pSite->GetWindow();
    if (pWindow && pWindow->window)
    {
	HWND hwnd = (HWND) pWindow->window;
	hDC = GetDC(hwnd);
	RECT destRect;
	GetClientRect(hwnd, &destRect);
	oldBrush = (HBRUSH)SelectObject(hDC, blackBrush);
	oldPen = (HPEN)SelectObject(hDC, nullPen);
	Rectangle(hDC, 0, 0, destRect.right+1, destRect.bottom+1);
	SelectObject(hDC, oldBrush);
	SelectObject(hDC, oldPen);
	ReleaseDC(hwnd, hDC);
    }
    m_bBltLock = TRUE;
}

void CWinBaseRootSurface::GetDisplayModes(CModesDesc* pModesDesc, INT32* nNumModes)
{
    OpenWindraw();
    WinDraw2_GetModes(&m_windraw, pModesDesc, (UINT32*)nNumModes);
}

void CWinBaseRootSurface::SetResolution(int width, int height, int depth, void* hwnd)
{
    OpenWindraw();
    WinDraw2_SetResolution(&m_windraw, width, height, depth, (HWND)hwnd);
    m_pCompMutex->Lock();
    _DestroyCompositionSurface();
    _CreateCompositionSurface();
    m_pCompMutex->Unlock();
}

BOOL CWinBaseRootSurface::_OptimizedSurfaceOpened()
{
    return m_bWindrawOpened;
}

void CWinBaseRootSurface::SelectFont(HDC hDC)
{
    if (!m_hFont)
    {
	int oldMappingMode = SetMapMode( hDC, MM_TEXT );
	int nHeight = -MulDiv(50, GetDeviceCaps(hDC, LOGPIXELSY), 72);

	m_hFont = CreateFont(nHeight, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, ANSI_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Ariel");
	SetMapMode( hDC, oldMappingMode);
    }

    m_hOldFont = (HFONT)SelectObject(hDC, m_hFont);
}

void CWinBaseRootSurface::UnSelectFont(HDC hDC)
{
     SelectObject(hDC, m_hOldFont);
}

void CWinBaseRootSurface::CreateTextAlpha(COverlay* pOverlay)
{
    /*
     *	The default function for text overlays is that anywhere
     *  that the pixels are set to a value the alpha will be set to
     *  128, everywhere else it will be set to 0
     */

    UINT32* pPixels = (UINT32*)pOverlay->LockOverlay();
    UCHAR*  pAlpha  = pOverlay->GetAlpha();
    HXxSize size;

    pOverlay->GetSize(size);
    int x,y;
    int nCID = pOverlay->GetCID();
    UINT32 rgbValue = 0;
    int state =0;

    switch(nCID)
    {
	case CID_RGB32:
	case CID_RGB24:
	for(y=0; y<size.cy; y++)
	{
	    for(x=0;x<size.cx;x++, pAlpha++)
	    {
		switch(state)
		{
		    case 0:
		    {
			rgbValue = (*pPixels && 0xFFFFFF00)>>8;
			state = 1;
			break;
		    }
		    case 1:
		    {
		    	rgbValue = ((*pPixels && 0x000000FF)<<16);
			pPixels++;
			rgbValue += ((*pPixels && 0xFFFFF0000)>>16);
			state = 2;
			break;
		    }
		    case 2:
		    {
		    	rgbValue = ((*pPixels && 0x0000FFFF)<<8);
			pPixels++;
			rgbValue += ((*pPixels && 0xFF000000)>>24);
			state = 3;
			break;
		    }
		    case 3:
		    {
		    	rgbValue = ((*pPixels && 0x00FFFFFF));
			pPixels++;
			state = 0;
			break;
		    }
		}
		*pAlpha = rgbValue ? 255 : 0;
	    }
	}
	break;
    }
    pOverlay->UnlockOverlay();
}

#if 0
void CWinBaseRootSurface::DrawFocusRect(WINDRAWSURFACE *pSurface,
                                        int nSurfaceCID,
                                        HXxSize *pSurfaceSize,
                                        UCHAR *pVidMem,
                                        CHXBaseSite* pSite)
{
    // Draw the focus for this site
    HXBitmapInfoHeader info;
    memset(&info, 0, sizeof(info));

    MakeBitmap((LPBITMAPINFO)&info, sizeof(info), nSurfaceCID,
            pSurfaceSize->cx, pSurfaceSize->cy, NULL, NULL);

    HDC hdc = 0;
    WinDrawSurface_GetDC(&m_windraw, pSurface, &hdc);
    HXxRect rc = {0,0,pSurfaceSize->cx, pSurfaceSize->cy};

    pSite->_DrawFocusRect(pVidMem,
                          &info,
                          &rc,
                          (void*)&hdc);

    WinDrawSurface_ReleaseDC(&m_windraw, pSurface, hdc);
}
#endif

void CWinBaseRootSurface::DrawText()
{
    SIZE textSize;
    m_bShowingText = TRUE;
    HXxSize overlaySize;

    COverlay* pOverlay;

    if (!m_overlayMgr.GetOverlay(TEXTOVERLAY, (COverlay**)&pOverlay))
    {
		HDC hDC = GetDC(NULL);
		SelectFont(hDC);
		GetTextExtentPoint(hDC, "XXX", 4, &textSize);
		UnSelectFont(hDC);
		ReleaseDC(0, hDC);

		overlaySize.cx = m_allocatedCompositionSize.cx;
		overlaySize.cy = textSize.cy+4;

		pOverlay = new COverlay(&m_windraw);
		pOverlay->CreateOverlay(overlaySize, m_nCompositionSurfaceCID);
		m_overlayMgr.AddOverlay(TEXTOVERLAY, pOverlay);
    }
    else
    {
		pOverlay->GetSize(overlaySize);
		if (overlaySize.cx < m_allocatedCompositionSize.cx)
		{
			m_overlayMgr.RemoveOverlay(TEXTOVERLAY, &m_compositionSurface);
			overlaySize.cx = m_allocatedCompositionSize.cx;
			pOverlay->CreateOverlay(overlaySize, m_nCompositionSurfaceCID);
			m_overlayMgr.AddOverlay(TEXTOVERLAY, pOverlay);
		}
    }

    RECT boundsRect;
    boundsRect.left	= 0;
    boundsRect.top	= 0;
    boundsRect.right	= m_compositionSize.cx;
    boundsRect.bottom	= textSize.cy;

    /*
	*	create ther drawing surface
	*/

    HDC hMemDC = NULL;
    pOverlay->GetDC(&hMemDC);

    if (!m_hBrush)
    {
		LOGBRUSH brush;

		brush.lbStyle = BS_SOLID;
		brush.lbColor =	m_nTextBackgroundColor;
		brush.lbHatch = 0;

		m_hBrush = CreateBrushIndirect(&brush);
    }

    if (!m_hPen)
    {
		m_hPen = CreatePen(PS_SOLID, 0, m_textColor);
    }

    HBRUSH	hOldBrush	= (HBRUSH)SelectObject(hMemDC, m_hBrush);
    HPEN	hOldPen		= (HPEN)SelectObject(hMemDC, m_hPen);

    /*
	*	figure out how much text we should use.
	*/

    CHXString	finalString = m_sStatusText;
    HXxPoint	textOffset;

    SelectFont(hMemDC);
    int length = m_sStatusText.GetLength();
    GetTextExtentPoint(hMemDC, (const char*)m_sStatusText, m_sStatusText.GetLength(), &textSize);
    if (textSize.cx > (boundsRect.right - boundsRect.left - 4))
    {
	/*
	*  we will loop to find out where to put the elipise
		*/
		for (int i = 1; i <= m_sStatusText.GetLength(); i++)
		{
			GetTextExtentPoint(hMemDC, (const char*)m_sStatusText, i, &textSize);
			if (textSize.cx > (boundsRect.right - boundsRect.left - 4))
			{
				for(int j = i - 1; j > 0 ; j--)
				{
					finalString = m_sStatusText.Left(j);
					finalString	= finalString + "...";
					GetTextExtentPoint(hMemDC, (const char*)finalString, finalString.GetLength(), &textSize);
					if (textSize.cx > (boundsRect.right - boundsRect.left - 4))
					{
						continue;
					}
					break;
				}
				textOffset.x = 3;
				textOffset.y = 3;
			}
		}
    }
    else
    {
		textOffset.x	= (m_compositionSize.cx - textSize.cx) / 2;
		textOffset.y	= 3;
    }

    /*
	*	Do the drawing
	*/
    RECT rectBounds;

    rectBounds.left	= (m_compositionSize.cx - textSize.cx)/2 - 2;
    rectBounds.right	= (m_compositionSize.cx + textSize.cx)/2 + 2;
    rectBounds.top	= 0;
    rectBounds.bottom	= textSize.cy+4;

    int delta = 2;

    BOOL ret = Rectangle(hMemDC, rectBounds.top - delta, rectBounds.left - delta, rectBounds.bottom + delta, rectBounds.right + delta);

    COLORREF oldColor = SetTextColor(hMemDC, m_textColor);
    COLORREF oldBkColor	= SetBkColor(hMemDC, m_nTextBackgroundColor);
    ret = TextOut(hMemDC, textOffset.x, textOffset.y, finalString, finalString.GetLength());
    SetBkColor(hMemDC, oldBkColor);
    SetTextColor(hMemDC, oldColor);
    UnSelectFont(hMemDC);

    SelectObject(hMemDC, hOldBrush);
    SelectObject(hMemDC, hOldPen);
    GdiFlush();

    pOverlay->ReleaseDC(hMemDC);
    pOverlay->SetSrcRect((HXxRect&)rectBounds);

    CreateTextAlpha(pOverlay);

    rectBounds.top	= m_compositionSize.cy - (textSize.cy+4);
    rectBounds.bottom	= m_compositionSize.cy;

    pOverlay->SetDestRect((HXxRect&)rectBounds, &m_compositionSurface);
}

void CWinBaseRootSurface::_FillBorders()
{
   HXxWindow* pWindow = m_pSite->GetWindow();
   if (pWindow && pWindow->window)
   {
      HWND hwnd = (HWND) pWindow->window;
      HDC hDC = GetDC(hwnd);
      RECT destRect;
      GetClientRect(hwnd, &destRect);
      HXxSize size;
      m_pSite->GetSize(size);
      HBRUSH blackBrush = (HBRUSH)GetStockObject(BLACK_BRUSH);
      HBRUSH oldBrush = (HBRUSH)SelectObject(hDC, blackBrush);
      HPEN nullPen = (HPEN)GetStockObject(NULL_PEN);
      HPEN oldPen = (HPEN)SelectObject(hDC, nullPen);
      if (size.cx < destRect.right)
      {
         Rectangle(hDC, 0, 0, (destRect.right - size.cx + 1) / 2 + 1, destRect.bottom+1);
         Rectangle(hDC, destRect.right - ((destRect.right - size.cx + 1) / 2), 0, destRect.right+1, destRect.bottom+1);
      }
      else
      {
         Rectangle(hDC, 0, 0, destRect.right+1, (destRect.bottom - size.cy + 1) / 2 + 1);
         Rectangle(hDC, 0, destRect.bottom - ((destRect.bottom - size.cy + 1) / 2), destRect.right+1, destRect.bottom+1);
      }

      SelectObject(hDC, oldBrush);
      SelectObject(hDC, oldPen);
      ReleaseDC(hwnd, hDC);
   }
}

BOOL CWinBaseRootSurface::_IsHardwareAccelerationAvail()
{
    return WinDraw2_IsDDAvailable(&m_windrawCaps);
}

BOOL CWinBaseRootSurface::_AcquireHardwareAcceleration()
{
    if (!m_bWindrawOpened)
        return FALSE;

    m_pCompMutex->Lock();

    // Close accelerated windraw instance
    if (m_bWindrawCapsOpened)
    {
	WinDraw2_Close(&m_windrawCaps);
	memset(&m_windrawCaps, 0, sizeof(m_windrawCaps));
	m_bWindrawCapsOpened = FALSE;
    }

    _DestroyCompositionSurface();

    // Close the non accelrated windraw instance
    WinDraw2_Close(&m_windraw);
    m_bWindrawOpened = FALSE;

    // Turn on acceleration
    m_fSurfaceType |= HX_ACCELERATION_ON;

    if (m_bUseCardMemory)
        m_fSurfaceType |= HX_USE_VIDEO_MEMORY;
    else
        m_fSurfaceType |= HX_USE_SYSTEM_MEMORY;

    _CreateCompositionSurface();
    m_pCompMutex->Unlock();

    return TRUE;
}

BOOL CWinBaseRootSurface::_LoseHardwareAcceleration()
{
    if (!m_bWindrawOpened)
        return FALSE;

    m_pCompMutex->Lock();
    _DestroyCompositionSurface();

    m_overlayMgr.RemoveAll();

    m_windrawCaps = m_windraw;

    memset(&m_windraw, 0, sizeof(m_windraw));
    m_bWindrawOpened = FALSE;

    // Preserve the DD instance
    m_bWindrawCapsOpened = TRUE;

    // Turn off acceleration
    m_fSurfaceType &= ~HX_ACCELERATION_ON;
    m_fSurfaceType &= ~HX_USE_VIDEO_MEMORY;
    m_fSurfaceType &= ~HX_USE_SYSTEM_MEMORY;

    _CreateCompositionSurface();
    m_pCompMutex->Unlock();

    return TRUE;
}

BOOL CWinBaseRootSurface::_EnableHardwareAcceleration()
{
    // If hardware acceleration is disabled, enabled it
    if ((m_fSurfaceType & HX_ACCELERATION_ON) != HX_ACCELERATION_ON)
    {
        BOOL bCreateCompSurface = FALSE;

        m_pCompMutex->Lock();

        // If we have an "unaccelerated" composition surface, destroy it
        if(HXR_OK == _DestroyCompositionSurface())
            bCreateCompSurface = TRUE;

        // Close rgb windraw instance
        if (m_bWindrawOpened)
        {
            m_overlayMgr.RemoveAll();
            WinDraw2_Close(&m_windraw);
            m_bWindrawOpened = FALSE;
        }

        // Turn on acceleration
        m_fSurfaceType |= HX_ACCELERATION_ON;

        if (m_bUseCardMemory)
            m_fSurfaceType |= HX_USE_VIDEO_MEMORY;
        else
            m_fSurfaceType |= HX_USE_SYSTEM_MEMORY;

        // Create an "accelerated" composition surface if we need one
        if(bCreateCompSurface)
            _CreateCompositionSurface();

        m_pCompMutex->Unlock();
    }

    return TRUE;
}

BOOL CWinBaseRootSurface::_DisableHardwareAcceleration()
{
    // If hardware acceleration is enabled, disable it
    if ((m_fSurfaceType & HX_ACCELERATION_ON) == HX_ACCELERATION_ON)
    {
        BOOL bCreateCompSurface = FALSE;

        m_pCompMutex->Lock();

        // If we have an "accelerated" composition surface, destroy it
        if(HXR_OK == _DestroyCompositionSurface())
            bCreateCompSurface = TRUE;

        // Close yuv windraw instance
        if (m_bWindrawOpened)
        {
            m_overlayMgr.RemoveAll();
            WinDraw2_Close(&m_windraw);
            m_bWindrawOpened = FALSE;
        }

        // Close yuv caps windraw instance
        if (m_bWindrawCapsOpened)
        {
            WinDraw2_Close(&m_windrawCaps);
            m_bWindrawCapsOpened = FALSE;
        }

        // Turn off acceleration
        m_fSurfaceType &= ~HX_ACCELERATION_ON;
        m_fSurfaceType &= ~HX_USE_VIDEO_MEMORY;
        m_fSurfaceType &= ~HX_USE_SYSTEM_MEMORY;

        // Create an "unaccelerated" composition surface if we need one
        if(bCreateCompSurface)
            _CreateCompositionSurface();

        m_pCompMutex->Unlock();

    }

    return TRUE;
}

/**********************************************************************
*
*	COverlayMgr
*
*   A class which manages the internals of video overlays.
*
**********************************************************************/

COverlayMgr::COverlayMgr()
{
}

COverlayMgr::~COverlayMgr()
{
    RemoveAll();
}

BOOL COverlayMgr::GetOverlay(const char* pName, COverlay** pOverlay)
{
    return m_overlayMap.Lookup(pName, (void*&)pOverlay);
}

void COverlayMgr::RemoveOverlay(const char* pName, WINDRAWSURFACE* pDestSurface)
{
    COverlay* pOverlay;

    if (m_overlayMap.Lookup(pName, (void*&)pOverlay))
    {
		m_overlayMap.RemoveKey(pName);
		pOverlay->RemoveOverlay(pDestSurface);
		HX_DELETE(pOverlay);
    }
}

void COverlayMgr::RemoveAll()
{
    COverlay* pOverlay;

    CHXMapStringToOb::Iterator i = m_overlayMap.Begin();

    for(;i != m_overlayMap.End(); ++i)
    {
		pOverlay = (COverlay*) *i;
		HX_DELETE(pOverlay);
    }
    m_overlayMap.RemoveAll();
}

void COverlayMgr::AddOverlay(const char* pName, COverlay* pOverlay)
{
    m_overlayMap.SetAt(pName, (void*)pOverlay);
}

void COverlayMgr::UpdateOverlays(HXxRect* pRect, WINDRAWSURFACE* pSrcSurface)
{
    CHXMapStringToOb::Iterator i = m_overlayMap.Begin();

    for(;i!=m_overlayMap.End();++i)
    {
		COverlay* pOverlay = (COverlay*)*i;
		pOverlay->UpdateOverlay(pRect, pSrcSurface);
    }
}

/**********************************************************************
*
*	COverlay
*
*   A class which manages the internals of video overlays.
*
**********************************************************************/


COverlay::COverlay(WINDRAW* pWindraw)
:	m_nCID(0)
,	m_nAlpha(0)
,	m_pWindraw(0)
{
    m_pWindraw = pWindraw;
    memset(&m_backgroundSurface, 0, sizeof(WINDRAWSURFACE));
    memset(&m_overlaySurface, 0, sizeof(WINDRAWSURFACE));
    memset(&m_destRect, 0, sizeof(HXxRect));
    memset(&m_srcRect, 0, sizeof(HXxRect));
    memset(&m_backgroundSrcRect, 0, sizeof(HXxRect));
    memset(&m_allocSize, 0, sizeof(HXxRect));
}

COverlay::~COverlay()
{
    WinDraw2_ReleaseSurface(m_pWindraw, &m_overlaySurface);
    WinDraw2_ReleaseSurface(m_pWindraw, &m_backgroundSurface);
}

void COverlay::CreateOverlay(REF(HXxSize) size, int nCID)
{
    BMI textBMI;

    m_nCID = nCID;
    memcpy(&m_allocSize, &size, sizeof(HXxSize)); /* Flawfinder: ignore */
    MakeBitmap((LPBITMAPINFO)&textBMI, sizeof(BITMAPINFO), m_nCID, size.cx, size.cy, NULL, NULL);
    HX_VERIFY(NOERROR == WinDraw2_CreateSurface(m_pWindraw, &m_overlaySurface, &textBMI, NULL, 0, 0));
}

void COverlay::SetSrcRect(REF(HXxRect) rect)
{
    memcpy(&m_srcRect, &rect, sizeof(HXxRect)); /* Flawfinder: ignore */
}

void COverlay::SetAlpha(int nAlpha)
{
    m_nAlpha = nAlpha;
}

void COverlay::GetSize(REF(HXxSize) size)
{
    memcpy(&size, &m_allocSize, sizeof(HXxSize)); /* Flawfinder: ignore */
}

UCHAR*	COverlay::LockOverlay()
{
    UCHAR*  retVal;
    LONG    pitch;

    WinDrawSurface_Lock(m_pWindraw, &m_overlaySurface, 0, (void**)&retVal, &pitch);
    return retVal;
}

void COverlay::UnlockOverlay()
{
    WinDrawSurface_Unlock(m_pWindraw, &m_overlaySurface, 0);
}

UCHAR* COverlay::GetAlpha()
{
    UCHAR* retVal = NULL;

    WinDraw2_CreateAlphaSurface(m_pWindraw, &m_overlaySurface);
    WinDraw2_GetAlphaSurface(m_pWindraw, &m_overlaySurface, &retVal);
    return retVal;
}

int COverlay::GetCID()
{
    return m_nCID;
}

void COverlay::GetDC(HDC* phDC)
{
    WinDrawSurface_GetDC(m_pWindraw, &m_overlaySurface, phDC);
}

void COverlay::ReleaseDC(HDC hDC)
{
    WinDrawSurface_ReleaseDC(m_pWindraw, &m_overlaySurface, hDC);
}

void COverlay::SetDestRect(REF(HXxRect) rect, WINDRAWSURFACE* pDestSurface)
{
/*
*	if the dest rect changes it is time to recreate the surface.
	*/
    if (memcmp(&m_destRect, &rect, sizeof(RECT)))
    {

	/*
	*  get a buffer of what is on the composition surface
		*/

		if (rect.right - rect.left != m_destRect.right - m_destRect.left ||
			rect.bottom - rect.top != m_destRect.bottom - m_destRect.top )
		{
			BMI textBMI;
			MakeBitmap((LPBITMAPINFO)&textBMI, sizeof(BITMAPINFO), m_nCID, rect.right - rect.left, rect.bottom - rect.top, NULL, NULL);
			WinDraw2_ReleaseSurface(m_pWindraw, &m_backgroundSurface);
			HX_VERIFY(NOERROR == WinDraw2_CreateSurface(m_pWindraw, &m_backgroundSurface, &textBMI, NULL, 0, 0));
		}

		memcpy(&m_destRect, &rect, sizeof(HXxRect)); /* Flawfinder: ignore */
		memset(&m_backgroundSrcRect, 0, sizeof(RECT));
		m_backgroundSrcRect.right   = rect.right - rect.left;
		m_backgroundSrcRect.bottom  = rect.bottom - rect.top;
		WinDrawSurface_BltIndirect(m_pWindraw, &m_backgroundSurface, pDestSurface, (RECT*)&m_backgroundSrcRect, (RECT*)&rect);
    }
}

void COverlay::DrawOverlay(WINDRAWSURFACE* pDestSurface)
{
    WinDrawSurface_BltIndirect(m_pWindraw, pDestSurface, &m_backgroundSurface, (RECT*)&m_backgroundSrcRect, (RECT*)&m_destRect);
    WinDrawSurface_BltIndirect(m_pWindraw, pDestSurface, &m_overlaySurface, (RECT*)&m_srcRect, (RECT*)&m_destRect);
}

void COverlay::RemoveOverlay(WINDRAWSURFACE* pDestSurface)
{
    WinDrawSurface_BltIndirect(m_pWindraw, pDestSurface, &m_backgroundSurface, (RECT*)&m_backgroundSrcRect, (RECT*)&m_destRect);
}

void COverlay::UpdateOverlay(HXxRect* pRect, WINDRAWSURFACE* pSrcSurface)
{
    RECT tempRect;
    if (IntersectRect( &tempRect, (RECT*)pRect, (RECT*) &m_destRect))
    {
		/* yes they intersected */
		RECT destRect;
		destRect.left	= tempRect.left	    - m_destRect.left;
		destRect.top	= tempRect.top	    - m_destRect.top;
		destRect.right	= tempRect.right    - m_destRect.left;
		destRect.bottom	= tempRect.bottom   - m_destRect.top;

		/* update the background */
		WinDrawSurface_BltIndirect(m_pWindraw, &m_backgroundSurface, pSrcSurface, &destRect, &tempRect);

		destRect.right	= tempRect.right;
		destRect.left	= tempRect.left;

		/* Now blt */
		WinDrawSurface_BltIndirect(m_pWindraw, pSrcSurface, &m_overlaySurface, (RECT*)&tempRect, &destRect);
    }
}
