/*
 *  Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
 *
 *  Use of this source code is governed by a BSD-style license
 *  that can be found in the LICENSE file in the root of the source
 *  tree. An additional intellectual property rights grant can be found
 *  in the file PATENTS.  All contributing project authors may
 *  be found in the AUTHORS file in the root of the source tree.
 */

#include "engine_configurations.h"
#include "video_render_windows_impl.h"

#include "critical_section_wrapper.h"
#include "trace.h"
#ifdef DIRECTDRAW_RENDERING
#include "video_render_directdraw.h"
#endif
#ifdef DIRECT3D9_RENDERING
#include "video_render_direct3d9.h"
#endif

#include <tchar.h>

namespace webrtc {

VideoRenderWindowsImpl::VideoRenderWindowsImpl(
                                               const WebRtc_Word32 id,
                                               const VideoRenderType videoRenderType,
                                               void* window,
                                               const bool fullscreen) :
            _id(id),
            _renderWindowsCritsect(
                                   *CriticalSectionWrapper::CreateCriticalSection()),
            _prtWindow(window), _fullscreen(fullscreen), _ptrRendererWin(NULL)
{
}

VideoRenderWindowsImpl::~VideoRenderWindowsImpl()
{
    delete &_renderWindowsCritsect;
    if (_ptrRendererWin)
    {
        delete _ptrRendererWin;
        _ptrRendererWin = NULL;
    }
}

WebRtc_Word32 VideoRenderWindowsImpl::Init()
{
    //LogOSAndHardwareDetails();
    CheckHWAcceleration();

    _renderMethod = kVideoRenderWinD3D9;

    // Create the win renderer
    switch (_renderMethod)
    {
        case kVideoRenderWinDd:
        {
#ifdef DIRECTDRAW_RENDERING
            VideoRenderDirectDraw* ptrRenderer;
            ptrRenderer = new VideoRenderDirectDraw(NULL, (HWND) _prtWindow, _fullscreen);
            if (ptrRenderer == NULL)
            {
                break;
            }
            _ptrRendererWin = reinterpret_cast<IVideoRenderWin*>(ptrRenderer);
#else
            return NULL;
#endif  //DIRECTDRAW_RENDERING
        }
            break;
        case kVideoRenderWinD3D9:
        {
#ifdef DIRECT3D9_RENDERING
            VideoRenderDirect3D9* ptrRenderer;
            ptrRenderer = new VideoRenderDirect3D9(NULL, (HWND) _prtWindow, _fullscreen);
            if (ptrRenderer == NULL)
            {
                break;
            }
            _ptrRendererWin = reinterpret_cast<IVideoRenderWin*>(ptrRenderer);
#else
            return NULL;
#endif  //DIRECT3D9_RENDERING
        }
            break;
        default:
            break;
    }

    //Init renderer
    if (_ptrRendererWin)
        return _ptrRendererWin->Init();
    else
        return -1;
}

WebRtc_Word32 VideoRenderWindowsImpl::ChangeUniqueId(const WebRtc_Word32 id)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    _id = id;
    return 0;
}

WebRtc_Word32 VideoRenderWindowsImpl::ChangeWindow(void* window)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
        return -1;
    }
    else
    {
        return _ptrRendererWin->ChangeWindow(window);
    }
}

VideoRenderCallback*
VideoRenderWindowsImpl::AddIncomingRenderStream(const WebRtc_UWord32 streamId,
                                                const WebRtc_UWord32 zOrder,
                                                const float left,
                                                const float top,
                                                const float right,
                                                const float bottom)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    VideoRenderCallback* renderCallback = NULL;

    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        renderCallback = _ptrRendererWin->CreateChannel(streamId, zOrder, left,
                                                        top, right, bottom);
    }

    return renderCallback;
}

WebRtc_Word32 VideoRenderWindowsImpl::DeleteIncomingRenderStream(
                                                                 const WebRtc_UWord32 streamId)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->DeleteChannel(streamId);
    }
    return error;
}

WebRtc_Word32 VideoRenderWindowsImpl::GetIncomingRenderStreamProperties(
                                                                        const WebRtc_UWord32 streamId,
                                                                        WebRtc_UWord32& zOrder,
                                                                        float& left,
                                                                        float& top,
                                                                        float& right,
                                                                        float& bottom) const
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    zOrder = 0;
    left = 0;
    top = 0;
    right = 0;
    bottom = 0;

    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->GetStreamSettings(streamId, 0, zOrder, left,
                                                   top, right, bottom);
    }
    return error;
}

WebRtc_Word32 VideoRenderWindowsImpl::StartRender()
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->StartRender();
    }
    return error;
}

WebRtc_Word32 VideoRenderWindowsImpl::StopRender()
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->StopRender();
    }
    return error;
}

VideoRenderType VideoRenderWindowsImpl::RenderType()
{
    return kRenderWindows;
}

RawVideoType VideoRenderWindowsImpl::PerferedVideoType()
{
    return kVideoI420;
}

bool VideoRenderWindowsImpl::FullScreen()
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    bool fullscreen = false;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        fullscreen = _ptrRendererWin->IsFullScreen();
    }
    return fullscreen;
}

WebRtc_Word32 VideoRenderWindowsImpl::GetGraphicsMemory(
                                                        WebRtc_UWord64& totalGraphicsMemory,
                                                        WebRtc_UWord64& availableGraphicsMemory) const
{
    if (_ptrRendererWin)
    {
        return _ptrRendererWin->GetGraphicsMemory(totalGraphicsMemory,
                                                  availableGraphicsMemory);
    }

    totalGraphicsMemory = 0;
    availableGraphicsMemory = 0;
    return -1;
}

WebRtc_Word32 VideoRenderWindowsImpl::GetScreenResolution(
                                                          WebRtc_UWord32& screenWidth,
                                                          WebRtc_UWord32& screenHeight) const
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    screenWidth = 0;
    screenHeight = 0;
    return 0;
}

WebRtc_UWord32 VideoRenderWindowsImpl::RenderFrameRate(
                                                       const WebRtc_UWord32 streamId)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    return 0;
}

WebRtc_Word32 VideoRenderWindowsImpl::SetStreamCropping(
                                                        const WebRtc_UWord32 streamId,
                                                        const float left,
                                                        const float top,
                                                        const float right,
                                                        const float bottom)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->SetCropping(streamId, 0, left, top, right,
                                             bottom);
    }
    return error;
}

WebRtc_Word32 VideoRenderWindowsImpl::ConfigureRenderer(
                                                        const WebRtc_UWord32 streamId,
                                                        const unsigned int zOrder,
                                                        const float left,
                                                        const float top,
                                                        const float right,
                                                        const float bottom)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->ConfigureRenderer(streamId, 0, zOrder, left,
                                                   top, right, bottom);
    }

    return error;
}

WebRtc_Word32 VideoRenderWindowsImpl::SetTransparentBackground(
                                                               const bool enable)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->SetTransparentBackground(enable);
    }
    return error;
}

WebRtc_Word32 VideoRenderWindowsImpl::SetText(
                                              const WebRtc_UWord8 textId,
                                              const WebRtc_UWord8* text,
                                              const WebRtc_Word32 textLength,
                                              const WebRtc_UWord32 textColorRef,
                                              const WebRtc_UWord32 backgroundColorRef,
                                              const float left,
                                              const float top,
                                              const float right,
                                              const float bottom)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->SetText(textId, text, textLength,
                                         textColorRef, backgroundColorRef,
                                         left, top, right, bottom);
    }
    return error;
}

WebRtc_Word32 VideoRenderWindowsImpl::SetBitmap(const void* bitMap,
                                                const WebRtc_UWord8 pictureId,
                                                const void* colorKey,
                                                const float left,
                                                const float top,
                                                const float right,
                                                const float bottom)
{
    CriticalSectionScoped cs(_renderWindowsCritsect);
    WebRtc_Word32 error = -1;
    if (!_ptrRendererWin)
    {
        WEBRTC_TRACE(kTraceModuleCall, kTraceVideoRenderer,
                     _id, "%s, no renderer", __FUNCTION__);
    }
    else
    {
        error = _ptrRendererWin->SetBitmap(bitMap, pictureId, colorKey, left,
                                           top, right, bottom);
    }
    return error;
}

void VideoRenderWindowsImpl::LogOSAndHardwareDetails()
{
    HRESULT hr;
    IDxDiagProvider* m_pDxDiagProvider = NULL;
    IDxDiagContainer* m_pDxDiagRoot = NULL;

    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
    bool coUninitializeIsRequired = true;
    if (FAILED(hr))
    {
        // Avoid calling CoUninitialize() since CoInitializeEx() failed.
        coUninitializeIsRequired = false;
        if (hr == RPC_E_CHANGED_MODE)
        {
            // Calling thread has already initialized COM to be used in a single-threaded
            // apartment (STA). We are then prevented from using STA.
            // Details: hr = 0x80010106 <=> "Cannot change thread mode after it is set".
            //
            WEBRTC_TRACE(
                         kTraceWarning,
                         kTraceVideoRenderer,
                         _id,
                         "VideoRenderWindowsImpl::LogOSAndHardwareDetails() CoInitializeEx(NULL, COINIT_APARTMENTTHREADED) => RPC_E_CHANGED_MODE, error 0x%x",
                         hr);
        }
    }

    hr = CoCreateInstance(CLSID_DxDiagProvider, NULL, CLSCTX_INPROC_SERVER,
                          IID_IDxDiagProvider, (LPVOID*) &m_pDxDiagProvider);

    if (FAILED(hr) || m_pDxDiagProvider == NULL)
    {
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    // Fill out a DXDIAG_INIT_PARAMS struct and pass it to IDxDiagContainer::Initialize
    // Passing in TRUE for bAllowWHQLChecks, allows dxdiag to check if drivers are 
    // digital signed as logo'd by WHQL which may connect via internet to update 
    // WHQL certificates.    
    DXDIAG_INIT_PARAMS dxDiagInitParam;
    ZeroMemory(&dxDiagInitParam, sizeof(DXDIAG_INIT_PARAMS));

    dxDiagInitParam.dwSize = sizeof(DXDIAG_INIT_PARAMS);
    dxDiagInitParam.dwDxDiagHeaderVersion = DXDIAG_DX9_SDK_VERSION;
    dxDiagInitParam.bAllowWHQLChecks = TRUE;
    dxDiagInitParam.pReserved = NULL;

    hr = m_pDxDiagProvider->Initialize(&dxDiagInitParam);
    if (FAILED(hr))
    {
        m_pDxDiagProvider->Release();
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    hr = m_pDxDiagProvider->GetRootContainer(&m_pDxDiagRoot);
    if (FAILED(hr) || m_pDxDiagRoot == NULL)
    {
        m_pDxDiagProvider->Release();
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    IDxDiagContainer* pObject = NULL;

    hr = m_pDxDiagRoot->GetChildContainer(L"DxDiag_SystemInfo", &pObject);
    if (FAILED(hr) || pObject == NULL)
    {
        m_pDxDiagRoot->Release();
        m_pDxDiagProvider->Release();
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    TCHAR m_szDirectXVersionLongEnglish[100];
    TCHAR m_szOSLocalized[100];
    TCHAR m_szProcessorEnglish[200];
    TCHAR m_szSystemManufacturerEnglish[200];

    ZeroMemory(m_szDirectXVersionLongEnglish, sizeof(TCHAR) * 100);
    ZeroMemory(m_szOSLocalized, sizeof(TCHAR) * 100);
    ZeroMemory(m_szProcessorEnglish, sizeof(TCHAR) * 200);
    ZeroMemory(m_szSystemManufacturerEnglish, sizeof(TCHAR) * 200);

    GetStringValue( pObject, L"szDirectXVersionLongEnglish",
                   EXPAND(m_szDirectXVersionLongEnglish) );
    GetStringValue(pObject, L"szOSLocalized", EXPAND(m_szOSLocalized) );
    GetStringValue(pObject, L"szProcessorEnglish", EXPAND(m_szProcessorEnglish) );
    GetStringValue( pObject, L"szSystemManufacturerEnglish",
                   EXPAND(m_szSystemManufacturerEnglish) );

    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "System Manufacturer             --- %s",
                 m_szSystemManufacturerEnglish);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Processor                       --- %s", m_szProcessorEnglish);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Operating System                --- %s", m_szOSLocalized);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "DirectX Version                 --- %s",
                 m_szDirectXVersionLongEnglish);

    if (pObject)
        pObject->Release();

    struct DisplayInfo
    {
        TCHAR m_szDescription[200];
        TCHAR m_szManufacturer[200];
        TCHAR m_szChipType[100];
        TCHAR m_szDisplayMemoryEnglish[100];
        TCHAR m_szDisplayModeEnglish[100];
        TCHAR m_szDriverName[100];
        TCHAR m_szDriverVersion[100];
        TCHAR m_szDDStatusEnglish[100];
        TCHAR m_szD3DStatusEnglish[100];
        BOOL m_bDDAccelerationEnabled;
        BOOL m_bNoHardware;
        BOOL m_b3DAccelerationExists;
        BOOL m_b3DAccelerationEnabled;
    };

    WCHAR wszContainer[256];
    IDxDiagContainer* pContainer = NULL;

    DWORD nInstanceCount = 0;
    DWORD nItem = 0;
    DWORD nCurCount = 0;

    // Get the IDxDiagContainer object called "DxDiag_DisplayDevices".
    // This call may take some time while dxdiag gathers the info.
    if (FAILED(hr = m_pDxDiagRoot->GetChildContainer(L"DxDiag_DisplayDevices",
                                                     &pContainer)))
    {
        m_pDxDiagRoot->Release();
        m_pDxDiagProvider->Release();
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    if (FAILED(hr = pContainer->GetNumberOfChildContainers(&nInstanceCount)))
    {
        pContainer->Release();
        m_pDxDiagRoot->Release();
        m_pDxDiagProvider->Release();
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    DisplayInfo *pDisplayInfo = new DisplayInfo;
    if (pDisplayInfo == NULL)
        return;
    ZeroMemory(pDisplayInfo, sizeof(DisplayInfo));

    hr = pContainer->EnumChildContainerNames(nItem, wszContainer, 256);
    if (FAILED(hr))
    {
        delete pDisplayInfo;
        pContainer->Release();
        m_pDxDiagRoot->Release();
        m_pDxDiagProvider->Release();
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    hr = pContainer->GetChildContainer(wszContainer, &pObject);
    if (FAILED(hr) || pObject == NULL)
    {
        delete pDisplayInfo;
        pContainer->Release();
        m_pDxDiagRoot->Release();
        m_pDxDiagProvider->Release();
        if (coUninitializeIsRequired)
            CoUninitialize();
        return;
    }

    GetStringValue( pObject, L"szDescription",
                   EXPAND(pDisplayInfo->m_szDescription) );
    GetStringValue( pObject, L"szManufacturer",
                   EXPAND(pDisplayInfo->m_szManufacturer) );
    GetStringValue(pObject, L"szChipType", EXPAND(pDisplayInfo->m_szChipType) );
    GetStringValue( pObject, L"szDisplayMemoryEnglish",
                   EXPAND(pDisplayInfo->m_szDisplayMemoryEnglish) );
    GetStringValue( pObject, L"szDisplayModeEnglish",
                   EXPAND(pDisplayInfo->m_szDisplayModeEnglish) );
    GetStringValue( pObject, L"szDriverName",
                   EXPAND(pDisplayInfo->m_szDriverName) );
    GetStringValue( pObject, L"szDriverVersion",
                   EXPAND(pDisplayInfo->m_szDriverVersion) );
    GetBoolValue(pObject, L"bDDAccelerationEnabled",
                 &pDisplayInfo->m_bDDAccelerationEnabled);
    GetBoolValue(pObject, L"bNoHardware", &pDisplayInfo->m_bNoHardware);
    GetBoolValue(pObject, L"bDDAccelerationEnabled",
                 &pDisplayInfo->m_bDDAccelerationEnabled);
    GetBoolValue(pObject, L"b3DAccelerationExists",
                 &pDisplayInfo->m_b3DAccelerationExists);
    GetBoolValue(pObject, L"b3DAccelerationEnabled",
                 &pDisplayInfo->m_b3DAccelerationEnabled);
    GetStringValue( pObject, L"szDDStatusEnglish",
                   EXPAND(pDisplayInfo->m_szDDStatusEnglish));
    GetStringValue( pObject, L"szD3DStatusEnglish",
                   EXPAND(pDisplayInfo->m_szD3DStatusEnglish));

    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Device Name                     --- %s",
                 pDisplayInfo->m_szDescription);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Device Manufacturer             --- %s",
                 pDisplayInfo->m_szManufacturer);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Device ChipType                 --- %s",
                 pDisplayInfo->m_szChipType);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Approx. Total Device Memory     --- %s",
                 pDisplayInfo->m_szDisplayMemoryEnglish);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Current Display Mode            --- %s",
                 pDisplayInfo->m_szDisplayModeEnglish);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Device Driver Name              --- %s",
                 pDisplayInfo->m_szDriverName);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "Device Driver Version           --- %s",
                 pDisplayInfo->m_szDriverVersion);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "DirectDraw Acceleration Enabled --- %s",
                 pDisplayInfo->m_szDescription ? "Enabled" : "Disabled");
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "bNoHardware                     --- %s",
                 pDisplayInfo->m_bNoHardware ? "Enabled" : "Disabled");
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "b3DAccelerationExists Enabled   --- %s",
                 pDisplayInfo->m_b3DAccelerationExists ? "Enabled" : "Disabled");
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "b3DAccelerationEnabled Enabled  --- %s",
                 pDisplayInfo->m_b3DAccelerationEnabled ? "Enabled"
                         : "Disabled");
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "DDraw Status                    --- %s",
                 pDisplayInfo->m_szDDStatusEnglish);
    WEBRTC_TRACE(kTraceStateInfo, kTraceVideo, -1,
                 "D3D Status                      --- %s",
                 pDisplayInfo->m_szD3DStatusEnglish);

    // Get OS version
    OSVERSIONINFOEX osvie;
    osvie.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
    GetVersionEx((LPOSVERSIONINFO) & osvie);
    /*
     Operating system	    Version number	dwMajorVersion	dwMinorVersion
     Windows 7	            6.1	            6	            1
     Windows Server 2008 R2	6.1	            6	            1
     Windows Server 2008	    6.0	            6           	0
     Windows Vista	        6.0	            6	            0
     Windows Server 2003 R2	5.2	            5	            2
     Windows Server 2003	    5.2	            5           	2
     Windows XP	            5.1	            5           	1
     Windows 2000	        5.0         	5	            0
     */
    //RDP problem exists only when XP is involved
    if (osvie.dwMajorVersion < 6)
    {
        WEBRTC_TRACE(kTraceStateInfo, kTraceVideoRenderer, _id,
                     "Checking for RDP driver");
        if (_tcsncmp(pDisplayInfo->m_szDriverName, _T("RDPDD.dll"), 9) == 0)
        {
            //
        }
    }

    if (pObject)
    {
        pObject->Release();
        pObject = NULL;
    }

    if (pContainer)
        pContainer->Release();

    if (m_pDxDiagProvider)
        m_pDxDiagProvider->Release();

    if (m_pDxDiagRoot)
        m_pDxDiagRoot->Release();

    if (pDisplayInfo)
        delete pDisplayInfo;

    if (coUninitializeIsRequired)
        CoUninitialize();

    return;
}

//-----------------------------------------------------------------------------
// Name: GetStringValue()
// Desc: Get a string value from a IDxDiagContainer object
//-----------------------------------------------------------------------------
HRESULT VideoRenderWindowsImpl::GetStringValue(IDxDiagContainer* pObject,
                                               WCHAR* wstrName,
                                               TCHAR* strValue, int nStrLen)
{
    HRESULT hr;
    VARIANT var;
    VariantInit(&var);

    if (FAILED(hr = pObject->GetProp(wstrName, &var)))
        return hr;

    if (var.vt != VT_BSTR)
        return E_INVALIDARG;

#ifdef _UNICODE
    wcsncpy( strValue, var.bstrVal, nStrLen-1 );
#else
    wcstombs(strValue, var.bstrVal, nStrLen);
#endif
    strValue[nStrLen - 1] = TEXT('\0');
    VariantClear(&var);

    return S_OK;
}

//-----------------------------------------------------------------------------
// Name: GetBoolValue()
// Desc: Get a BOOL value from a IDxDiagContainer object
//-----------------------------------------------------------------------------
HRESULT VideoRenderWindowsImpl::GetBoolValue(IDxDiagContainer* pObject,
                                             WCHAR* wstrName, BOOL* pbValue)
{
    HRESULT hr;
    VARIANT var;
    VariantInit(&var);

    if (FAILED(hr = pObject->GetProp(wstrName, &var)))
        return hr;

    if (var.vt != VT_BOOL)
        return E_INVALIDARG;

    *pbValue = (var.boolVal != 0);
    VariantClear(&var);

    return S_OK;
}

int VideoRenderWindowsImpl::CheckHWAcceleration()
{
    // Read the registry to check if HW acceleration is enabled or not.
    HKEY regKey;
    DWORD value = 0;
    DWORD valueLength = 4;

    bool directDraw = true;
    bool direct3D = true;
    bool dci = true;

    // DirectDraw
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\DirectDraw"),
                     0, KEY_QUERY_VALUE, &regKey) == ERROR_SUCCESS)
    {
        // We have the registry key
        value = 0;
        if (RegQueryValueEx(regKey, _T("EmulationOnly"), NULL, NULL,
                            (BYTE*) &value, &valueLength) == ERROR_SUCCESS)
        {
            if (value == 1)
            {
                WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
                             "DirectDraw acceleration is disabled");
                directDraw = false;
            }
            else
            {
                WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
                             "DirectDraw acceleration is enabled");
            }
        }
        else
        {
            // Could not get the value for this one.
            WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
                         "Could not find EmulationOnly key, DirectDraw acceleration is probably enabled");
        }
        RegCloseKey(regKey);
    }
    else
    {
        WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
                     "Could not open DirectDraw settings");
    }

    // Direct3D
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                     _T("SOFTWARE\\Microsoft\\Direct3D\\Drivers"), 0,
                     KEY_QUERY_VALUE, &regKey) == ERROR_SUCCESS)
    {
        // We have the registry key
        value = 0;
        if (RegQueryValueEx(regKey, _T("SoftwareOnly"), NULL, NULL,
                            (BYTE*) &value, &valueLength) == ERROR_SUCCESS)
        {
            if (value == 1)
            {
                WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
                             "Direct3D acceleration is disabled");
                direct3D = false;
            }
            else
            {
                WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
                             "Direct3D acceleration is enabled");
            }
        }
        else
        {
            // Could not get the value for this one.
            WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
                         "Could not find SoftwarOnly key, Direct3D acceleration is probably enabled");
        }
        RegCloseKey(regKey);
    }
    else
    {
        WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
                     "Could not open Direct3D settings");
    }

    // DCI
    if (RegOpenKeyEx(
                     HKEY_LOCAL_MACHINE,
                     _T(
                        "SYSTEM\\CurrentControlSet\\Control\\GraphicsDrivers\\DCI"),
                     0, KEY_QUERY_VALUE, &regKey) == ERROR_SUCCESS)
    {
        // We have found the registry key
        value = 0;
        if (RegQueryValueEx(regKey, _T("Timeout"), NULL, NULL, (BYTE*) &value,
                            &valueLength) == ERROR_SUCCESS)
        {
            if (value == 0)
            {
                WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
                             "DCI - DirectDraw acceleration is disabled");
                dci = false;
            }
            else if (value == 7)
            {
                WEBRTC_TRACE(kTraceWarning, kTraceVideo, -1,
                             "DCI is fully enabled");
            }
            else
            {
                WEBRTC_TRACE(
                             kTraceWarning,
                             kTraceVideo,
                             -1,
                             "DCI - DirectDraw acceleration is enabled, but short timeout: %d",
                             value);
            }
        }
        else
        {
            // Could not get the value for this one.
            WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
                         "Could not find Timeout key");
        }
        RegCloseKey(regKey);
    }
    else
    {
        WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
                     "Could not open DCI settings");
    }

    // We don't care about Direct3D right now...
    if (dci == false || directDraw == false)
    {
        return -1;
    }

    return 0;
}

void VideoRenderWindowsImpl::CheckHWDriver(bool& badDriver,
                                           bool& fullAccelerationEnabled)
{
    // Read the registry to check if HW acceleration is enabled or not.
    HKEY regKey;
    DWORD value = 0;
    DWORD valueLength = 4;

    //Assume the best
    badDriver = false;
    fullAccelerationEnabled = true;

    // Check the path to the currently used driver
    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DEVICEMAP\\VIDEO"), 0,
                     KEY_QUERY_VALUE, &regKey) == ERROR_SUCCESS)
    {
        // We have found the registry key containing the driver location
        value = 0;
        DWORD driverPathLen = 512;
        TCHAR driverPath[512];
        memset(driverPath, 0, driverPathLen * sizeof(TCHAR));
        DWORD dwType = REG_SZ;

        long retVal = RegQueryValueEx(regKey, _T("\\Device\\Video0"), NULL,
                                      NULL, (BYTE*) driverPath, &driverPathLen);

        // Close the key...
        RegCloseKey(regKey);

        if (retVal == ERROR_SUCCESS)
        {
            // We have the path to the currently used video card

            // trueDriverPath = modified nameStr, from above, that works
            // for RegOpenKeyEx
            TCHAR trueDriverPath[512];
            memset(trueDriverPath, 0, 512 * sizeof(TCHAR));

            // Convert the path to correct format.
            //      - Remove \Registry\Machine\
            //      - Replace '\' with '\\'
            // Should be something like this: System\\CurrentControlSet\\Control\\Video\\{F6987E15-F12C-4B15-8C84-0F635F3F09EA}\\0000"
            int idx = 0;
            for (DWORD i = 18; i < (driverPathLen / sizeof(TCHAR)); i++)
            {
                trueDriverPath[idx++] = driverPath[i];
                if (driverPath[i] == _T('\\'))
                {
                    trueDriverPath[idx++] = driverPath[i];
                }
            }

            // Open the driver key
            if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, trueDriverPath, 0,
                             KEY_QUERY_VALUE, &regKey) == ERROR_SUCCESS)
            {
                TCHAR driverName[64];
                memset(driverName, 0, 64 * sizeof(TCHAR));
                DWORD driverNameLength = 64;
                retVal = RegQueryValueEx(regKey, _T("drv"), NULL, NULL,
                                         (BYTE*) driverName, &driverNameLength);
                if (retVal == ERROR_SUCCESS)
                {
                    WEBRTC_TRACE(kTraceInfo, kTraceVideo, -1,
                                 "Graphics card driver name: %s", driverName);
                }
                DWORD accLevel = 0;
                DWORD accLevelS = sizeof(accLevel);

                RegQueryValueEx(regKey, _T("Acceleration.Level"), NULL, NULL,
                                (LPBYTE) & accLevel, &accLevelS);
                //Don't care if the key is not found. It probably means that acceleration is enabled
                if (accLevel != 0)
                {
                    // Close the key...
                    RegCloseKey(regKey);

                    if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, trueDriverPath, 0,
                                     KEY_SET_VALUE, &regKey) == ERROR_SUCCESS)
                    {
                        // try setting it to full
                        accLevel = 0;
                        LONG retVal;
                        retVal = RegSetValueEx(regKey,
                                               _T("Acceleration.Level"), NULL,
                                               REG_DWORD, (PBYTE) & accLevel,
                                               sizeof(DWORD));
                        if (retVal != ERROR_SUCCESS)
                        {
                            fullAccelerationEnabled = false;
                        }
                        else
                        {
                            RegQueryValueEx(regKey, _T("Acceleration.Level"),
                                            NULL, NULL, (LPBYTE) & accLevel,
                                            &accLevelS);
                            if (accLevel != 0)
                            {
                                fullAccelerationEnabled = false;
                            }
                            else
                            {
                                fullAccelerationEnabled = true;
                            }
                        }
                    }
                    else
                    {
                        fullAccelerationEnabled = false;
                    }
                }
                else
                {
                    fullAccelerationEnabled = true;
                }

                // Close the key...
                RegCloseKey(regKey);
            }
        }
    }
}

} //namespace webrtc

