/*
 * ***** BEGIN GPL LICENSE BLOCK *****
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 * The Original Code is Copyright (C) 2013 Blender Foundation.
 * All rights reserved.
 *
 * The Original Code is: all of this file.
 *
 * Contributor(s): Jason Wilkins
 *
 * ***** END GPL LICENSE BLOCK *****
 */

/** \file ghost/intern/GHOST_ContextWGL.cpp
 *  \ingroup GHOST
 *
 * Definition of GHOST_ContextWGL class.
 */

#include "GHOST_ContextWGL.h"

#include <tchar.h>

#include <cstdio>
#include <cassert>
#include <vector>


#ifdef WITH_GLEW_MX
WGLEWContext *wglewContext = NULL;
#endif

HGLRC GHOST_ContextWGL::s_sharedHGLRC = NULL;
int   GHOST_ContextWGL::s_sharedCount = 0;

bool GHOST_ContextWGL::s_singleContextMode = false;


/* Intel video-cards don't work fine with multiple contexts and
 * have to share the same context for all windows.
 * But if we just share context for all windows it could work incorrect
 * with multiple videocards configuration. Suppose, that Intel videocards
 * can't be in multiple-devices configuration. */
static bool is_crappy_intel_card()
{
	return strstr((const char *)glGetString(GL_VENDOR), "Intel") != NULL;
}


GHOST_ContextWGL::GHOST_ContextWGL(
        bool stereoVisual,
        bool alphaBackground,
        GHOST_TUns16 numOfAASamples,
        HWND hWnd,
        HDC hDC,
        int contextProfileMask,
        int contextMajorVersion,
        int contextMinorVersion,
        int contextFlags,
        int contextResetNotificationStrategy)
    : GHOST_Context(stereoVisual, numOfAASamples),
      m_hWnd(hWnd),
      m_hDC(hDC),
      m_contextProfileMask(contextProfileMask),
      m_contextMajorVersion(contextMajorVersion),
      m_contextMinorVersion(contextMinorVersion),
      m_contextFlags(contextFlags),
      m_alphaBackground(alphaBackground),
      m_contextResetNotificationStrategy(contextResetNotificationStrategy),
      m_hGLRC(NULL)
#ifdef WITH_GLEW_MX
      ,
      m_wglewContext(NULL)
#endif
#ifndef NDEBUG
      ,
      m_dummyVendor(NULL),
      m_dummyRenderer(NULL),
      m_dummyVersion(NULL)
#endif
{
	assert(m_hWnd);
	assert(m_hDC);
}


GHOST_ContextWGL::~GHOST_ContextWGL()
{
	if (m_hGLRC != NULL) {
		if (m_hGLRC == ::wglGetCurrentContext())
			WIN32_CHK(::wglMakeCurrent(NULL, NULL));

		if (m_hGLRC != s_sharedHGLRC || s_sharedCount == 1) {
			assert(s_sharedCount > 0);

			s_sharedCount--;

			if (s_sharedCount == 0)
				s_sharedHGLRC = NULL;

			WIN32_CHK(::wglDeleteContext(m_hGLRC));
		}
	}

#ifdef WITH_GLEW_MX
	delete m_wglewContext;
#endif

#ifndef NDEBUG
	free((void*)m_dummyRenderer);
	free((void*)m_dummyVendor);
	free((void*)m_dummyVersion);
#endif
}


GHOST_TSuccess GHOST_ContextWGL::swapBuffers()
{
	return WIN32_CHK(::SwapBuffers(m_hDC)) ? GHOST_kSuccess : GHOST_kFailure;
}


GHOST_TSuccess GHOST_ContextWGL::setSwapInterval(int interval)
{
	if (WGLEW_EXT_swap_control)
		return WIN32_CHK(::wglSwapIntervalEXT(interval)) == TRUE ? GHOST_kSuccess : GHOST_kFailure;
	else
		return GHOST_kFailure;
}


GHOST_TSuccess GHOST_ContextWGL::getSwapInterval(int &intervalOut)
{
	if (WGLEW_EXT_swap_control) {
		intervalOut = ::wglGetSwapIntervalEXT();
		return GHOST_kSuccess;
	}
	else {
		return GHOST_kFailure;
	}
}


GHOST_TSuccess GHOST_ContextWGL::activateDrawingContext()
{
	if (WIN32_CHK(::wglMakeCurrent(m_hDC, m_hGLRC))) {
		activateGLEW();
		return GHOST_kSuccess;
	}
	else {
		return GHOST_kFailure;
	}
}


/* Ron Fosner's code for weighting pixel formats and forcing software.
 * See http://www.opengl.org/resources/faq/technical/weight.cpp
 */
static int weight_pixel_format(PIXELFORMATDESCRIPTOR &pfd, PIXELFORMATDESCRIPTOR &preferredPFD)
{
	int weight = 0;

	/* assume desktop color depth is 32 bits per pixel */

	/* cull unusable pixel formats */
	/* if no formats can be found, can we determine why it was rejected? */
	if (!(pfd.dwFlags & PFD_SUPPORT_OPENGL)  ||
	    !(pfd.dwFlags & PFD_DRAW_TO_WINDOW)  ||
	    !(pfd.dwFlags & PFD_DOUBLEBUFFER)    || /* Blender _needs_ this */
	    !(pfd.iPixelType == PFD_TYPE_RGBA)   ||
	     (pfd.cDepthBits < 16)               ||
	     (pfd.cColorBits > 32)               || /* 64 bit formats disable aero */
	     (pfd.dwFlags & PFD_GENERIC_FORMAT))    /* no software renderers */
	{
		return 0;
	}

	weight = 1;  /* it's usable */

	/* the bigger the depth buffer the better */
	/* give no weight to a 16-bit depth buffer, because those are crap */
	weight += pfd.cDepthBits - 16;

	weight += pfd.cColorBits -  8;

	if (preferredPFD.cAlphaBits > 0 && pfd.cAlphaBits > 0)
		weight++;
#ifdef WIN32_COMPOSITING
	if ((preferredPFD.dwFlags & PFD_SUPPORT_COMPOSITION) && (pfd.dwFlags & PFD_SUPPORT_COMPOSITION))
		weight++;
#endif
#ifdef GHOST_OPENGL_STENCIL
	if (pfd.cStencilBits >= 8)
		weight++;
#endif

	/* want swap copy capability -- it matters a lot */
	if (pfd.dwFlags & PFD_SWAP_COPY)
		weight += 16;

	return weight;
}


/*
 * A modification of Ron Fosner's replacement for ChoosePixelFormat
 * returns 0 on error, else returns the pixel format number to be used
 */
static int choose_pixel_format_legacy(HDC hDC, PIXELFORMATDESCRIPTOR &preferredPFD)
{
	int iPixelFormat = 0;
	int weight = 0;

	int iStereoPixelFormat = 0;
	int stereoWeight = 0;

	/* choose a pixel format using the useless Windows function in case we come up empty handed */
	int iLastResortPixelFormat = ::ChoosePixelFormat(hDC, &preferredPFD);

	WIN32_CHK(iLastResortPixelFormat != 0);

	int lastPFD = ::DescribePixelFormat(hDC, 1, sizeof(PIXELFORMATDESCRIPTOR), NULL);

	WIN32_CHK(lastPFD != 0);

	for (int i = 1; i <= lastPFD; i++) {
		PIXELFORMATDESCRIPTOR pfd;
		int check = ::DescribePixelFormat(hDC, i, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

		WIN32_CHK(check == lastPFD);

		int w = weight_pixel_format(pfd, preferredPFD);

		if (w > weight) {
			weight = w;
			iPixelFormat = i;
		}

		if (w > stereoWeight && (preferredPFD.dwFlags & pfd.dwFlags & PFD_STEREO)) {
			stereoWeight = w;
			iStereoPixelFormat = i;
		}
	}

	/* choose any available stereo format over a non-stereo format */
	if (iStereoPixelFormat != 0)
		iPixelFormat = iStereoPixelFormat;

	if (iPixelFormat == 0) {
		fprintf(stderr, "Warning! Using result of ChoosePixelFormat.\n");
		iPixelFormat = iLastResortPixelFormat;
	}

	return iPixelFormat;
}


/*
 * Clone a window for the purpose of creating a temporary context to initialize WGL extensions.
 * There is no generic way to clone the lpParam parameter, so the caller is responsible for cloning it themselves.
 */

static HWND clone_window(HWND hWnd, LPVOID lpParam)
{
	int count;

	SetLastError(NO_ERROR);

	DWORD dwExStyle = GetWindowLong(hWnd, GWL_EXSTYLE);
	WIN32_CHK(GetLastError() == NO_ERROR);

	WCHAR lpClassName[100] = L"";
	count = GetClassNameW(hWnd, lpClassName, sizeof(lpClassName));
	WIN32_CHK(count != 0);

	WCHAR lpWindowName[100] = L"";
	count = GetWindowTextW(hWnd, lpWindowName, sizeof(lpWindowName));
	WIN32_CHK(count != 0);

	DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE);
	WIN32_CHK(GetLastError() == NO_ERROR);

	RECT rect;
	GetWindowRect(hWnd, &rect);
	WIN32_CHK(GetLastError() == NO_ERROR);

	HWND hWndParent = (HWND)GetWindowLongPtr(hWnd, GWLP_HWNDPARENT);
	WIN32_CHK(GetLastError() == NO_ERROR);

	HMENU hMenu = GetMenu(hWnd);
	WIN32_CHK(GetLastError() == NO_ERROR);

	HINSTANCE hInstance = (HINSTANCE)GetWindowLongPtr(hWnd, GWLP_HINSTANCE);
	WIN32_CHK(GetLastError() == NO_ERROR);

	HWND hwndCloned = CreateWindowExW(
	        dwExStyle,
	        lpClassName,
	        lpWindowName,
	        dwStyle,
	        rect.left,
	        rect.top,
	        rect.right - rect.left,
	        rect.bottom - rect.top,
	        hWndParent,
	        hMenu,
	        hInstance,
	        lpParam);

	WIN32_CHK(hwndCloned != NULL);

	return hwndCloned;
}


void GHOST_ContextWGL::initContextWGLEW(PIXELFORMATDESCRIPTOR &preferredPFD)
{
	HWND  dummyHWND  = NULL;
	HDC   dummyHDC   = NULL;
	HGLRC dummyHGLRC = NULL;

	HDC   prevHDC;
	HGLRC prevHGLRC;

	int iPixelFormat;


#ifdef WITH_GLEW_MX
	wglewContext = new WGLEWContext;
	memset(wglewContext, 0, sizeof(WGLEWContext));

	delete m_wglewContext;
	m_wglewContext = wglewContext;
#endif

	SetLastError(NO_ERROR);

	prevHDC = ::wglGetCurrentDC();
	WIN32_CHK(GetLastError() == NO_ERROR);

	prevHGLRC = ::wglGetCurrentContext();
	WIN32_CHK(GetLastError() == NO_ERROR);

	dummyHWND = clone_window(m_hWnd, NULL);

	if (dummyHWND == NULL)
		goto finalize;

	dummyHDC = GetDC(dummyHWND);

	if (!WIN32_CHK(dummyHDC != NULL))
		goto finalize;

	iPixelFormat = choose_pixel_format_legacy(dummyHDC, preferredPFD);

	if (iPixelFormat == 0)
		goto finalize;

	PIXELFORMATDESCRIPTOR chosenPFD;
	if (!WIN32_CHK(::DescribePixelFormat(dummyHDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD)))
		goto finalize;

	if (!WIN32_CHK(::SetPixelFormat(dummyHDC, iPixelFormat, &chosenPFD)))
		goto finalize;

	dummyHGLRC = ::wglCreateContext(dummyHDC);

	if (!WIN32_CHK(dummyHGLRC != NULL))
		goto finalize;

	if (!WIN32_CHK(::wglMakeCurrent(dummyHDC, dummyHGLRC)))
		goto finalize;

#ifdef WITH_GLEW_MX
	if (GLEW_CHK(wglewInit()) != GLEW_OK)
		fprintf(stderr, "Warning! WGLEW failed to initialize properly.\n");
#else
	if (GLEW_CHK(glewInit()) != GLEW_OK)
		fprintf(stderr, "Warning! Dummy GLEW/WGLEW failed to initialize properly.\n");
#endif

	// the following are not technially WGLEW, but they also require a context to work

#ifndef NDEBUG
	free((void*)m_dummyRenderer);
	free((void*)m_dummyVendor);
	free((void*)m_dummyVersion);

	m_dummyRenderer = _strdup(reinterpret_cast<const char *>(glGetString(GL_RENDERER)));
	m_dummyVendor   = _strdup(reinterpret_cast<const char *>(glGetString(GL_VENDOR)));
	m_dummyVersion  = _strdup(reinterpret_cast<const char *>(glGetString(GL_VERSION)));
#endif

	s_singleContextMode = is_crappy_intel_card();

finalize:
	WIN32_CHK(::wglMakeCurrent(prevHDC, prevHGLRC));

	if (dummyHGLRC != NULL)
		WIN32_CHK(::wglDeleteContext(dummyHGLRC));

	if (dummyHWND != NULL) {
		if (dummyHDC != NULL)
			WIN32_CHK(::ReleaseDC(dummyHWND, dummyHDC));

		WIN32_CHK(::DestroyWindow(dummyHWND));
	}
}


static void makeAttribList(
        std::vector<int>& out,
        bool stereoVisual,
        int numOfAASamples,
        int swapMethod,
        bool needAlpha,
        bool needStencil,
        bool sRGB)
{
	out.clear();
	out.reserve(30);

	out.push_back(WGL_SUPPORT_OPENGL_ARB);
	out.push_back(GL_TRUE);

	out.push_back(WGL_DRAW_TO_WINDOW_ARB);
	out.push_back(GL_TRUE);

	out.push_back(WGL_DOUBLE_BUFFER_ARB);
	out.push_back(GL_TRUE);

	out.push_back(WGL_ACCELERATION_ARB);
	out.push_back(WGL_FULL_ACCELERATION_ARB);

	out.push_back(WGL_SWAP_METHOD_ARB);
	out.push_back(swapMethod);

	if (stereoVisual) {
		out.push_back(WGL_STEREO_ARB);
		out.push_back(GL_TRUE);
	}

	out.push_back(WGL_PIXEL_TYPE_ARB);
	out.push_back(WGL_TYPE_RGBA_ARB);

	out.push_back(WGL_COLOR_BITS_ARB);
	out.push_back(24);

	out.push_back(WGL_DEPTH_BITS_ARB);
	out.push_back(24);

	if (needAlpha) {
		out.push_back(WGL_ALPHA_BITS_ARB);
		out.push_back(8);
	}

	if (needStencil) {
		out.push_back(WGL_STENCIL_BITS_ARB);
		out.push_back(8);
	}

	if (numOfAASamples > 0) {
		out.push_back(WGL_SAMPLES_ARB);
		out.push_back(numOfAASamples);

		out.push_back(WGL_SAMPLE_BUFFERS_ARB);
		out.push_back(GL_TRUE);
	}

	if (sRGB) {
		out.push_back(WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB);
		out.push_back(GL_TRUE);
	}

	out.push_back(0);
}


int GHOST_ContextWGL::_choose_pixel_format_arb_2(
        bool stereoVisual,
        int *numOfAASamples,
        bool needAlpha,
        bool needStencil,
        bool sRGB,
        int swapMethod)
{
	std::vector<int> iAttributes;

#define _MAX_PIXEL_FORMATS 32

	int iPixelFormat = 0;
	int iPixelFormats[_MAX_PIXEL_FORMATS];

	int samples;

	// guard against some insanely high number of samples
	if (*numOfAASamples > 64) {
		fprintf(stderr, "Warning! Clamping number of samples to 64.\n");
		samples = 64;
	}
	else {
		samples = *numOfAASamples;
	}

	// request a format with as many samples as possible, but not more than requested
	while (samples >= 0) {
		makeAttribList(
		        iAttributes,
		        stereoVisual,
		        samples,
		        swapMethod,
		        needAlpha,
		        needStencil,
		        sRGB);

		UINT nNumFormats;
		WIN32_CHK(wglChoosePixelFormatARB(m_hDC, &(iAttributes[0]), NULL, _MAX_PIXEL_FORMATS, iPixelFormats, &nNumFormats));

#ifdef WIN32_COMPOSITING
		if (needAlpha && nNumFormats) {
			// scan through all pixel format to make sure one supports compositing
			PIXELFORMATDESCRIPTOR pfd;
			int i;

			for (i = 0; i < nNumFormats; i++) {
				if (DescribePixelFormat(m_hDC, iPixelFormats[i], sizeof(PIXELFORMATDESCRIPTOR), &pfd)) {
					if (pfd.dwFlags & PFD_SUPPORT_COMPOSITION) {
						iPixelFormat = iPixelFormats[i];
						break;
					}
				}
			}
			if (i == nNumFormats) {
				fprintf(stderr,
						"Warning! Unable to find a pixel format with compositing capability.\n");
				iPixelFormat = iPixelFormats[0];
			}
		}
		else
#endif
			iPixelFormat = iPixelFormats[0];
		/* total number of formats that match (regardless of size of iPixelFormat array)
		 * see: WGL_ARB_pixel_format extension spec */
		if (nNumFormats > 0)
			break;

		/* if not reset, then the state of iPixelFormat is undefined after call to wglChoosePixelFormatARB
		 * see: WGL_ARB_pixel_format extension spec */
		iPixelFormat = 0;

		samples--;
	}

	// check how many samples were actually gotten
	if (iPixelFormat != 0) {
		int iQuery[] = { WGL_SAMPLES_ARB };
		int actualSamples, alphaBits;
		wglGetPixelFormatAttribivARB(m_hDC, iPixelFormat, 0, 1, iQuery, &actualSamples);

		if (actualSamples != *numOfAASamples) {
			fprintf(stderr,
			        "Warning! Unable to find a multisample pixel format that supports exactly %d samples. "
			        "Substituting one that uses %d samples.\n",
			        *numOfAASamples, actualSamples);

			*numOfAASamples = actualSamples; // set context property to actual value
		}
		if (needAlpha) {
			iQuery[0] = WGL_ALPHA_BITS_ARB;
			wglGetPixelFormatAttribivARB(m_hDC, iPixelFormat, 0, 1, iQuery, &alphaBits);
			if (alphaBits == 0) {
				fprintf(stderr,
						"Warning! Unable to find a frame buffer with alpha channel.\n");
			}
		}
	}
	else {
		*numOfAASamples = 0;
	}
	return iPixelFormat;
}


int GHOST_ContextWGL::_choose_pixel_format_arb_1(bool stereoVisual,
        int numOfAASamples,
        bool needAlpha,
        bool needStencil,
        bool sRGB,
        int *swapMethodOut)
{
	int iPixelFormat;
	int copyPixelFormat = 0;
	int undefPixelFormat = 0;
	int exchPixelFormat = 0;
	int copyNumOfAASamples = 0;
	int undefNumOfAASamples = 0;
	int exchNumOfAASamples = 0;

	*swapMethodOut = WGL_SWAP_COPY_ARB;
	copyNumOfAASamples = numOfAASamples;
	copyPixelFormat  = _choose_pixel_format_arb_2(
		stereoVisual, &copyNumOfAASamples, needAlpha, needStencil, sRGB, *swapMethodOut);

	if (copyPixelFormat == 0 || copyNumOfAASamples < numOfAASamples) {
		*swapMethodOut = WGL_SWAP_UNDEFINED_ARB;
		undefNumOfAASamples = numOfAASamples;
		undefPixelFormat = _choose_pixel_format_arb_2(
			stereoVisual, &undefNumOfAASamples, needAlpha, needStencil, sRGB, *swapMethodOut);

		if (undefPixelFormat == 0 || undefNumOfAASamples < numOfAASamples) {
			*swapMethodOut = WGL_SWAP_EXCHANGE_ARB;
			exchNumOfAASamples = numOfAASamples;
			exchPixelFormat = _choose_pixel_format_arb_2(
				stereoVisual, &exchNumOfAASamples, needAlpha, needStencil, sRGB, *swapMethodOut);
			if (exchPixelFormat == 0 || exchNumOfAASamples < numOfAASamples) {
				// the number of AA samples cannot be met, take the highest
				if (undefPixelFormat != 0 && undefNumOfAASamples >= exchNumOfAASamples) {
					exchNumOfAASamples = undefNumOfAASamples;
					exchPixelFormat = undefPixelFormat;
					*swapMethodOut = WGL_SWAP_UNDEFINED_ARB;
				}
				if (copyPixelFormat != 0 && copyNumOfAASamples >= exchNumOfAASamples) {
					exchNumOfAASamples = copyNumOfAASamples;
					exchPixelFormat = copyPixelFormat;
					*swapMethodOut = WGL_SWAP_COPY_ARB;
				}
			}
			iPixelFormat = exchPixelFormat;
			m_numOfAASamples = exchNumOfAASamples;
		}
		else {
			iPixelFormat = undefPixelFormat;
			m_numOfAASamples = undefNumOfAASamples;
		}
	}
	else {
		iPixelFormat = copyPixelFormat;
		m_numOfAASamples = copyNumOfAASamples;
	}
	return iPixelFormat;
}


int GHOST_ContextWGL::choose_pixel_format_arb(
        bool stereoVisual,
        int numOfAASamples,
        bool needAlpha,
        bool needStencil,
        bool sRGB)
{
	int iPixelFormat;
	int swapMethodOut;

	iPixelFormat = _choose_pixel_format_arb_1(
	        stereoVisual,
	        numOfAASamples,
	        needAlpha,
	        needStencil,
	        sRGB,
	        &swapMethodOut);

	if (iPixelFormat == 0 && stereoVisual) {
		fprintf(stderr, "Warning! Unable to find a stereo pixel format.\n");

		iPixelFormat = _choose_pixel_format_arb_1(
		        false,
		        numOfAASamples,
		        needAlpha,
		        needStencil,
		        sRGB,
		        &swapMethodOut);

		m_stereoVisual = false;  // set context property to actual value
	}

	if (swapMethodOut != WGL_SWAP_COPY_ARB) {
		fprintf(stderr,
		        "Warning! Unable to find a pixel format that supports WGL_SWAP_COPY_ARB. "
		        "Substituting one that uses %s.\n",
		        swapMethodOut == WGL_SWAP_UNDEFINED_ARB ? "WGL_SWAP_UNDEFINED_ARB" : "WGL_SWAP_EXCHANGE_ARB");
	}

	return iPixelFormat;
}


int GHOST_ContextWGL::choose_pixel_format(
        bool stereoVisual,
        int  numOfAASamples,
        bool needAlpha,
        bool needStencil,
        bool sRGB)
{
	PIXELFORMATDESCRIPTOR preferredPFD = {
		sizeof(PIXELFORMATDESCRIPTOR),   /* size */
		1,                               /* version */
		(DWORD) (
		PFD_SUPPORT_OPENGL |
		PFD_DRAW_TO_WINDOW |
		PFD_SWAP_COPY      |             /* support swap copy */
		PFD_DOUBLEBUFFER   |             /* support double-buffering */
		(stereoVisual ? PFD_STEREO : 0) |/* support stereo */
		(
#ifdef WIN32_COMPOSITING
		needAlpha ? PFD_SUPPORT_COMPOSITION :	 /* support composition for transparent background */
#endif
		0
		)),
		PFD_TYPE_RGBA,                   /* color type */
		(BYTE) (needAlpha ? 32 : 24),           /* preferred color depth */
		0, 0, 0, 0, 0, 0,                /* color bits (ignored) */
		(BYTE) (needAlpha ? 8 : 0),               /* alpha buffer */
		0,                               /* alpha shift (ignored) */
		0,                               /* no accumulation buffer */
		0, 0, 0, 0,                      /* accum bits (ignored) */
		24,                              /* depth buffer */
		(BYTE) (needStencil ? 8 : 0),             /* stencil buffer */
		0,                               /* no auxiliary buffers */
		PFD_MAIN_PLANE,                  /* main layer */
		0,                               /* reserved */
		0, 0, 0                          /* layer, visible, and damage masks (ignored) */
	};

	initContextWGLEW(preferredPFD);

	if (numOfAASamples > 0 && !WGLEW_ARB_multisample) {
		fprintf(stderr, "Warning! Unable to request a multisample framebuffer.\n");
		numOfAASamples = 0;
	}

	if (sRGB && !(WGLEW_ARB_framebuffer_sRGB || WGLEW_EXT_framebuffer_sRGB)) {
		fprintf(stderr, "Warning! Unable to request an sRGB framebuffer.\n");
		sRGB = false;
	}

	int iPixelFormat = 0;

	if (WGLEW_ARB_pixel_format)
		iPixelFormat = choose_pixel_format_arb(stereoVisual, numOfAASamples, needAlpha, needStencil, sRGB);

	if (iPixelFormat == 0)
		iPixelFormat = choose_pixel_format_legacy(m_hDC, preferredPFD);

	return iPixelFormat;
}


#ifndef NDEBUG
static void reportContextString(const char *name, const char *dummy, const char *context)
{
	fprintf(stderr, "%s: %s\n", name, context);

	if (strcmp(dummy, context) != 0)
		fprintf(stderr, "Warning! Dummy %s: %s\n", name, dummy);
}
#endif


GHOST_TSuccess GHOST_ContextWGL::initializeDrawingContext()
{
	const bool needAlpha = m_alphaBackground;

#ifdef GHOST_OPENGL_STENCIL
	const bool needStencil = true;
#else
	const bool needStencil = false;
#endif

#ifdef GHOST_OPENGL_SRGB
	const bool sRGB = true;
#else
	const bool sRGB = false;
#endif

	HGLRC prevHGLRC;
	HDC   prevHDC;

	int iPixelFormat;
	int lastPFD;

	PIXELFORMATDESCRIPTOR chosenPFD;


	SetLastError(NO_ERROR);

	prevHGLRC = ::wglGetCurrentContext();
	WIN32_CHK(GetLastError() == NO_ERROR);

	prevHDC   = ::wglGetCurrentDC();
	WIN32_CHK(GetLastError() == NO_ERROR);

	iPixelFormat = choose_pixel_format(m_stereoVisual, m_numOfAASamples, needAlpha, needStencil, sRGB);

	if (iPixelFormat == 0) {
		::wglMakeCurrent(prevHDC, prevHGLRC);
		return GHOST_kFailure;
	}

	lastPFD = ::DescribePixelFormat(m_hDC, iPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosenPFD);

	if (!WIN32_CHK(lastPFD != 0)) {
		::wglMakeCurrent(prevHDC, prevHGLRC);
		return GHOST_kFailure;
	}

	if (needAlpha && chosenPFD.cAlphaBits == 0)
		fprintf(stderr, "Warning! Unable to find a pixel format with an alpha channel.\n");

	if (needStencil && chosenPFD.cStencilBits == 0)
		fprintf(stderr, "Warning! Unable to find a pixel format with a stencil buffer.\n");

	if (!WIN32_CHK(::SetPixelFormat(m_hDC, iPixelFormat, &chosenPFD))) {
		::wglMakeCurrent(prevHDC, prevHGLRC);
		return GHOST_kFailure;
	}

	activateWGLEW();

	if (WGLEW_ARB_create_context) {
		int profileBitCore   = m_contextProfileMask & WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
		int profileBitCompat = m_contextProfileMask & WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;

#ifdef WITH_GLEW_ES
		int profileBitES     = m_contextProfileMask & WGL_CONTEXT_ES_PROFILE_BIT_EXT;
#endif

		if (!WGLEW_ARB_create_context_profile && profileBitCore)
			fprintf(stderr, "Warning! OpenGL core profile not available.\n");

		if (!WGLEW_ARB_create_context_profile && profileBitCompat)
			fprintf(stderr, "Warning! OpenGL compatibility profile not available.\n");

#ifdef WITH_GLEW_ES
		if (!WGLEW_EXT_create_context_es_profile && profileBitES && m_contextMajorVersion == 1)
			fprintf(stderr, "Warning! OpenGL ES profile not available.\n");

		if (!WGLEW_EXT_create_context_es2_profile && profileBitES && m_contextMajorVersion == 2)
			fprintf(stderr, "Warning! OpenGL ES2 profile not available.\n");
#endif

		int profileMask = 0;

		if (WGLEW_ARB_create_context_profile && profileBitCore)
			profileMask |= profileBitCore;

		if (WGLEW_ARB_create_context_profile && profileBitCompat)
			profileMask |= profileBitCompat;

#ifdef WITH_GLEW_ES
		if (WGLEW_EXT_create_context_es_profile && profileBitES)
			profileMask |= profileBitES;
#endif

		if (profileMask != m_contextProfileMask)
			fprintf(stderr, "Warning! Ignoring untested OpenGL context profile mask bits.");

		std::vector<int> iAttributes;

		if (profileMask) {
			iAttributes.push_back(WGL_CONTEXT_PROFILE_MASK_ARB);
			iAttributes.push_back(profileMask);
		}

		if (m_contextMajorVersion != 0) {
			iAttributes.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
			iAttributes.push_back(m_contextMajorVersion);
		}

		if (m_contextMinorVersion != 0) {
			iAttributes.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
			iAttributes.push_back(m_contextMinorVersion);
		}

		if (m_contextFlags != 0) {
			iAttributes.push_back(WGL_CONTEXT_FLAGS_ARB);
			iAttributes.push_back(m_contextFlags);
		}

		if (m_contextResetNotificationStrategy != 0) {
			if (WGLEW_ARB_create_context_robustness) {
				iAttributes.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);
				iAttributes.push_back(m_contextResetNotificationStrategy);
			}
			else {
				fprintf(stderr, "Warning! Cannot set the reset notification strategy.");
			}
		}

		iAttributes.push_back(0);

		if (!s_singleContextMode || s_sharedHGLRC == NULL)
			m_hGLRC = ::wglCreateContextAttribsARB(m_hDC, NULL, &(iAttributes[0]));
		else
			m_hGLRC = s_sharedHGLRC;
	}
	else {
		if (m_contextProfileMask  != 0)
			fprintf(stderr, "Warning! Legacy WGL is unable to select between OpenGL profiles.");

		if (m_contextMajorVersion != 0 || m_contextMinorVersion != 0)
			fprintf(stderr, "Warning! Legacy WGL is unable to select between OpenGL versions.");

		if (m_contextFlags != 0)
			fprintf(stderr, "Warning! Legacy WGL is unable to set context flags.");

		if (!s_singleContextMode || s_sharedHGLRC == NULL)
			m_hGLRC = ::wglCreateContext(m_hDC);
		else
			m_hGLRC = s_sharedHGLRC;
	}

	if (!WIN32_CHK(m_hGLRC != NULL)) {
		::wglMakeCurrent(prevHDC, prevHGLRC);
		return GHOST_kFailure;
	}

	if (s_sharedHGLRC == NULL)
		s_sharedHGLRC = m_hGLRC;

	s_sharedCount++;

	if (!s_singleContextMode && s_sharedHGLRC != m_hGLRC && !WIN32_CHK(::wglShareLists(s_sharedHGLRC, m_hGLRC))) {
		::wglMakeCurrent(prevHDC, prevHGLRC);
		return GHOST_kFailure;
	}

	if (!WIN32_CHK(::wglMakeCurrent(m_hDC, m_hGLRC))) {
		::wglMakeCurrent(prevHDC, prevHGLRC);
		return GHOST_kFailure;
	}

	initContextGLEW();

	initClearGL();
	::SwapBuffers(m_hDC);

	const char *vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
	const char *renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
	const char *version = reinterpret_cast<const char*>(glGetString(GL_VERSION));

#ifndef NDEBUG
	reportContextString("Vendor",   m_dummyVendor,   vendor);
	reportContextString("Renderer", m_dummyRenderer, renderer);
	reportContextString("Version",  m_dummyVersion,  version);
#endif

	if ((strcmp(vendor, "Microsoft Corporation") == 0 ||
	     strcmp(renderer, "GDI Generic") == 0) && version[0] == '1' && version[2] == '1')
	{
		MessageBox(m_hWnd, "Your system does not use 3D hardware acceleration.\n"
		                   "Blender requires a graphics driver with OpenGL 2.1 support.\n\n"
		                   "This may be caused by:\n"
		                   "* A missing or faulty graphics driver installation.\n"
		                   "  Blender needs a graphics card driver to work correctly.\n"
		                   "* Accessing Blender through a remote connection.\n"
		                   "* Using Blender through a virtual machine.\n\n"
		                   "The program will now close.",
		           "Blender - Can't detect 3D hardware accelerated Driver!",
		           MB_OK | MB_ICONERROR);
		exit(0);
	}
	else if (version[0] < '2' || (version[0] == '2' && version[2] < '1')) {
		MessageBox(m_hWnd, "Blender requires a graphics driver with OpenGL 2.1 support.\n\n"
		                   "The program will now close.",
		           "Blender - Unsupported Graphics Driver!",
		           MB_OK | MB_ICONERROR);
		exit(0);
	}

	return GHOST_kSuccess;
}


GHOST_TSuccess GHOST_ContextWGL::releaseNativeHandles()
{
	GHOST_TSuccess success = m_hGLRC != s_sharedHGLRC || s_sharedCount == 1 ? GHOST_kSuccess : GHOST_kFailure;

	m_hWnd = NULL;
	m_hDC  = NULL;

	return success;
}
