/*
								+--------------------------------+
								|                                |
								|  ***  Mini OpenGL driver  ***  |
								|                                |
								|  Copyright © -tHE SWINe- 2009  |
								|                                |
								|           MiniGL.cpp           |
								|                                |
								+--------------------------------+
*/

/**
 *	@file gl2/MiniGL.cpp
 *	@author -tHE SWINe-
 *	@date 2009
 *	@brief minimal OpenGL driver
 *
 *	@date 2009-10-08
 *
 *	added MINIGL_FORCE_GLUT, renamed GPU_LIB_WIN32_SHOW_WINDOW to MINIGL_SHOW_WINDOW
 *
 *	wrapped global functions as static members to CMiniGL class
 *
 */

#include "../NewFix.h"
#include "../CallStack.h"
#include "OpenGL20.h"
#include "OpenGLState.h"
#include "Texture.h"
#include "Shader2.h"
#include "RenderBuffer2.h"
#include "VertexBufferObject.h"
#include "../MinMax.h"
#include "MiniGL.h"
#ifdef MINIGL_SHOW_WINDOW
#include "OpenGL20Logo.h"
#endif // MINIGL_SHOW_WINDOW

static bool b_pixel_buffer_supported = false;
static bool b_fence_supported = false;
static bool b_fbo_supported = false;
static bool b_imaging_supported = false;
static bool b_gl_images_supported = false;
static bool b_shaders_supported = false;
// opengl capabilities

#if (defined(_WIN32) || defined(_WIN64)) && !defined(MINIGL_FORCE_GLUT)
#define MINIGL_USE_WGL
#else
#define MINIGL_USE_GLUT
#endif
// decide between use of glut / windows opengl

/*
 *								--- win32 OpenGL ---
 */

static const char *p_s_OpenGL_window_name = "ÜberLame OpenGL";
// window name

#ifdef MINIGL_USE_WGL

#include "OpenGLDrv.h"

static HWND h_wnd;
static CGLDriver *p_gl_driver = 0;

static const char *p_s_OpenGL_window_classname = "UberLameGLWnd";
// wndclass name

/*
 *	bool Create_Window(const char *p_s_name, int n_width, int n_height)
 *		- creates window with given name and size
 *		- returns true on success, false on failure
 */
bool Create_Window(const char *p_s_name, int n_width, int n_height)
{
	WNDCLASSEX t_wnd_class;
	t_wnd_class.cbSize = sizeof(WNDCLASSEX);
	t_wnd_class.style = CS_HREDRAW | CS_VREDRAW;
	t_wnd_class.lpfnWndProc = DefWindowProc;
	t_wnd_class.cbClsExtra = 0;
	t_wnd_class.cbWndExtra = 0;
	t_wnd_class.hInstance = GetModuleHandle(NULL);
	t_wnd_class.hIcon = LoadIcon(GetModuleHandle(NULL), IDI_APPLICATION);
	t_wnd_class.hCursor = LoadCursor(GetModuleHandle(NULL), IDC_ARROW);
	t_wnd_class.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	t_wnd_class.lpszMenuName = 0;
	t_wnd_class.lpszClassName = p_s_OpenGL_window_classname;
	t_wnd_class.hIconSm = LoadIcon(GetModuleHandle(NULL), IDI_APPLICATION);
	if(!RegisterClassEx(&t_wnd_class))
		return false;
	// create window class

	RECT t_rect;
	t_rect.left = t_rect.top = 0;
	t_rect.right = n_width;
	t_rect.bottom = n_height;
	if(!AdjustWindowRectEx(&t_rect, WS_BORDER | WS_CAPTION, false, WS_EX_TOOLWINDOW))
		return false;
	// calculate window size to contain client area with desired resolution

	if(!(h_wnd = CreateWindowEx(WS_EX_TOOLWINDOW, p_s_OpenGL_window_classname, p_s_name,
	   WS_BORDER | WS_CAPTION, CW_USEDEFAULT, CW_USEDEFAULT,
	   t_rect.right - t_rect.left, t_rect.bottom - t_rect.top,
	   NULL, NULL, GetModuleHandle(NULL), NULL)))
		return false;
	// create window

#ifdef MINIGL_SHOW_WINDOW
	ShowWindow(h_wnd, SW_SHOW);
#else // MINIGL_SHOW_WINDOW
	ShowWindow(h_wnd, SW_HIDE);
#endif // MINIGL_SHOW_WINDOW
	UpdateWindow(h_wnd);
	// show & update window

	return true;
}

#else // MINIGL_USE_WGL

#include <GL/glut.h>

static int n_glut_window = 0;
static bool b_glut_running = false;

#endif // MINIGL_USE_WGL

/*
 *								--- ~win32 OpenGL ---
 */

/*
 *								--- CMiniGL ---
 */

CGLState *CMiniGL::m_p_state = 0;
// opengl state guard

/*
 *	static bool CMiniGL::Init()
 *		- initializes OpenGL. under linux uses GLUT, under windows uses
 *		  WGL system (unless MINIGL_FORCE_GLUT defined)
 *		- there's option to display window, containing "OpenGL 2.0"
 *		  logo (enabled using MINIGL_SHOW_WINDOW macro)
 *		- returns true on success (or multiple initialization), false on failure
 *		- note there are no parameters with framebuffer requirements,
 *		  one should not have any assumptions about the framebuffer and
 *		  always use offscreen buffers (P-buffers / FBO's)
 */
bool CMiniGL::Init()
{
	if(m_p_state)
		return true;

#ifdef MINIGL_USE_WGL
	if(!Create_Window(p_s_OpenGL_window_name, 150, 57))
		return false;
	// create window

	if(!(p_gl_driver = new(std::nothrow) CGLDriver()))
		return false;
	if(!p_gl_driver->Init(h_wnd, 150, 57, 24, 0)) {
		delete p_gl_driver;
		p_gl_driver = 0;
		return false;
	}
	// init open-gl
#else // MINIGL_USE_WGL
	if(!b_glut_running) {
		int n_arg_num = 1;
		char p_s_arg[] = "no-arg", *p_arg_list[] = {p_s_arg}; // not const!
		glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);
		glutInitWindowSize(150, 57);
		glutInit(&n_arg_num, (char**)p_arg_list);
		b_glut_running = true;
		// initialize glut
	}
	// can't shutdown glut

    n_glut_window = glutCreateWindow(p_s_OpenGL_window_name);
	// create glut window
#endif // MINIGL_USE_WGL
	// create window, init open-gl

#ifdef GL_STATE_SINGLE_CONTEXT_ONLY
	if(!(m_p_state = new(std::nothrow) CGLState())) {
#else // GL_STATE_SINGLE_CONTEXT_ONLY
#ifdef MINIGL_USE_WGL
	if(!(m_p_state = new(std::nothrow) CGLState(wglGetCurrentDC(), wglGetCurrentContext()))) {
#else // MINIGL_USE_WGL
	if(!(m_p_state = new(std::nothrow) CGLState(NULL, NULL))) {
#endif // MINIGL_USE_WGL
#endif // GL_STATE_SINGLE_CONTEXT_ONLY
#ifdef MINIGL_USE_WGL
		delete p_gl_driver;
		p_gl_driver = 0;
#endif // MINIGL_USE_WGL
		return false;
	}
	// create state guard

	b_fbo_supported = CGLFrameBuffer_FBO::b_Supported(); // gets function pointers as well
	CGLExtensionHandler::n_GetGL12FuncPointers();
	CGLExtensionHandler::n_GetGL13FuncPointers();
	CGLExtensionHandler::n_GetMultitextureFuncPointers(); // opengl1.3 as well
	CGLExtensionHandler::n_GetGL14FuncPointers();
	CGLExtensionHandler::n_GetGL15FuncPointers();
	CGLExtensionHandler::n_GetGL20FuncPointers();
	b_pixel_buffer_supported = CGLPixelBufferObject::b_Supported(); // gets function pointers as well
	b_gl_images_supported = CGLPixelBufferObject::b_Supported();
	b_fence_supported = CGLExtensionHandler::b_SupportedExtension("GL_NV_fence") &&
		!CGLExtensionHandler::n_GetFenceNVFuncPointers();
	b_imaging_supported = CGLExtensionHandler::b_SupportedExtension("GL_ARB_imaging");
	b_shaders_supported = CGL_ARB_shader::b_Supported() || CGL_Core_shader::b_Supported();
	// get function pointers

#ifdef MINIGL_SHOW_WINDOW
	unsigned __int32 *p_gl_logo;
	if((p_gl_logo = COpenGL20Logo::p_Decompress())) {
		/*glDrawPixels(n_opengl_logo_2_0_sm_jpg_width, n_opengl_logo_2_0_sm_jpg_height,
			GL_BGRA, GL_UNSIGNED_BYTE, p_gl_logo);*/ // upside-down
		CGLTexture_2D opengl_tex(m_p_state, COpenGL20Logo::n_Width(),
			COpenGL20Logo::n_Height(), GL_RGBA8, false, 0,
			GL_BGRA, GL_UNSIGNED_BYTE, p_gl_logo);
		delete[] p_gl_logo;

		m_p_state->DisableDepthTest();
		m_p_state->DisableCullFace();
		m_p_state->Color3f(1, 1, 1);

		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();

		opengl_tex.Bind_Enable(m_p_state);
		const float p_vertices[] = {
			0, 1, -1, -1, 0,
			0, 0, -1, +1, 0,
			1, 0, +1, +1, 0,
			1, 1, +1, -1, 0
		};
		glInterleavedArrays(GL_T2F_V3F, 0, p_vertices);
		glDrawArrays(GL_QUADS, 0, 4);
		glDisableClientState(GL_VERTEX_ARRAY);
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		// use VA, rather than glBegin

		//m_p_state->BindTexture2D(0); // destroying texture with logo

#ifdef MINIGL_USE_WGL
		p_gl_driver->Blit();
#else // MINIGL_USE_WGL
		glutSwapBuffers();
#endif // MINIGL_USE_WGL
	}
	// load OpenGL logo
#endif // MINIGL_SHOW_WINDOW

	return true;
}

/*
 *	static bool CMiniGL::Shutdown();
 *		- shuts OpenGL down, previously initialized by Init()
 *		- returns true on success, false on failure
 *		- note there's no function to shut down GLUT, this is expected
 *		  to be called just before exitting as it has no effect
 *		  (but repeating Init() - Shutdown() multiple times is still possible)
 */
bool CMiniGL::Shutdown()
{
	if(!m_p_state)
		return true;

	bool b_result = true;

	delete m_p_state;
	m_p_state = 0;

#ifdef MINIGL_USE_WGL
	b_result &= p_gl_driver->Shutdown();
	delete p_gl_driver;
	// shutdown open-gl

	UnregisterClass(p_s_OpenGL_window_classname, GetModuleHandle(NULL));
	DestroyWindow(h_wnd);
	// destroy window
#else // MINIGL_USE_WGL
	glutDestroyWindow(n_glut_window);
	n_glut_window = 0;
	// destroy glut window

	// no function to shutdown glut, it is still running here ... but no OpenGL context exists
#endif // MINIGL_USE_WGL

	return b_result;
}

/*
 *								--- ~CMiniGL ---
 */
