#ifndef _OSGVIEWER_WIN32GWUTILS_H_
#define _OSGVIEWER_WIN32GWUTILS_H_

#if defined(OSG_USE_EGL)
#include <EGL/egl.h>
#define EGL_EGLEXT_PROTOTYPES
#include <EGL/eglext.h>
#endif

// Fallback if not correctly detected in CMake macro
#ifndef _WIN32_WINNT
//#define _WIN32_WINNT    0x0A00 // Windows 10
//#define _WIN32_WINNT    0x0603 // Windows 8.1
//#define _WIN32_WINNT    0x0602 // Windows 8
//#define _WIN32_WINNT    0x0601 // Windows 7
//#define _WIN32_WINNT    0x0600 // Windows Server 2008
//#define _WIN32_WINNT    0x0600 // Windows Vista
//#define _WIN32_WINNT    0x0502 // Windows Server 2003 with SP1, Windows XP with SP2
//#define _WIN32_WINNT    0x0501 // Windows Server 2003, Windows XP
#define _WIN32_WINNT    0x0500 // Windows NT
#endif
#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include <osg/GraphicsContext>

namespace osgViewer {
		
	template <typename T> class XGLAttributes
	{
	public:

		XGLAttributes() {}
		~XGLAttributes() {}

		void begin() { m_parameters.clear(); }
		void set(const T& id, const T& value) { add(id); add(value); }
		void enable(const T& id) { add(id); add(true); }
		void disable(const T& id) { add(id); add(false); }
		#if defined(OSG_USE_EGL)
		void end() { add(EGL_NONE); }
		#else
		void end() { add(0); }
		#endif

		const T* get() const { return &m_parameters.front(); }

	protected:

		void add(const T& t) { m_parameters.push_back(t); }

		std::vector<T>    m_parameters;        // parameters added

	private:

		// No implementation for these
		XGLAttributes(const XGLAttributes&);
		XGLAttributes& operator=(const XGLAttributes&);
	};
	
	typedef XGLAttributes<int>   XGLIntegerAttributes;
	typedef XGLAttributes<float> XGLFloatAttributes;

	void reportError(const std::string& msg);
	void reportError(const std::string& msg, unsigned int errorCode);
	void reportErrorForScreen(const std::string& msg, const osg::GraphicsContext::ScreenIdentifier& si, unsigned int errorCode);


	#if defined(OSG_USE_EGL)
	namespace EGL {
		struct ContextInfo {
			ContextInfo();
			ContextInfo(EGLContext _eglContext, EGLDisplay _eglDisplay, EGLSurface _eglSurface);
			ContextInfo(const ContextInfo& o);
			ContextInfo& operator=(const ContextInfo& o);
			void clear();
			bool isEmpty();
			EGLContext  eglContext;
			EGLDisplay  eglDisplay;
			EGLSurface  eglSurface;
		};

		// A class representing an OpenGL rendering context 
		class OpenGLContext
		{
		public:

			OpenGLContext();
			OpenGLContext(HWND hwnd, HDC hdc, EGLContext eglContext, EGLDisplay eglDisplay, EGLSurface eglSurface);

			~OpenGLContext();

			void set(HWND hwnd, HDC hdc, EGLContext eglContext, EGLDisplay eglDisplay, EGLSurface eglSurface, EGLConfig eglConfig);
			void clear();

			HDC deviceContext();
			EGLConfig getConfig();

			bool makeCurrent(HDC restoreOnHdc, bool restorePreviousOnExit);
			ContextInfo& contextInfo();

		protected:

			EGL::ContextInfo _eglCtx;
			EGLConfig _eglConfig;

			HDC   _previousHdc;             // previously HDC to restore rendering context on
			EGLContext _previousContext;           // previously current rendering context

			HWND  _hwnd;                    // handle to OpenGL window
			HDC   _hdc;                     // handle to device context

		private:

			// no implementation for these
			OpenGLContext(const OpenGLContext&);
			OpenGLContext& operator=(const OpenGLContext&);
		};

		bool createDisplaySurfaceAndContext(ContextInfo& context, EGLConfig& config, XGLAttributes<int>& configAttribs, HWND hwnd, HDC hdc);
		bool createDisplaySurfaceAndContextForPBuffer(ContextInfo& context, EGLConfig& config, XGLAttributes<int>& configAttribs);
		EGLContext createContext(EGLDisplay eglDisplay, const EGLConfig& config);
		void destroyContext(ContextInfo& c);
		void preparePixelFormatSpecifications(const osg::GraphicsContext::Traits& traits, XGLIntegerAttributes& attributes, bool allowSwapExchangeARB);
	} // end of namespace EGL

	#else

	namespace WGL {
		class OpenGLContext
		{
		public:

			OpenGLContext();
			OpenGLContext(HWND hwnd, HDC hdc, HGLRC hglrc);

			~OpenGLContext();

			void set(HWND hwnd, HDC hdc, HGLRC hglrc);

			void clear();

			HDC deviceContext();

			bool makeCurrent(HDC restoreOnHdc, bool restorePreviousOnExit);

		protected:

			//
			// Data members
			//

			HDC   _previousHdc;             // previously HDC to restore rendering context on
			HGLRC _previousHglrc;           // previously current rendering context
			HWND  _hwnd;                    // handle to OpenGL window
			HDC   _hdc;                     // handle to device context
			HGLRC _hglrc;                   // handle to OpenGL rendering context
			bool  _restorePreviousOnExit;   // restore original context on exit

		private:

			// no implementation for these
			OpenGLContext(const OpenGLContext&);
			OpenGLContext& operator=(const OpenGLContext&);
		};
	} // end of namespace WGL
	#endif
}

#if !defined(OSG_USE_EGL)
//
// Defines from the WGL_ARB_pixel_format specification document
// See http://www.opengl.org/registry/specs/ARB/wgl_pixel_format.txt
//
#ifndef WGL_ARB_pixel_format
#define WGL_ARB_pixel_format 1
#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_SWAP_EXCHANGE_ARB               0x2028
#define WGL_SWAP_COPY_ARB                   0x2029
#define WGL_SWAP_UNDEFINED_ARB              0x202A
#define WGL_TYPE_RGBA_ARB                   0x202B
#define WGL_TYPE_COLORINDEX_ARB             0x202C
#define WGL_SAMPLE_BUFFERS_ARB                0x2041
#define WGL_SAMPLES_ARB                        0x2042
#endif

#ifndef WGL_ARB_render_texture
#define WGL_ARB_render_texture 1
#define WGL_BIND_TO_TEXTURE_RGB_ARB         0x2070
#define WGL_BIND_TO_TEXTURE_RGBA_ARB        0x2071
#define WGL_TEXTURE_FORMAT_ARB              0x2072
#define WGL_TEXTURE_TARGET_ARB              0x2073
#define WGL_MIPMAP_TEXTURE_ARB              0x2074
#define WGL_TEXTURE_RGB_ARB                 0x2075
#define WGL_TEXTURE_RGBA_ARB                0x2076
#define WGL_NO_TEXTURE_ARB                  0x2077
#define WGL_TEXTURE_CUBE_MAP_ARB            0x2078
#define WGL_TEXTURE_1D_ARB                  0x2079
#define WGL_TEXTURE_2D_ARB                  0x207A
#define WGL_MIPMAP_LEVEL_ARB                0x207B
#define WGL_CUBE_MAP_FACE_ARB               0x207C
#define WGL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB 0x207D
#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB 0x207E
#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB 0x207F
#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB 0x2080
#define WGL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB 0x2081
#define WGL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB 0x2082
#define WGL_FRONT_LEFT_ARB                  0x2083
#define WGL_FRONT_RIGHT_ARB                 0x2084
#define WGL_BACK_LEFT_ARB                   0x2085
#define WGL_BACK_RIGHT_ARB                  0x2086
#define WGL_AUX0_ARB                        0x2087
#define WGL_AUX1_ARB                        0x2088
#define WGL_AUX2_ARB                        0x2089
#define WGL_AUX3_ARB                        0x208A
#define WGL_AUX4_ARB                        0x208B
#define WGL_AUX5_ARB                        0x208C
#define WGL_AUX6_ARB                        0x208D
#define WGL_AUX7_ARB                        0x208E
#define WGL_AUX8_ARB                        0x208F
#define WGL_AUX9_ARB                        0x2090
#endif

#ifndef WGL_NV_render_depth_texture
#define WGL_NV_render_depth_texture 1
#define WGL_BIND_TO_TEXTURE_DEPTH_NV   0x20A3
#define WGL_BIND_TO_TEXTURE_RECTANGLE_DEPTH_NV 0x20A4
#define WGL_DEPTH_TEXTURE_FORMAT_NV    0x20A5
#define WGL_TEXTURE_DEPTH_COMPONENT_NV 0x20A6
#define WGL_DEPTH_COMPONENT_NV         0x20A7
#endif

#ifndef WGL_NV_render_texture_rectangle
#define WGL_NV_render_texture_rectangle 1
#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGB_NV 0x20A0
#define WGL_BIND_TO_TEXTURE_RECTANGLE_RGBA_NV 0x20A1
#define WGL_TEXTURE_RECTANGLE_NV       0x20A2
#endif

#ifndef WGL_SAMPLE_BUFFERS_ARB
#define        WGL_SAMPLE_BUFFERS_ARB         0x2041
#endif
#ifndef WGL_SAMPLES_ARB
#define        WGL_SAMPLES_ARB                0x2042
#endif

#ifndef WGL_ARB_create_context
#define WGL_CONTEXT_DEBUG_BIT_ARB      0x00000001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB 0x00000002
#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 ERROR_INVALID_VERSION_ARB      0x2095
#endif

#ifndef WGL_ARB_pbuffer
#define WGL_ARB_pbuffer 1
DECLARE_HANDLE(HPBUFFERARB);
#define WGL_DRAW_TO_PBUFFER_ARB             0x202D
#define WGL_MAX_PBUFFER_PIXELS_ARB          0x202E
#define WGL_MAX_PBUFFER_WIDTH_ARB           0x202F
#define WGL_MAX_PBUFFER_HEIGHT_ARB          0x2030
#define WGL_PBUFFER_LARGEST_ARB             0x2033
#define WGL_PBUFFER_WIDTH_ARB               0x2034
#define WGL_PBUFFER_HEIGHT_ARB              0x2035
#define WGL_PBUFFER_LOST_ARB                0x2036
#endif

#ifndef WGL_ARB_create_context
#define WGL_ARB_create_context 1
#ifdef WGL_WGLEXT_PROTOTYPES
extern HGLRC WINAPI wglCreateContextAttribsARB(HDC, HGLRC, const int *);
#endif /* WGL_WGLEXT_PROTOTYPES */
typedef HGLRC(WINAPI * PFNWGLCREATECONTEXTATTRIBSARBPROC) (HDC hDC, HGLRC hShareContext, const int *attribList);
#endif

//
// Entry points used from the WGL extensions
//
//    BOOL wglChoosePixelFormatARB(HDC hdc,
//                                 const int *piAttribIList,
//                                 const FLOAT *pfAttribFList,
//                                 UINT nMaxFormats,
//                                 int *piFormats,
//                                 UINT *nNumFormats);
//

typedef bool (WINAPI * WGLChoosePixelFormatARB) (HDC, const int *, const float *, unsigned int, int *, unsigned int *);
#endif 

#endif