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

#pragma once
#ifndef __GLES_DRIVER_INCLUDED
#define __GLES_DRIVER_INCLUDED

/**
 *	@file gles2/OpenGLESDrv.h
 *	@author -tHE SWINe-
 *	@date 2012
 *	@brief OpenGL ES 2.0 driver
 *
 *	@date 2012-04-03
 *
 *	Fixed problem where point sprites were not rendered correctly when
 *	emulating OpenGLES on full OpenGL graphics card.
 *
 *	@date 2012-06-19
 *
 *	Moved multiple inclusion guard before file documentation comment.
 *
 */

/**
 *	@def __GLES_DRIVER_CHOOSE_FRAMEBUFFER_CONFIG
 *	@brief if defined, all EGL configs are queried for their parameters and
 *		the best match is chosen (otherwise the first one (the deepest one) is chosen)
 */
#define __GLES_DRIVER_CHOOSE_FRAMEBUFFER_CONFIG

/**
 *	@def __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE
 *	@brief if defined, CGLESDriver::Init() prints number of framebuffer configs
 *		and parameters of the chosen one
 */
#define __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE

/**
 *	@def __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE_SHOW_ALL
 *	@brief if defined, CGLESDriver::Init() prints parameters of all the framebuffer configs
 */
//#define __GLES_DRIVER_FRAMEBUFFER_CONFIG_VERBOSE_SHOW_ALL

#ifdef __HAVE_GLES
#include "GLES20.h"
#else // __HAVE_GLES
#include "GLES20Emu.h"
#endif // __HAVE_GLES

#if defined(__HAVE_EGL)
#include <EGL/egl.h>
#elif defined(_WIN32) || defined(_WIN64)
#define NOMINMAX
#include <windows.h>
#else // __HAVE_EGL
#include <GL/glx.h>
#include <X11/Xlib.h>
#endif // __HAVE_EGL

/**
 *	@brief OpenGL ES driver
 */
class CGLESDriver {
public:
#if defined(__HAVE_EGL)
	typedef EGLConfig TNativePixelFormat;
#elif defined(_WIN32) || defined(_WIN64)
	typedef PIXELFORMATDESCRIPTOR TNativePixelFormat;
#else // __HAVE_EGL
	typedef GLXFBConfig TNativePixelFormat;
#endif // __HAVE_EGL

private:
#if defined(__HAVE_EGL)
	EGLDisplay m_t_display;
	EGLSurface m_t_surface;
	EGLContext m_t_context;
#elif defined(_WIN32) || defined(_WIN64)
	HDC m_h_dc;
	HGLRC m_h_glrc;
	HWND m_h_wnd;
#else // __HAVE_EGL
	Display *m_h_dc;
	GLXContext m_h_glrc;
	Window m_h_wnd;
#endif // __HAVE_EGL

	bool m_b_fullscreen;

	int m_n_width;
	int m_n_height;

	bool m_b_status;

	TNativePixelFormat m_t_pixel_format;
	GLuint m_n_pixel_format_id;

public:
	/**
	 *	@brief default constructor
	 *
	 *	Default constructor; has no effect.
	 */
	CGLESDriver();

	/**
	 *	@brief destructor
	 *
	 *	Takes care of OpenGL shutdown in case Shutdown() wasn't called.
	 */
	~CGLESDriver();

	/**
	 *	@brief initializes OpenGL
	 *
	 *	Initializes OpenGL, enables creating forward-compatible OpenGL contexts.
	 *		Regular OpenGL context is created with b_forward_compatible set to false
	 *		and both n_opengl_major and n_opengl_minor set to -1. That results in
	 *		backward-compatible context, running newest implemented version of OpenGL.
	 *		Forward-compatible context is created with b_forward_compatible set to true
	 *		and n_opengl_major, n_opengl_minor set to required OpenGL version
	 *		(3.0, 3.1 or 3.2 are available to date)
	 *
	 *	@param[in] h_wnd is handle of window to init OpenGL in
	 *	@param[in] b_forward_compatible requests forward compatible OpenGL mode
	 *		(in which case, n_opengl_major and n_opengl_minor contains required version).
	 *	@param[in] n_opengl_major is required OpenGL major version (only valid if
	 *		b_forward_compatible is set)
	 *	@param[in] n_opengl_minor is required OpenGL minor version (only valid if
	 *		b_forward_compatible is set)
	 *	@param[in] n_width is viewport width in pixels
	 *	@param[in] n_height is viewport height in pixels
	 *	@param[in] n_bpp is number of bits per pixel
	 *	@param[in] n_depth_bpp is number of depth bits per pixel
	 *	@param[in] n_stencil_bpp is number of stencil bits per pixel
	 *	@param[in] b_fullscreen enables switching to fullscreen mode if set,
	 *		otherwise leaves screen mode intact (this is mostly ignored)
	 *
	 *	@return Returns true on success, false on failure.
	 *
	 *	@note In case OpenGL is already initialized, it is first shut down,
	 *		causing all OpenGL objects being effectively deleted.
	 */
#if defined(_WIN32) || defined(_WIN64)
	bool 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 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

	/**
	 *	@brief shuts OpenGL down
	 *
	 *	@return Returns true on success, false on failure.
	 *
	 *	@note This always succeeds in case OpenGL was not initialized.
	 */
	bool Shutdown();

	/**
	 *	@brief returns OpenGL status
	 *
	 *	@return Returns true if OpenGL was successfuly initialized, otherwise returns false.
	 */
	bool b_Status() const;

	/**
	 *	@brief makes context, associated with this OpenGL driver, current
	 *
	 *	@return Returns true on success, false on failure.
	 *
	 *	@note This doesn't explicitly handle case where OpenGL was not initialized.
	 */
	bool MakeCurrent();

	/**
	 *	@brief gets viewport width
	 *
	 *	@return Returns width, in pixels, as passed to Init().
	 *
	 *	@note This value may have changed, in case OpenGL window was resized.
	 */
	inline int n_Width() const { return m_n_width; }

	/**
	 *	@brief gets viewport height
	 *
	 *	@return Returns height, in pixels,
	 *
	 *	@note This value may have changed, in case OpenGL window was resized.
	 */
	inline int n_Height() const { return m_n_height; }

	/**
	 *	@brief gets PIXELFORMATDESCRIPTOR
	 *
	 *	@return Returns current pixel format descriptor. Return value is undefined
	 *		in case Init() wasn't called / did not succeed.
	 */
	inline const TNativePixelFormat &t_PixelFormat() const { return m_t_pixel_format; }

	/**
	 *	@brief gets pixel format index
	 *
	 *	@return Returns current pixel format index. Return value is undefined
	 *		in case Init() wasn't called / did not succeed.
	 */
	inline GLuint n_PixelFormat() const { return m_n_pixel_format_id; }

	/**
	 *	@brief resets viewport
	 *
	 *	Calls glViewport with width / height values, as passed to Init().
	 *
	 *	@note This doesn't explicitly handle case where OpenGL was not initialized.
	 */
	void ResetViewport() const;

	/**
	 *	@brief exchanges front and back buffer
	 *
	 *	Exchanges front and back buffer, implicates back buffer contains undefined data
	 *		after the swap (most likely contains one of previous frames), reading it back
	 *		may give unexpected results. Justification for this limitation is no need
	 *		to physically copy contents of the buffers, resulting in greater speed.
	 *
	 *	@note This doesn't explicitly handle case where OpenGL was not initialized.
	 */
	void SwapBuffers() const;

	/**
	 *	@copydoc SwapBuffers
	 *	@note This is deprecated and kept for compatibility reasons only; use SwapBuffers() instead.
	 */
	inline void Blit() const
	{
		SwapBuffers();
	}

private:
#if !defined(__HAVE_EGL)
	static int n_GetCreateContextARBFuncPointers();
#endif // !__HAVE_EGL
};

#endif // !__GLES_DRIVER_INCLUDED
