/*
								+---------------------------------+
								|                                 |
								| ***   OpenGL ES 20 driver   *** |
								|                                 |
								|  Copyright  © -tHE SWINe- 2012  |
								|                                 |
								|         OpenGLESDrv.cpp         |
								|                                 |
								+---------------------------------+
*/

/**
 *	@file gles2/OpenGLESDrv.cpp
 *	@author -tHE SWINe-
 *	@date 2012
 *	@brief OpenGL ES 2.0 driver
 */

#include "../NewFix.h"
#include "../CallStack.h"
#include <stdio.h>
#include "OpenGLESDrv.h"

#if !defined(__HAVE_EGL)

/*
 *								=== WGL_ARB_create_context / GLX_ARB_create_context ===
 */

#ifndef WGL_ARB_create_context
#define WGL_ARB_create_context 1
#define __GENERATE_WGL_ARB_create_context__

#if defined(_WIN32) || defined(_WIN64)
extern HGLRC (GLApi *wglCreateContextAttribsARB)(HDC, HGLRC, const int*);
#else //_WIN32 || _WIN64
extern GLXContext (GLApi *glXCreateContextAttribsARB)(Display*, GLXFBConfig, GLXContext, Bool, const int *);
//extern GLXFBConfig *(GLApi *glXChooseFBConfigARB)(Display*, int, const int*, int*);
//extern XVisualInfo *(GLApi *glXGetVisualFromFBConfigARB)(Display*, GLXFBConfig);
#define glXChooseFBConfigARB glXChooseFBConfig
#define glXGetVisualFromFBConfigARB glXGetVisualFromFBConfig
#endif //_WIN32 || _WIN64

#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 GL_ERROR_INVALID_VERSION_ARB				0x2095
#define GL_ERROR_INVALID_PROFILE_ARB				0x2096

#endif //WGL_ARB_create_context

/*
 *								=== ~WGL_ARB_create_context / GLX_ARB_create_context ===
 */

#endif // __HAVE_EGL

/*
 *								=== CGLESDriver ===
 */

#if !defined(__HAVE_EGL)

#ifdef __GENERATE_WGL_ARB_create_context__

#if defined(_WIN32) || defined(_WIN64)
HGLRC (GLApi *wglCreateContextAttribsARB)(HDC, HGLRC, const int*) = 0;
#else //_WIN32 || _WIN64
GLXContext (GLApi *glXCreateContextAttribsARB)(Display*, GLXFBConfig, GLXContext, Bool, const int *) = 0;
//GLXFBConfig *(GLApi *glXChooseFBConfigARB)(Display*, int, const int*, int*) = 0;
//XVisualInfo *(GLApi *glXGetVisualFromFBConfigARB)(Display*, GLXFBConfig) = 0;
#endif //_WIN32 || _WIN64

#endif //__GENERATE_WGL_ARB_create_context__

int CGLESDriver::n_GetCreateContextARBFuncPointers()
{
	int n_failed_functions = 0;

#ifdef __GENERATE_WGL_ARB_create_context__

#if defined(_WIN32) || defined(_WIN64)
	if(!(wglCreateContextAttribsARB = (HGLRC(GLApi*)(HDC, HGLRC, const int*))wglGetProcAddress("wglCreateContextAttribsARB"))) ++ n_failed_functions;
#else //_WIN32 || _WIN64
	if(!(glXCreateContextAttribsARB = (GLXContext(GLApi*)(Display*, GLXFBConfig, GLXContext, Bool, const int*))glXGetProcAddress((const GLubyte*)"glXCreateContextAttribsARB"))) ++ n_failed_functions;
	//if(!(glXChooseFBConfigARB = (GLXFBConfig*(GLApi*)(Display*, int, const int*, int*))glXGetProcAddressARB((const GLubyte*)"glXChooseFBConfigARB"))) ++ n_failed_functions;
  	//if(!(glXGetVisualFromFBConfigARB = (XVisualInfo*(GLApi*)(Display*, GLXFBConfig))glXGetProcAddressARB((const GLubyte*)"glXGetVisualFromFBConfigARB"))) ++ n_failed_functions;
#endif //_WIN32 || _WIN64

#endif //__GENERATE_WGL_ARB_create_context__

	return n_failed_functions;
}

#endif // !__HAVE_EGL

CGLESDriver::CGLESDriver()
	:m_b_status(false)
{
#if defined(__HAVE_EGL)
	m_t_display = EGL_NO_DISPLAY;
	m_t_surface = EGL_NO_SURFACE;
	m_t_context = EGL_NO_CONTEXT;
#elif defined(_WIN32) || defined(_WIN64)
	m_h_glrc = NULL;
	m_h_dc = NULL;
	m_h_wnd = NULL;
#else // __HAVE_EGL
	m_h_dc = NULL;
	m_h_glrc = NULL;
#endif // __HAVE_EGL
}

CGLESDriver::~CGLESDriver()
{
    if(m_b_status)
        Shutdown();
}

inline bool b_LooseMatch(int n_offered_quantity, int n_requested_quantity)
{
	return (n_requested_quantity != 0) == (n_offered_quantity != 0) &&
		n_requested_quantity <= n_offered_quantity;
}

inline bool b_LooseMatch2(int n_offered_quantity, int n_requested_quantity)
{
	return (n_requested_quantity != 0) == (n_offered_quantity != 0);
}

#if defined(_WIN32) || defined(_WIN64)
bool CGLESDriver::Init(HWND h_wnd, bool b_forward_compatible,
	int n_opengl_major, int n_opengl_minor, int n_width, int n_height, int n_bpp,
	int n_depth_bpp, int n_stencil_bpp, bool b_fullscreen)
#else //_WIN32 || _WIN64
bool CGLESDriver::Init(Window h_wnd, Display *h_display, bool b_forward_compatible,
	int n_opengl_major, int n_opengl_minor, int n_width, int n_height, int n_bpp,
	int n_depth_bpp, int n_stencil_bpp, bool b_fullscreen)
#endif //_WIN32 || _WIN64
{
	if(m_b_status)
		Shutdown();

#if defined(__HAVE_EGL)
#if defined(_WIN32) || defined(_WIN64)
	HDC h_display = GetDC(h_wnd);
#else // _WIN32 || _WIN64
	//Display *h_display = XOpenDisplay(NULL); // supplied as function argument
#endif // _WIN32 || _WIN64

	if((m_t_display = eglGetDisplay(h_display)) == EGL_NO_DISPLAY)
		return false;
	// create EGL display from system specific one

	EGLint n_major, n_minor;
	if(eglInitialize(m_t_display, &n_major, &n_minor) != EGL_TRUE)
		return false;
	// initialize EGL library

	_ASSERTE(n_bpp == 32 || n_bpp == 24 || n_bpp == 25 || n_bpp == 17 || n_bpp == 16 || n_bpp == 15); // code below would fail on different bit depths
	int n_alpha_size = (n_bpp == 32)? 8 : (n_bpp == 25 || n_bpp == 17)? 1 : 0;
	n_bpp -= n_alpha_size;
	_ASSERTE(n_bpp == 24 || n_bpp == 16 || n_bpp == 15); // make sure there are sane values
	int n_component_size = (n_bpp == 24)? 8 : 5;
	// calculate component sizes

	EGLint p_attributes[] = { // @note - do not change order here, it is used for comparison with returned configs
		EGL_RED_SIZE, n_component_size,
		EGL_GREEN_SIZE, n_bpp - 2 * n_component_size,
		EGL_BLUE_SIZE, n_component_size,
		EGL_ALPHA_SIZE, n_alpha_size,
		EGL_DEPTH_SIZE, n_depth_bpp,
		EGL_STENCIL_SIZE, n_stencil_bpp,
		//EGL_NATIVE_RENDERABLE, EGL_TRUE, // "native" doesn't mean hardware acceleration but the interoperability with windowing subsystem
		EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
		EGL_NONE
	};
	// config attribs

	EGLConfig t_my_config;
	{
#ifdef __GLES_DRIVER_CHOOSE_FRAMEBUFFER_CONFIG
		EGLint n_config_num = 0;
		if(eglChooseConfig(m_t_display, p_attributes, 0, 0, &n_config_num) != EGL_TRUE || n_config_num <= 0) {
#if defined(_WIN32) || defined(_WIN64)
			MessageBoxW(h_wnd, L"no EGL configurations were returned", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
			return false;
		}
		// get number of configs

#ifdef __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE
		printf("EGL config num: %d\n", n_config_num); // debug
#endif // __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE

		EGLConfig *p_my_config;
		if(!(p_my_config = new(std::nothrow) EGLConfig[n_config_num])) {
#if defined(_WIN32) || defined(_WIN64)
			MessageBoxW(h_wnd, L"not enough memory for EGL configurations", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
			return false;
		}
		// alloc configs

		EGLint n_config2_num = 0;
		if(eglChooseConfig(m_t_display, p_attributes, p_my_config,
		   n_config_num, &n_config2_num) != EGL_TRUE || n_config2_num <= 0) {
#if defined(_WIN32) || defined(_WIN64)
			MessageBoxW(h_wnd, L"no EGL configurations were returned (1)", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
			delete[] p_my_config;
			return false;
		}
		_ASSERTE(n_config2_num == n_config_num);
		n_config_num = n_config2_num;
		// get configs

		int n_my_config = 0; // default - the deepest
		for(int n_pass = 0;; ++ n_pass) {
			bool b_have_match = false;
			for(int i = 0; i < n_config_num; ++ i) {
				EGLint n_red_size, n_green_size, n_blue_size, n_alpha_size, n_depth_size, n_stencil_size;
				if(eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_RED_SIZE, &n_red_size) != EGL_TRUE ||
				   eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_GREEN_SIZE, &n_green_size) != EGL_TRUE ||
				   eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_BLUE_SIZE, &n_blue_size) != EGL_TRUE ||
				   eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_ALPHA_SIZE, &n_alpha_size) != EGL_TRUE ||
				   eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_DEPTH_SIZE, &n_depth_size) != EGL_TRUE ||
				   eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_STENCIL_SIZE, &n_stencil_size) != EGL_TRUE) {
#if defined(_WIN32) || defined(_WIN64)
					MessageBoxW(h_wnd, L"failed to query EGL configuration properties", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
					delete[] p_my_config;
					return false;
				}
				// query parameters

#ifdef __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE_SHOW_ALL
				if(!n_pass) {
					EGLint n_is_native, n_caveat/*, p_additional[7] = {0}*/;
					eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_NATIVE_RENDERABLE, &n_is_native);
					eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_CONFIG_CAVEAT, &n_caveat);

					/*eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_SAMPLE_BUFFERS, &p_additional[0]); // 0
					eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_SAMPLES, &p_additional[1]); // 0
					eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_SURFACE_TYPE, &p_additional[2]); // 0x0465 / 0x0467
					//eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_PRESERVED_RESOURCES, &p_additional[3]); // not supported in this EGL version
					eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_LEVEL, &p_additional[4]); // = 0
					eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_BUFFER_SIZE, &p_additional[5]); // = r+g+b+a (no tricks here)
					eglGetConfigAttrib(m_t_display, p_my_config[i], EGL_TRANSPARENT_TYPE, &p_additional[6]); // = EGL_NONE
					// ignore errors here*/

					printf("EGL config(id: %d, rgba: %d %d %d %d, depth: %d, stencil: %d, native: %s, caveat: %s)\n",
						i, n_red_size, n_green_size, n_blue_size, n_alpha_size, n_depth_size, n_stencil_size,
						(n_is_native == EGL_TRUE)? "yes" : "no", (n_caveat == EGL_NONE)? "no" :
						(n_caveat == EGL_SLOW_CONFIG)? "slow" : (n_caveat == EGL_NON_CONFORMANT_CONFIG)?
						"non-conformant" : "unknown");
					// the same configs are printed more than once, they only differ in config id
					// (you can uncomment the code blocks above and below to verify that)

					/*printf("\t(%d %d 0x%04x %d %d %d 0x%04x)\n", p_additional[0], p_additional[1],
						p_additional[2], p_additional[3], p_additional[4], p_additional[5], p_additional[6]);*/
				}
				// print all configs
#endif // __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE_SHOW_ALL

				if(n_pass == 0) {
					if(n_red_size == p_attributes[1] && n_green_size == p_attributes[3] &&
					   n_blue_size == p_attributes[5] && n_alpha_size == p_attributes[7] &&
					   n_depth_size == p_attributes[9] && n_stencil_size == p_attributes[11] /*&& n_is_native*/) {
						b_have_match = true;
						n_my_config = i;
						break;
					}
					// try to find an exact match in the first round
				} else if(n_pass == 1) {
					if(b_LooseMatch(n_red_size, p_attributes[1]) && b_LooseMatch(n_green_size, p_attributes[3]) &&
					   b_LooseMatch(n_blue_size, p_attributes[5]) && b_LooseMatch(n_alpha_size, p_attributes[7]) &&
					   b_LooseMatch(n_depth_size, p_attributes[9]) && b_LooseMatch(n_stencil_size, p_attributes[11]) /*&&
					   n_is_native*/) {
						b_have_match = true;
						n_my_config = i;
						break;
					}
					// try to find a loose match in the second round (loose match(a, b) is (a != 0) == (b != 0) && a <= b)
				} else if(n_pass == 2) {
					if(n_red_size >= p_attributes[1] && n_green_size >= p_attributes[3] &&
					   n_blue_size >= p_attributes[5] && n_alpha_size >= p_attributes[7] &&
					   n_depth_size >= p_attributes[9] && n_stencil_size >= p_attributes[11] /*&& n_is_native*/) {
						b_have_match = true;
						n_my_config = i;
						break;
					}
					// try to find any config with greater or equal bits per pixel
					// and hardware acceleration in the third round
				} else if(n_pass == 3) {
					if(b_LooseMatch2(n_red_size, p_attributes[1]) && b_LooseMatch2(n_green_size, p_attributes[3]) &&
					   b_LooseMatch2(n_blue_size, p_attributes[5]) && b_LooseMatch2(n_alpha_size, p_attributes[7]) &&
					   b_LooseMatch2(n_depth_size, p_attributes[9]) && b_LooseMatch2(n_stencil_size, p_attributes[11]) /*&&
					   n_is_native*/) {
						b_have_match = true;
						n_my_config = i;
						break;
					}
					// try to find any config with zero / nonzero bits per pixel
					// and hardware acceleration in the fourd round (loose match2(a, b) is (a != 0) == (b != 0))
				} else {
					b_have_match = true;
					break; // no more passes
				}
			}
			if(b_have_match)
				break;
		}
		t_my_config = p_my_config[n_my_config];
		delete[] p_my_config;
#else // __GLES_DRIVER_CHOOSE_FRAMEBUFFER_CONFIG
		EGLint n_config_num = 0;
		if(eglChooseConfig(m_t_display, p_attributes, &t_my_config,
		   1, &n_config_num) != EGL_TRUE || n_config_num <= 0) {
#if defined(_WIN32) || defined(_WIN64)
			MessageBoxW(h_wnd, L"no EGL configurations were returned (2)", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
			return false;
		}
		// get the first config

#ifdef __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE
		const int n_my_config = 0; // for verbose only
		eglChooseConfig(m_t_display, p_attributes, 0, 0, &n_config_num); // otherwise n_config_num is 1
		printf("EGL config num: %d\n", n_config_num); // debug
#endif // __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE
#endif // __GLES_DRIVER_CHOOSE_FRAMEBUFFER_CONFIG
		// choose the required configuration

#ifdef __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE
		{
			EGLint n_red_size, n_green_size, n_blue_size, n_alpha_size, n_depth_size, n_stencil_size, n_is_native, n_caveat;
			if(eglGetConfigAttrib(m_t_display, t_my_config, EGL_RED_SIZE, &n_red_size) != EGL_TRUE ||
			   eglGetConfigAttrib(m_t_display, t_my_config, EGL_GREEN_SIZE, &n_green_size) != EGL_TRUE ||
			   eglGetConfigAttrib(m_t_display, t_my_config, EGL_BLUE_SIZE, &n_blue_size) != EGL_TRUE ||
			   eglGetConfigAttrib(m_t_display, t_my_config, EGL_ALPHA_SIZE, &n_alpha_size) != EGL_TRUE ||
			   eglGetConfigAttrib(m_t_display, t_my_config, EGL_DEPTH_SIZE, &n_depth_size) != EGL_TRUE ||
			   eglGetConfigAttrib(m_t_display, t_my_config, EGL_STENCIL_SIZE, &n_stencil_size) != EGL_TRUE ||
			   eglGetConfigAttrib(m_t_display, t_my_config, EGL_NATIVE_RENDERABLE, &n_is_native) != EGL_TRUE ||
			   eglGetConfigAttrib(m_t_display, t_my_config, EGL_CONFIG_CAVEAT, &n_caveat) != EGL_TRUE) {
#if defined(_WIN32) || defined(_WIN64)
				MessageBoxW(h_wnd, L"failed to query EGL configuration properties", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
				return false;
			}
			// query parameters

			printf("EGL config(id: %d, rgba: %d %d %d %d, depth: %d, stencil: %d, native: %s, caveat: %s)\n",
				n_my_config, n_red_size, n_green_size, n_blue_size, n_alpha_size, n_depth_size, n_stencil_size,
				(n_is_native == EGL_TRUE)? "yes" : "no", (n_caveat == EGL_NONE)? "no" :
				(n_caveat == EGL_SLOW_CONFIG)? "slow" : (n_caveat == EGL_NON_CONFORMANT_CONFIG)?
				"non-conformant" : "unknown");
			// display the parameters
		}
		// verbose / debug
#endif // __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE
	}

	EGLNativeWindowType h_window = h_wnd;
	if((m_t_surface = eglCreateWindowSurface(m_t_display, t_my_config, h_window, NULL)) == EGL_NO_SURFACE) {
#if defined(_WIN32) || defined(_WIN64)
		MessageBoxW(h_wnd, L"failed to create EGL surface", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
		return false;
	}
	// create EGL window surface

	EGLint a_context_attributes[] = {
		EGL_CONTEXT_CLIENT_VERSION, (n_opengl_major)? n_opengl_major : 2,
		EGL_NONE
	};
	if((m_t_context = eglCreateContext(m_t_display, t_my_config,
	   EGL_NO_CONTEXT, a_context_attributes)) == EGL_NO_CONTEXT) {
#if defined(_WIN32) || defined(_WIN64)
		MessageBoxW(h_wnd, L"failed to create EGL context", L"EGL driver", MB_OK);
#endif // _WIN32 || _WIN64
		return false;
	}
	// create EGL context

	if(eglMakeCurrent(m_t_display, m_t_surface, m_t_surface, m_t_context) != EGL_TRUE)
		return false;
	// make current
#elif defined(_WIN32) || defined(_WIN64)
    if(b_fullscreen) {
	    DEVMODE t_device_mode;
        memset(&t_device_mode, 0, sizeof(t_device_mode));
        t_device_mode.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
        t_device_mode.dmSize = sizeof(DEVMODE);
        t_device_mode.dmPelsWidth = n_width;
        t_device_mode.dmPelsHeight = n_height;
        t_device_mode.dmBitsPerPel = n_bpp;
        // sets device mode for fullscreen mode

        if(ChangeDisplaySettings(&t_device_mode, 4) != DISP_CHANGE_SUCCESSFUL) {
            if(MessageBoxA(NULL, "Unable to set fullscreen mode.\n"
               "Use windowed mode instead?", "Fullscreen", MB_YESNO | MB_ICONEXCLAMATION) == IDYES)
                m_b_fullscreen = 0;
            else
                return false;
        }
        // tries to change the mode, allows fallback to windowed

        ShowCursor(false);
    }

    memset(&m_t_pixel_format, 0, sizeof(PIXELFORMATDESCRIPTOR));
    m_t_pixel_format.nSize = sizeof(PIXELFORMATDESCRIPTOR);
    m_t_pixel_format.nVersion = 1;
    m_t_pixel_format.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER | PFD_SWAP_EXCHANGE;
    m_t_pixel_format.dwLayerMask = PFD_MAIN_PLANE;
    m_t_pixel_format.iPixelType = PFD_TYPE_RGBA;
    m_t_pixel_format.cColorBits = n_bpp;
    m_t_pixel_format.cDepthBits = n_depth_bpp;
    m_t_pixel_format.cAccumBits = 0;
    m_t_pixel_format.cStencilBits = n_stencil_bpp;
    // sets pixel format descriptor

    if(!(m_h_dc = GetDC(m_h_wnd = h_wnd))) {
        MessageBoxA(NULL, "Unable to create a GL device context.", "OpenGL driver", MB_OK | MB_ICONERROR);
        return false;
    }
    if(!(m_n_pixel_format_id = ChoosePixelFormat(m_h_dc, &m_t_pixel_format))) {
        MessageBoxA(NULL, "Unable to find a suitable pixelformat.", "OpenGL driver", MB_OK | MB_ICONERROR);
        return false;
    }
    if(!SetPixelFormat(m_h_dc, m_n_pixel_format_id, &m_t_pixel_format)) {
        MessageBoxA(NULL, "Unable to set the pixelformat.", "OpenGL driver", MB_OK | MB_ICONERROR);
        return false;
    }

	HGLRC h_gl_rc;
	if(!(h_gl_rc = wglCreateContext(m_h_dc))) {
        MessageBoxA(NULL, "Unable to create a GL rendering context.", "OpenGL driver", MB_OK | MB_ICONERROR);
        return false;
	}
    if(!wglMakeCurrent(m_h_dc, h_gl_rc)) {
        MessageBoxA(NULL, "Unable to activate the GL rendering context.", "OpenGL driver", MB_OK | MB_ICONERROR);
        return false;
    }
	// create and activate dummy context

	if(b_forward_compatible) {
		if(!wglCreateContextAttribsARB && n_GetCreateContextARBFuncPointers()) {
			MessageBoxA(NULL, "OpenGL 3.0 not supported by window system\n"
				"(WGL_ARB_create_context not present).", "OpenGL driver", MB_OK | MB_ICONERROR);
			return false;
		}
		// needs wglCreateContextAttribsARB extension

		const int p_params[] = {
			WGL_CONTEXT_LAYER_PLANE_ARB, 0, // main plane
			WGL_CONTEXT_MAJOR_VERSION_ARB, n_opengl_major,
			WGL_CONTEXT_MINOR_VERSION_ARB, n_opengl_minor,
			WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, // want forward compatible context
			//WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, // want core profile // nvidia drivers have problem with this
			0
		};
		if(!(m_h_glrc = wglCreateContextAttribsARB(m_h_dc, 0, p_params))) {
			//MessageBoxA(NULL, "Unable to create OpenGL 3.0 rendering context.", "OpenGL driver", MB_OK | MB_ICONERROR);
			return false;
		}
		if(!wglMakeCurrent(m_h_dc, m_h_glrc)) {
			MessageBoxA(NULL, "Unable to activate the GL rendering context.", "OpenGL driver", MB_OK | MB_ICONERROR);
			return false;
		}
		// creates forward compatible OpenGL context

		wglDeleteContext(h_gl_rc);
		// deletes the dummy context
	} else
		m_h_glrc = h_gl_rc; // otherwise just use the dummy context

	glEnable(0x8642); // GL_PROGRAM_POINT_SIZE_EXT
	// enable point sprites (GLES doesn't have glPointSize())
#else // __HAVE_EGL
	n_GetCreateContextARBFuncPointers();
	/*if(!glXChooseFBConfigARB || glXGetVisualFromFBConfigARB)
		return false;*/
	// get x-window extension entry points: need glXChooseFBConfig and glXGetVisualFromFBConfig functions

	int n_default_bpc_color = 8;
	int n_default_bpc_alpha = 0;
	static int p_visual_attrib_list[] = {
        GLX_X_RENDERABLE, True,
        GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT,
        GLX_RENDER_TYPE, GLX_RGBA_BIT,
        GLX_X_VISUAL_TYPE, GLX_TRUE_COLOR,
        GLX_RED_SIZE, (n_bpp == 32)? 8 : (n_bpp == 16)? 5 : (n_bpp == 15)? 5 : n_default_bpc_color,
        GLX_GREEN_SIZE, (n_bpp == 32)? 8 : (n_bpp == 16)? 6 : (n_bpp == 15)? 5 : n_default_bpc_color,
        GLX_BLUE_SIZE, (n_bpp == 32)? 8 : (n_bpp == 16)? 5 : (n_bpp == 15)? 5 : n_default_bpc_color,
        GLX_ALPHA_SIZE, (n_bpp == 32)? 8 : (n_bpp == 16 || n_bpp == 15)? 0 : n_default_bpc_alpha,
        GLX_DEPTH_SIZE, n_depth_bpp,
        GLX_STENCIL_SIZE, n_stencil_bpp,
        GLX_DOUBLEBUFFER, True,
   		None
   	};
	// visual attributes

	m_h_dc = h_display;
	// get DC

	int n_fb_num = 0;
   	GLXFBConfig *p_fb_cfg = glXChooseFBConfigARB(h_display, DefaultScreen(h_display), p_visual_attrib_list, &n_fb_num);
    if(n_fb_num < 1 || !p_fb_cfg) {
		fprintf(stderr, "error: no suitable pixel format\n");
		return false;
	}
	m_t_pixel_format = p_fb_cfg[0];
	// choose suitable pixel format

	XVisualInfo *p_vi = glXGetVisualFromFBConfigARB(h_display, m_t_pixel_format);
	{
		Colormap t_colormap = XCreateColormap(h_display, RootWindow(h_display, p_vi->screen), p_vi->visual, AllocNone);
		XSetWindowAttributes t_swa;
	   	t_swa.colormap = t_colormap;
	    t_swa.border_pixel = 0;
		XChangeWindowAttributes(h_display, h_wnd, CWBorderPixel | CWColormap, &t_swa);
	}
	// change window colormap so OpenGL can be used with that window

	m_h_wnd = h_wnd;
	// remember window handle

	if(!(m_h_glrc = glXCreateContext(h_display, p_vi, 0, GL_TRUE))) {
		fprintf(stderr, "error: failed to create OpenGL rendering context\n");
		return false;
	}
	if(!glXMakeCurrent(h_display, h_wnd, m_h_glrc)) {
		fprintf(stderr, "error: failed to make OpenGL rendering context current\n");
		return false;
	}
	// create OpenGL context and bind it

	XFree(p_vi);
	// don't need this anymore

	if(b_forward_compatible) {
		if(!glXCreateContextAttribsARB && n_GetCreateContextARBFuncPointers()) {
			fprintf(stderr, "error: OpenGL 3.0 not supported by the X Window system\n");
			return false;
		}
		// needs glXCreateContextAttribsARB extension

		GLXContext h_old_glrc = m_h_glrc;
		// remember current (dummy) context

		const int p_params[] = {
			//WGL_CONTEXT_LAYER_PLANE_ARB, 0, // main plane // linux doesn't like this (gives GLX error)
			WGL_CONTEXT_MAJOR_VERSION_ARB, n_opengl_major,
			WGL_CONTEXT_MINOR_VERSION_ARB, n_opengl_minor,
			WGL_CONTEXT_FLAGS_ARB, WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, // want forward compatible context
			//WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_CORE_PROFILE_BIT_ARB, // want core profile // nvidia drivers have problem with this
			0
		};
		if(!(m_h_glrc = glXCreateContextAttribsARB(m_h_dc, m_t_pixel_format, 0, True, p_params))) {
			fprintf(stderr, "error: Unable to create OpenGL 3.0 rendering context.\n");
			return false;
		}
		glXMakeCurrent(m_h_dc, None, NULL);
		glXDestroyContext(m_h_dc, h_old_glrc);
		if(!glXMakeCurrent(m_h_dc, h_wnd, m_h_glrc)) {
			fprintf(stderr, "error: Unable to activate the GL rendering context.\n");
			return false;
		}
		// creates forward compatible OpenGL context
	}

	glEnable(0x8642); // GL_PROGRAM_POINT_SIZE_EXT
	// enable point sprites (GLES doesn't have glPointSize())
#endif // __HAVE_EGL

    glViewport(0, 0, n_width, n_height);
    // sets viewport

    m_n_width = n_width;
    m_n_height = n_height;
    m_b_status = true;
    m_b_fullscreen = b_fullscreen;

    return true;
}

bool CGLESDriver::Shutdown()
{
	if(!m_b_status)
		return true;

    bool b_result = true;

    m_b_status = false;

#if defined(__HAVE_EGL)
	if(m_t_display != EGL_NO_DISPLAY) {
		if(eglMakeCurrent(EGL_NO_DISPLAY, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT) != EGL_TRUE && false) { // ignore errors here
			fprintf(stderr, "error: Release of DC and RC failed\n");
			b_result = false;
		}
		if(m_t_context != EGL_NO_CONTEXT) {
			eglDestroyContext(m_t_display, m_t_context);
			m_t_context = EGL_NO_CONTEXT;
		}
		if(m_t_surface != EGL_NO_SURFACE) {
			eglDestroySurface(m_t_display, m_t_surface);
			m_t_surface = EGL_NO_SURFACE;
		}
		eglTerminate(m_t_display);
		m_t_display = EGL_NO_DISPLAY;
	}
	// ignores some of errors here
#elif defined(_WIN32) || defined(_WIN64)
    if(m_b_fullscreen) {
        ChangeDisplaySettings(NULL, 0);
        ShowCursor(true);
    }
	// returns from fullscreen mode

    if(m_h_glrc) {
        if(!wglMakeCurrent(NULL, NULL)) {
            MessageBoxA(NULL, "Release of DC and RC failed.", "OpenGL driver", MB_OK | MB_ICONERROR);
            b_result = false;
        }
        if(!wglDeleteContext(m_h_glrc)) {
            MessageBoxA(NULL, "Release rendering context failed.", "OpenGL driver", MB_OK | MB_ICONERROR);
            m_h_glrc = NULL;
            b_result = false;
        }
    }

    if(m_h_dc && !ReleaseDC(m_h_wnd, m_h_dc)) {
		MessageBoxA(NULL, "Release device context failed.", "OpenGL driver", MB_OK | MB_ICONERROR);
        m_h_dc = NULL;
        b_result = false;
    }
#else // __HAVE_EGL
	if(m_h_dc) {
		if(!glXMakeCurrent(m_h_dc, None, NULL)) {
			fprintf(stderr, "error: Release of DC and RC failed\n");
			b_result = false;
		}
		if(m_h_glrc) {
			glXDestroyContext(m_h_dc, m_h_glrc);
			m_h_glrc = NULL;
		}
		XCloseDisplay(m_h_dc);
		m_h_dc = NULL;
	}
#endif // __HAVE_EGL

    return b_result;
}

bool CGLESDriver::b_Status() const
{
	return m_b_status;
}

bool CGLESDriver::MakeCurrent()
{
#if defined(__HAVE_EGL)
	if(eglMakeCurrent(m_t_display, m_t_surface, m_t_surface, m_t_context) != EGL_TRUE)
		return false;
#elif defined(_WIN32) || defined(_WIN64)
    if(!wglMakeCurrent(m_h_dc, m_h_glrc))
        return false;
#else // __HAVE_EGL
	if(!glXMakeCurrent(m_h_dc, m_h_wnd, m_h_glrc))
        return false;
#endif // __HAVE_EGL
	return true;
}

void CGLESDriver::ResetViewport() const
{
    glViewport(0, 0, m_n_width, m_n_height);
}

void CGLESDriver::SwapBuffers() const
{
#if defined(__HAVE_EGL)
	eglSwapBuffers(m_t_display, m_t_surface);
#elif defined(_WIN32) || defined(_WIN64)
	::SwapBuffers(m_h_dc);
#else // __HAVE_EGL
	glXSwapBuffers(m_h_dc, m_h_wnd);
#endif // __HAVE_EGL
}

/*
 *								=== ~CGLESDriver ===
 */

