/*
								+---------------------------------+
								|                                 |
								|      ***   OpenGlDrv   ***      |
								|                                 |
								|  Copyright  © -tHE SWINe- 2002  |
								|                                 |
								|          OpenGlDrv.cpp          |
								|                                 |
								+---------------------------------+
*/

/**
 *	@file gl2/OpenGLDrv.cpp
 *	@author -tHE SWINe-
 *	@date 2002
 *	@brief simple OpenGL driver
 *
 *	@date 2006-05-16
 *
 *	passed code revision
 *
 *	renamed arguments passed to CGLDriver::Init
 *	all integers storing only true or false values were replaced by bool
 *	erased all open-gl init code from CGLDriver::Init, but glDepthFunc(GL_LEQUAL); (default GL_LESS)
 *
 *	@date 2006-10-31
 *
 *	passed code revision
 *
 *	added bool CGLDriver::MakeCurrent()
 *	renamed bool CGLDriver::Terminate() to bool CGLDriver::Shutdown()
 *	propagated comments from .cpp to .h
 *
 *	@date 2006-11-01
 *
 *	passed code revision
 *
 *	added int CGLDriver::n_Width(), int CGLDriver::n_Height() and
 *	void CGLDriver::ResetViewport() functions
 *	parameters int n_depth_bpp and int n_stencil_bpp added to CGLDriver::Init function
 *
 *	@date 2006-11-13
 *
 *	passed code revision
 *
 *	fixed bug in n_depth_bpp and int n_stencil_bpp ussage in CGLDriver::Init function
 *	added CGLDriver::n_PixelFormat() and CGLDriver::t_Pixelormat() functions
 *	fixed function const-corectness
 *
 *	@date 2007-03-06
 *
 *	removed __declspec(dllexport)
 *
 *	@date 2008-03-04
 *
 *	__inline changed to inline
 *	added default value 32 of parameter n_bpp in CGLDriver::Init
 *
 *	@date 2009-10-20
 *
 *	added PFD_SWAP_EXCHANGE flag to pixel format descriptor. this makes SwapBuffers() faster,
 *	because back buffer isn't copied to front buffer, they are just exchanged. on the other hand,
 *	reading back buffer after swap yields unspecified results (previous frame mostly)
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 */

#include "../NewFix.h"

#include "../CallStack.h"
#include <vector>
#include <windows.h>
#include <stdlib.h>
#include <stdio.h>
#include <time.h>
#include <math.h>
#include <GL/gl.h>
#include <GL/glu.h>

#include "OpenGlDrv.h"

/*
 *								=== CGLDriver ===
 */

/*
 *	CGLDriver::CGLDriver()
 *		- default constructor
 */
CGLDriver::CGLDriver()
	:m_b_status(false)
{
	__FuncGuard("CGLDriver::CGLDriver");
}

/*
 *	CGLDriver::~CGLDriver()
 *		- default destructor
 */
CGLDriver::~CGLDriver()
{
	__FuncGuard("CGLDriver::~CGLDriver");

    if(m_b_status)
        Shutdown();
}

/*
 *	CGLDriver::Init(HWND h_wnd, int n_width, int n_height, int n_bpp,
 *		int n_depth_bpp = 24, int n_stencil_bpp = 0, bool b_fullscreen = false)
 *		- create OpenGL context for window h_wnd
 *		- display area should have size n_width per n_height pixels, with bit depth n_bpp
 *		- neither window size nor position nor type is changed
 *		- if b_fullscreen is true, it attempts to set display mode n_width per n_height
 *		  pixels and bit depth n_bpp and to hide cursor
 *		- return true on success otherwise false
 *		- on failure it pops ut message box with error description
 */
bool CGLDriver::Init(HWND h_wnd, int n_width, int n_height, int n_bpp,
	int n_depth_bpp, int n_stencil_bpp, bool b_fullscreen)
{
	__FuncGuard("CGLDriver::Init");

    DEVMODE t_device_mode;

	if(m_b_status)
		Shutdown();

    if(b_fullscreen) {
        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;
        // nastavi rozliseni (pokud fullscreen)

        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;
        }
        // zkusi prepnout, pokud to nejde, zepta
        // se a pak skonci nebo zustane v okne

        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;
    // nastavi pixel format descriptor

    if(!(m_h_dc = GetDC(m_h_wnd = h_wnd))) {
        MessageBoxA(NULL, "Unable to create a GL device context.", "GL_Drv", MB_OK);
        return false;
    }
    if(!(m_n_pixel_format_id = ChoosePixelFormat(m_h_dc, &m_t_pixel_format))) {
        MessageBoxA(NULL, "Unable to find a suitable pixelformat.", "GL_Drv", MB_OK);
        return false;
    }
    if(!SetPixelFormat(m_h_dc, m_n_pixel_format_id, &m_t_pixel_format)) {
        MessageBoxA(NULL, "Unable to set the pixelformat.", "GL_Drv", MB_OK);
        return false;
    }
    if(!(m_h_glrc = wglCreateContext(m_h_dc))) {
        MessageBoxA(NULL, "Unable to create a GL rendering context.", "GL_Drv", MB_OK);
        return false;
    }
    if(!wglMakeCurrent(m_h_dc, m_h_glrc)) {
        MessageBoxA(NULL, "Unable to activate the GL rendering context.", "GL_Drv", MB_OK);
        return false;
    }
    // vytvori OpenGl kontext

    glViewport(0, 0, n_width, n_height);
    // nastavi nase okno

    m_n_width = n_width;
    m_n_height = n_height;
    m_b_status = true;
    m_b_fullscreen = b_fullscreen;
    // nastavi class

	glDepthFunc(GL_LEQUAL);

    return true;
}

/*
 *	bool CGLDriver::Shutdown()
 *		- terminates previously initalized context
 *		- return true if no context was initialized or on succes, otherwise false
 *		- if fulscreen mode was set, sets default fullscreen mode and shows cursor
 *		- on failure it pops ut message box with error description
 */
bool CGLDriver::Shutdown()
{
	__FuncGuard("CGLDriver::Shutdown");

	if(!m_b_status)
		return true;

    bool b_result = true;

    m_b_status = false;

    if(m_b_fullscreen) {
        ChangeDisplaySettings(NULL, 0);
        ShowCursor(TRUE);
    }
    // pokud bylo fullscreen, vrati zpatky

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

    if(m_h_dc && !ReleaseDC(m_h_wnd, m_h_dc)) {
		MessageBoxA(NULL, "Release device context failed.", "GL_Drv", MB_OK);
        m_h_dc = NULL;
        b_result = false;
    }

    return b_result;
}

/*
 *	bool CGLDriver::b_Status() const
 *		- returns status of open-gl (false = not initialized, true = running)
 */
bool CGLDriver::b_Status() const
{
	return m_b_status;
}

/*
 *	bool CGLDriver::MakeCurrent()
 *		- makes this opengl context current
 *		- returns true on success, false on failure
 */
bool CGLDriver::MakeCurrent()
{
    if(!wglMakeCurrent(m_h_dc, m_h_glrc))
        return false;
	return true;
}

/*
 *	void CGLDriver::ResetViewport() const
 *		- calls glViewport() with window dimensions
 */
void CGLDriver::ResetViewport() const
{
    glViewport(0, 0, m_n_width, m_n_height);
    // nastavi nase okno
}

/*
 *	void CGLDriver::Blit() const
 *		- commits page flip (call glSwapBuffers())
 *		- doesn't call neither glFlush() nor glFinish()
 */
void CGLDriver::Blit() const
{
	__FuncGuard("CGLDriver::Blit");

    /*glFlush();
    glFinish();*/
    SwapBuffers(m_h_dc);
}

/*
 *								=== ~CGLDriver ===
 */
