/*-------------------------------------------------------------------------
 * drawElements Quality Program Tester Core
 * ----------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief WGL Utilities.
 *//*--------------------------------------------------------------------*/

#include "tcuWGL.hpp"
#include "tcuWin32Window.hpp"
#include "deDynamicLibrary.hpp"
#include "deMemory.h"
#include "deStringUtil.hpp"
#include "tcuFormatUtil.hpp"
#include "gluRenderConfig.hpp"
#include "glwEnums.hpp"

#include <map>
#include <sstream>
#include <iterator>
#include <set>

#include <wingdi.h>

// WGL_ARB_pixel_format
#define WGL_NUMBER_PIXEL_FORMATS_ARB 0x2000
#define WGL_DRAW_TO_WINDOW_ARB 0x2001
#define WGL_DRAW_TO_BITMAP_ARB 0x2002
#define WGL_ACCELERATION_ARB 0x2003
#define WGL_NEED_PALETTE_ARB 0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB 0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB 0x2006
#define WGL_SWAP_METHOD_ARB 0x2007
#define WGL_NUMBER_OVERLAYS_ARB 0x2008
#define WGL_NUMBER_UNDERLAYS_ARB 0x2009
#define WGL_TRANSPARENT_ARB 0x200A
#define WGL_TRANSPARENT_RED_VALUE_ARB 0x2037
#define WGL_TRANSPARENT_GREEN_VALUE_ARB 0x2038
#define WGL_TRANSPARENT_BLUE_VALUE_ARB 0x2039
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB 0x203A
#define WGL_TRANSPARENT_INDEX_VALUE_ARB 0x203B
#define WGL_SHARE_DEPTH_ARB 0x200C
#define WGL_SHARE_STENCIL_ARB 0x200D
#define WGL_SHARE_ACCUM_ARB 0x200E
#define WGL_SUPPORT_GDI_ARB 0x200F
#define WGL_SUPPORT_OPENGL_ARB 0x2010
#define WGL_DOUBLE_BUFFER_ARB 0x2011
#define WGL_STEREO_ARB 0x2012
#define WGL_PIXEL_TYPE_ARB 0x2013
#define WGL_COLOR_BITS_ARB 0x2014
#define WGL_RED_BITS_ARB 0x2015
#define WGL_RED_SHIFT_ARB 0x2016
#define WGL_GREEN_BITS_ARB 0x2017
#define WGL_GREEN_SHIFT_ARB 0x2018
#define WGL_BLUE_BITS_ARB 0x2019
#define WGL_BLUE_SHIFT_ARB 0x201A
#define WGL_ALPHA_BITS_ARB 0x201B
#define WGL_ALPHA_SHIFT_ARB 0x201C
#define WGL_ACCUM_BITS_ARB 0x201D
#define WGL_ACCUM_RED_BITS_ARB 0x201E
#define WGL_ACCUM_GREEN_BITS_ARB 0x201F
#define WGL_ACCUM_BLUE_BITS_ARB 0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB 0x2021
#define WGL_DEPTH_BITS_ARB 0x2022
#define WGL_STENCIL_BITS_ARB 0x2023
#define WGL_AUX_BUFFERS_ARB 0x2024

#define WGL_NO_ACCELERATION_ARB 0x2025
#define WGL_GENERIC_ACCELERATION_ARB 0x2026
#define WGL_FULL_ACCELERATION_ARB 0x2027

#define WGL_TYPE_RGBA_ARB 0x202B
#define WGL_TYPE_COLORINDEX_ARB 0x202C

// WGL_ARB_color_buffer_float
#define WGL_TYPE_RGBA_FLOAT_ARB 0x21A0

// WGL_EXT_pixel_type_packed_float
#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT 0x20A8

// WGL_ARB_multisample
#define WGL_SAMPLE_BUFFERS_ARB 0x2041
#define WGL_SAMPLES_ARB 0x2042

// WGL_EXT_colorspace
#define WGL_COLORSPACE_EXT 0x309D
#define WGL_COLORSPACE_SRGB_EXT 0x3089
#define WGL_COLORSPACE_LINEAR_EXT 0x308A

// WGL_ARB_create_context
#define WGL_CONTEXT_MAJOR_VERSION_ARB 0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB 0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB 0x2093
#define WGL_CONTEXT_FLAGS_ARB 0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB 0x9126
#define WGL_CONTEXT_DEBUG_BIT_ARB 0x0001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x0002
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB 0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB 0x00000002
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT 0x00000004

// WGL_ARB_create_context_robustness
#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB 0x0004
#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB 0x8256
#define WGL_NO_RESET_NOTIFICATION_ARB 0x8261
#define WGL_LOSE_CONTEXT_ON_RESET_ARB 0x8252

// WGL ARB_create_context_no_error
#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB 0x31B3

DE_BEGIN_EXTERN_C

// WGL core
typedef HGLRC(WINAPI *wglCreateContextFunc)(HDC hdc);
typedef BOOL(WINAPI *wglDeleteContextFunc)(HGLRC hglrc);
typedef BOOL(WINAPI *wglMakeCurrentFunc)(HDC hdc, HGLRC hglrc);
typedef PROC(WINAPI *wglGetProcAddressFunc)(LPCSTR lpszProc);
typedef BOOL(WINAPI *wglSwapLayerBuffersFunc)(HDC dhc, UINT fuPlanes);

// WGL_ARB_pixel_format
typedef BOOL(WINAPI *wglGetPixelFormatAttribivARBFunc)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes,
                                                       const int *piAttributes, int *piValues);
typedef BOOL(WINAPI *wglGetPixelFormatAttribfvARBFunc)(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes,
                                                       const int *piAttributes, FLOAT *pfValues);
typedef BOOL(WINAPI *wglChoosePixelFormatARBFunc)(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList,
                                                  UINT nMaxFormats, int *piFormats, UINT *nNumFormats);

// WGL_ARB_create_context
typedef HGLRC(WINAPI *wglCreateContextAttribsARBFunc)(HDC hdc, HGLRC hshareContext, const int *attribList);
typedef const char *(WINAPI *wglGetExtensionsStringARBFunc)(HDC hdc);

// WGL_EXT_swap_control
typedef BOOL(WINAPI *wglSwapIntervalEXTFunc)(int interval);

DE_END_EXTERN_C

namespace tcu
{
namespace wgl
{

// Functions

struct Functions
{
    // Core
    wglCreateContextFunc createContext;
    wglDeleteContextFunc deleteContext;
    wglMakeCurrentFunc makeCurrent;
    wglGetProcAddressFunc getProcAddress;
    wglSwapLayerBuffersFunc swapLayerBuffers;

    // WGL_ARB_pixel_format
    wglGetPixelFormatAttribivARBFunc getPixelFormatAttribivARB;
    wglGetPixelFormatAttribfvARBFunc getPixelFormatAttribfvARB;
    wglChoosePixelFormatARBFunc choosePixelFormatARB;

    // WGL_ARB_create_context
    wglCreateContextAttribsARBFunc createContextAttribsARB;
    wglGetExtensionsStringARBFunc getExtensionsStringARB;

    // WGL_EXT_swap_control
    wglSwapIntervalEXTFunc swapIntervalEXT;

    Functions(void)
        : createContext(DE_NULL)
        , deleteContext(DE_NULL)
        , makeCurrent(DE_NULL)
        , getProcAddress(DE_NULL)
        , swapLayerBuffers(DE_NULL)
        , getPixelFormatAttribivARB(DE_NULL)
        , getPixelFormatAttribfvARB(DE_NULL)
        , choosePixelFormatARB(DE_NULL)
        , createContextAttribsARB(DE_NULL)
        , getExtensionsStringARB(DE_NULL)
    {
    }
};

// Library

class Library
{
public:
    Library(HINSTANCE instance);
    ~Library(void);

    const Functions &getFunctions(void) const
    {
        return m_functions;
    }
    const de::DynamicLibrary &getGLLibrary(void) const
    {
        return m_library;
    }
    bool isWglExtensionSupported(const char *extName) const;

private:
    de::DynamicLibrary m_library;
    Functions m_functions;
    std::set<std::string> m_extensions;
};

Library::Library(HINSTANCE instance) : m_library("opengl32.dll")
{
    // Temporary 1x1 window for creating context
    win32::Window tmpWindow(instance, 1, 1);

    // Load WGL core.
    m_functions.createContext    = (wglCreateContextFunc)m_library.getFunction("wglCreateContext");
    m_functions.deleteContext    = (wglDeleteContextFunc)m_library.getFunction("wglDeleteContext");
    m_functions.getProcAddress   = (wglGetProcAddressFunc)m_library.getFunction("wglGetProcAddress");
    m_functions.makeCurrent      = (wglMakeCurrentFunc)m_library.getFunction("wglMakeCurrent");
    m_functions.swapLayerBuffers = (wglSwapLayerBuffersFunc)m_library.getFunction("wglSwapLayerBuffers");

    if (!m_functions.createContext || !m_functions.deleteContext || !m_functions.getProcAddress ||
        !m_functions.makeCurrent || !m_functions.swapLayerBuffers)
        throw ResourceError("Failed to load core WGL functions");

    {
        PIXELFORMATDESCRIPTOR pixelFormatDesc;
        deMemset(&pixelFormatDesc, 0, sizeof(pixelFormatDesc));

        pixelFormatDesc.nSize      = sizeof(pixelFormatDesc);
        pixelFormatDesc.nVersion   = 1;
        pixelFormatDesc.dwFlags    = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
        pixelFormatDesc.iPixelType = PFD_TYPE_RGBA;
        pixelFormatDesc.iLayerType = PFD_MAIN_PLANE;

        int pixelFormat = ChoosePixelFormat(tmpWindow.getDeviceContext(), &pixelFormatDesc);
        if (!SetPixelFormat(tmpWindow.getDeviceContext(), pixelFormat, &pixelFormatDesc))
            throw ResourceError("Failed to set pixel format for temporary context creation");
    }

    // Create temporary context for loading extension functions.
    HGLRC tmpCtx = m_functions.createContext(tmpWindow.getDeviceContext());
    if (!tmpCtx || !m_functions.makeCurrent(tmpWindow.getDeviceContext(), tmpCtx))
    {
        if (tmpCtx)
            m_functions.deleteContext(tmpCtx);
        throw ResourceError("Failed to create temporary WGL context");
    }

    // WGL_ARB_pixel_format
    m_functions.getPixelFormatAttribivARB =
        (wglGetPixelFormatAttribivARBFunc)m_functions.getProcAddress("wglGetPixelFormatAttribivARB");
    m_functions.getPixelFormatAttribfvARB =
        (wglGetPixelFormatAttribfvARBFunc)m_functions.getProcAddress("wglGetPixelFormatAttribfvARB");
    m_functions.choosePixelFormatARB =
        (wglChoosePixelFormatARBFunc)m_functions.getProcAddress("wglChoosePixelFormatARB");

    // WGL_ARB_create_context
    m_functions.createContextAttribsARB =
        (wglCreateContextAttribsARBFunc)m_functions.getProcAddress("wglCreateContextAttribsARB");
    m_functions.getExtensionsStringARB =
        (wglGetExtensionsStringARBFunc)m_functions.getProcAddress("wglGetExtensionsStringARB");

    // WGL_EXT_swap_control
    m_functions.swapIntervalEXT = (wglSwapIntervalEXTFunc)m_functions.getProcAddress("wglSwapIntervalEXT");

    m_functions.makeCurrent(tmpWindow.getDeviceContext(), NULL);
    m_functions.deleteContext(tmpCtx);

    if (!m_functions.getPixelFormatAttribivARB || !m_functions.getPixelFormatAttribfvARB ||
        !m_functions.choosePixelFormatARB || !m_functions.createContextAttribsARB ||
        !m_functions.getExtensionsStringARB)
        throw ResourceError("Failed to load WGL extension functions");

    const char *extensions = m_functions.getExtensionsStringARB(tmpWindow.getDeviceContext());
    std::istringstream extStream(extensions);
    m_extensions =
        std::set<std::string>(std::istream_iterator<std::string>(extStream), std::istream_iterator<std::string>());
}

Library::~Library(void)
{
}

bool Library::isWglExtensionSupported(const char *extName) const
{
    return m_extensions.find(extName) != m_extensions.end();
}

// Core

Core::Core(HINSTANCE instance) : m_library(new Library(instance))
{
}

Core::~Core(void)
{
    delete m_library;
}

std::vector<int> Core::getPixelFormats(HDC deviceCtx) const
{
    const Functions &wgl = m_library->getFunctions();

    int attribs[] = {WGL_NUMBER_PIXEL_FORMATS_ARB};
    int values[DE_LENGTH_OF_ARRAY(attribs)];

    if (!wgl.getPixelFormatAttribivARB(deviceCtx, 0, 0, DE_LENGTH_OF_ARRAY(attribs), &attribs[0], &values[0]))
        TCU_THROW(ResourceError, "Failed to query number of WGL pixel formats");

    std::vector<int> pixelFormats(values[0]);
    for (int i = 0; i < values[0]; i++)
        pixelFormats[i] = i + 1;

    return pixelFormats;
}

static PixelFormatInfo::Acceleration translateAcceleration(int accel)
{
    switch (accel)
    {
    case WGL_NO_ACCELERATION_ARB:
        return PixelFormatInfo::ACCELERATION_NONE;
    case WGL_GENERIC_ACCELERATION_ARB:
        return PixelFormatInfo::ACCELERATION_GENERIC;
    case WGL_FULL_ACCELERATION_ARB:
        return PixelFormatInfo::ACCELERATION_FULL;
    default:
        return PixelFormatInfo::ACCELERATION_UNKNOWN;
    }
}

static PixelFormatInfo::PixelType translatePixelType(int type)
{
    switch (type)
    {
    case WGL_TYPE_RGBA_ARB:
        return PixelFormatInfo::PIXELTYPE_RGBA;
    case WGL_TYPE_RGBA_FLOAT_ARB:
        return PixelFormatInfo::PIXELTYPE_RGBA_FLOAT;
    case WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT:
        return PixelFormatInfo::PIXELTYPE_RGBA_UNSIGNED_FLOAT;
    case WGL_TYPE_COLORINDEX_ARB:
        return PixelFormatInfo::PIXELTYPE_COLOR_INDEX;
    default:
        return PixelFormatInfo::PIXELTYPE_UNKNOWN;
    }
}

static void getPixelFormatAttribs(const Functions &wgl, HDC deviceCtx, int pixelFormat, int numAttribs,
                                  const int *attribs, std::map<int, int> *dst)
{
    std::vector<int> values(numAttribs);

    if (!wgl.getPixelFormatAttribivARB(deviceCtx, pixelFormat, 0, numAttribs, &attribs[0], &values[0]))
        TCU_THROW(ResourceError, "Pixel format query failed");

    for (int ndx = 0; ndx < numAttribs; ++ndx)
        (*dst)[attribs[ndx]] = values[ndx];
}

PixelFormatInfo Core::getPixelFormatInfo(HDC deviceCtx, int pixelFormat) const
{
    std::vector<int> s_attribsToQuery{
        WGL_DRAW_TO_WINDOW_ARB,   WGL_DRAW_TO_BITMAP_ARB,      WGL_ACCELERATION_ARB,
        WGL_NEED_PALETTE_ARB,     WGL_NEED_SYSTEM_PALETTE_ARB, WGL_NUMBER_OVERLAYS_ARB,
        WGL_NUMBER_UNDERLAYS_ARB, WGL_SUPPORT_OPENGL_ARB,      WGL_DOUBLE_BUFFER_ARB,
        WGL_STEREO_ARB,           WGL_PIXEL_TYPE_ARB,          WGL_RED_BITS_ARB,
        WGL_GREEN_BITS_ARB,       WGL_BLUE_BITS_ARB,           WGL_ALPHA_BITS_ARB,
        WGL_ACCUM_BITS_ARB,       WGL_DEPTH_BITS_ARB,          WGL_STENCIL_BITS_ARB,
        WGL_AUX_BUFFERS_ARB,      WGL_SAMPLE_BUFFERS_ARB,      WGL_SAMPLES_ARB,
    };
    if (getLibrary()->isWglExtensionSupported("WGL_EXT_colorspace"))
        s_attribsToQuery.push_back(WGL_COLORSPACE_EXT);

    const Functions &wgl = m_library->getFunctions();
    std::map<int, int> values;

    getPixelFormatAttribs(wgl, deviceCtx, pixelFormat, static_cast<int>(s_attribsToQuery.size()),
                          s_attribsToQuery.data(), &values);

    // Translate values.
    PixelFormatInfo info;

    info.pixelFormat = pixelFormat;
    info.surfaceTypes |= (values[WGL_DRAW_TO_WINDOW_ARB] ? PixelFormatInfo::SURFACE_WINDOW : 0);
    info.surfaceTypes |= (values[WGL_DRAW_TO_BITMAP_ARB] ? PixelFormatInfo::SURFACE_PIXMAP : 0);
    info.acceleration      = translateAcceleration(values[WGL_ACCELERATION_ARB]);
    info.needPalette       = values[WGL_NEED_PALETTE_ARB] != 0;
    info.needSystemPalette = values[WGL_NEED_SYSTEM_PALETTE_ARB] != 0;
    info.numOverlays       = values[WGL_NUMBER_OVERLAYS_ARB] != 0;
    info.numUnderlays      = values[WGL_NUMBER_UNDERLAYS_ARB] != 0;
    info.supportOpenGL     = values[WGL_SUPPORT_OPENGL_ARB] != 0;
    info.doubleBuffer      = values[WGL_DOUBLE_BUFFER_ARB] != 0;
    info.stereo            = values[WGL_STEREO_ARB] != 0;
    info.pixelType         = translatePixelType(values[WGL_PIXEL_TYPE_ARB]);
    info.redBits           = values[WGL_RED_BITS_ARB];
    info.greenBits         = values[WGL_GREEN_BITS_ARB];
    info.blueBits          = values[WGL_BLUE_BITS_ARB];
    info.alphaBits         = values[WGL_ALPHA_BITS_ARB];
    info.accumBits         = values[WGL_ACCUM_BITS_ARB];
    info.depthBits         = values[WGL_DEPTH_BITS_ARB];
    info.stencilBits       = values[WGL_STENCIL_BITS_ARB];
    info.numAuxBuffers     = values[WGL_AUX_BUFFERS_ARB];
    info.sampleBuffers     = values[WGL_SAMPLE_BUFFERS_ARB];
    info.samples           = values[WGL_SAMPLES_ARB];
    info.sRGB              = (getLibrary()->isWglExtensionSupported("WGL_EXT_colorspace")) ?
                                 (values[WGL_COLORSPACE_EXT] == WGL_COLORSPACE_SRGB_EXT) :
                                 false;

    return info;
}

// Context

Context::Context(const Core *core, HDC deviceCtx, const Context *sharedContext, glu::ContextType ctxType,
                 int pixelFormat, glu::ResetNotificationStrategy resetNotificationStrategy)
    : m_core(core)
    , m_deviceCtx(deviceCtx)
    , m_context(0)
{
    const Functions &wgl = core->getLibrary()->getFunctions();
    std::vector<int> attribList;

    // Context version and profile
    {
        int profileBit  = 0;
        HGLRC sharedCtx = DE_NULL;
        int minor       = ctxType.getMinorVersion();
        int major       = ctxType.getMajorVersion();

        switch (ctxType.getProfile())
        {
        case glu::PROFILE_CORE:
            profileBit = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
            if (major == 3 && minor < 3)
                minor = 3;
            break;

        case glu::PROFILE_ES:
            profileBit = WGL_CONTEXT_ES_PROFILE_BIT_EXT;
            break;

        case glu::PROFILE_COMPATIBILITY:
            profileBit = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
            break;

        default:
            TCU_THROW(NotSupportedError, "Unsupported context type for WGL");
        }

        attribList.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
        attribList.push_back(major);
        attribList.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
        attribList.push_back(minor);
        attribList.push_back(WGL_CONTEXT_PROFILE_MASK_ARB);
        attribList.push_back(profileBit);
    }

    // Context flags
    {
        int flags = 0;

        if ((ctxType.getFlags() & glu::CONTEXT_FORWARD_COMPATIBLE) != 0)
        {
            if (glu::isContextTypeES(ctxType))
                TCU_THROW(InternalError, "Only OpenGL core contexts can be forward-compatible");

            flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
        }

        if ((ctxType.getFlags() & glu::CONTEXT_DEBUG) != 0)
            flags |= WGL_CONTEXT_DEBUG_BIT_ARB;

        if ((ctxType.getFlags() & glu::CONTEXT_ROBUST) != 0)
            flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;

        if ((ctxType.getFlags() & glu::CONTEXT_NO_ERROR) != 0)
        {
            if (core->getLibrary()->isWglExtensionSupported("WGL_ARB_create_context_no_error"))
            {
                attribList.push_back(WGL_CONTEXT_OPENGL_NO_ERROR_ARB);
                attribList.push_back(1);
            }
            else
                TCU_THROW(NotSupportedError,
                          "WGL_ARB_create_context_no_error is required for creating no-error contexts");
        }

        if (flags != 0)
        {
            attribList.push_back(WGL_CONTEXT_FLAGS_ARB);
            attribList.push_back(flags);
        }
    }

    if (resetNotificationStrategy != glu::RESET_NOTIFICATION_STRATEGY_NOT_SPECIFIED)
    {
        attribList.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);

        if (resetNotificationStrategy == glu::RESET_NOTIFICATION_STRATEGY_NO_RESET_NOTIFICATION)
            attribList.push_back(WGL_NO_RESET_NOTIFICATION_ARB);
        else if (resetNotificationStrategy == glu::RESET_NOTIFICATION_STRATEGY_LOSE_CONTEXT_ON_RESET)
            attribList.push_back(WGL_LOSE_CONTEXT_ON_RESET_ARB);
        else
            TCU_THROW(InternalError, "Unknown reset notification strategy");
    }

    // Set pixel format
    {
        PIXELFORMATDESCRIPTOR pixelFormatDesc;
        deMemset(&pixelFormatDesc, 0, sizeof(pixelFormatDesc));

        if (!DescribePixelFormat(deviceCtx, pixelFormat, sizeof(pixelFormatDesc), &pixelFormatDesc))
            throw ResourceError("DescribePixelFormat() failed");

        if (!SetPixelFormat(deviceCtx, pixelFormat, &pixelFormatDesc))
            throw ResourceError("Failed to set pixel format");
    }

    HGLRC sharedCtx = DE_NULL;
    if (DE_NULL != sharedContext)
        sharedCtx = sharedContext->m_context;

    // Terminate attribList
    attribList.push_back(0);

    // Create context
    m_context = wgl.createContextAttribsARB(deviceCtx, sharedCtx, &attribList[0]);

    if (!m_context)
        TCU_THROW(ResourceError, "Failed to create WGL context");

    if (!wgl.makeCurrent(deviceCtx, m_context))
    {
        wgl.deleteContext(m_context);
        TCU_THROW(ResourceError, "wglMakeCurrent() failed");
    }

    if (core->getLibrary()->isWglExtensionSupported("WGL_EXT_swap_control"))
        core->getLibrary()->getFunctions().swapIntervalEXT(0);
}

Context::~Context(void)
{
    const Functions &wgl = m_core->getLibrary()->getFunctions();

    wgl.makeCurrent(m_deviceCtx, NULL);
    wgl.deleteContext(m_context);
}

FunctionPtr Context::getGLFunction(const char *name) const
{
    FunctionPtr ptr = DE_NULL;

    // Try first with wglGeProcAddress()
    ptr = (FunctionPtr)m_core->getLibrary()->getFunctions().getProcAddress(name);

    // Fall-back to dynlib
    if (!ptr)
        ptr = (FunctionPtr)m_core->getLibrary()->getGLLibrary().getFunction(name);

    return ptr;
}

void Context::makeCurrent(void)
{
    const Functions &wgl = m_core->getLibrary()->getFunctions();
    if (!wgl.makeCurrent(m_deviceCtx, m_context))
        TCU_THROW(ResourceError, "wglMakeCurrent() failed");
}

void Context::swapBuffers(void) const
{
    const Functions &wgl = m_core->getLibrary()->getFunctions();
    if (!wgl.swapLayerBuffers(m_deviceCtx, WGL_SWAP_MAIN_PLANE))
        TCU_THROW(ResourceError, "wglSwapBuffers() failed");
}

bool isSupportedByTests(const PixelFormatInfo &info)
{
    if (!info.supportOpenGL)
        return false;

    if (info.acceleration != wgl::PixelFormatInfo::ACCELERATION_FULL)
        return false;

    if (info.pixelType != wgl::PixelFormatInfo::PIXELTYPE_RGBA)
        return false;

    if ((info.surfaceTypes & wgl::PixelFormatInfo::SURFACE_WINDOW) == 0)
        return false;

    if (info.needPalette || info.needSystemPalette)
        return false;

    if (info.numOverlays != 0 || info.numUnderlays != 0)
        return false;

    if (info.stereo)
        return false;

    return true;
}

int choosePixelFormat(const Core &wgl, HDC deviceCtx, const glu::RenderConfig &config)
{
    std::vector<int> pixelFormats = wgl.getPixelFormats(deviceCtx);

    for (std::vector<int>::const_iterator fmtIter = pixelFormats.begin(); fmtIter != pixelFormats.end(); ++fmtIter)
    {
        const PixelFormatInfo info = wgl.getPixelFormatInfo(deviceCtx, *fmtIter);

        // Skip formats that are fundamentally not compatible with current tests
        if (!isSupportedByTests(info))
            continue;

        if (config.redBits != glu::RenderConfig::DONT_CARE && config.redBits != info.redBits)
            continue;

        if (config.greenBits != glu::RenderConfig::DONT_CARE && config.greenBits != info.greenBits)
            continue;

        if (config.blueBits != glu::RenderConfig::DONT_CARE && config.blueBits != info.blueBits)
            continue;

        if (config.alphaBits != glu::RenderConfig::DONT_CARE && config.alphaBits != info.alphaBits)
            continue;

        if (config.depthBits != glu::RenderConfig::DONT_CARE && config.depthBits != info.depthBits)
            continue;

        if (config.stencilBits != glu::RenderConfig::DONT_CARE && config.stencilBits != info.stencilBits)
            continue;

        if (config.numSamples != glu::RenderConfig::DONT_CARE && config.numSamples != info.samples)
            continue;

        // Passed all tests - select this.
        return info.pixelFormat;
    }

    return -1;
}

} // namespace wgl
} // namespace tcu
