/*
	Rogue Trader.
	Copyright (c) 2007, Darragh Coy.
	All rights reserved.
	Original work(s), source code & author(s) details available at: http://www.darraghcoy.com

	Redistribution and use in source and binary forms, with or without modification, are permitted 
	provided that the following conditions are met:

		* 	Redistributions of source code must retain the above copyright notice, this list of 
			conditions and the following disclaimer.
		* 	Redistributions in binary form must reproduce the above copyright notice, this list of 
			conditions and the following disclaimer in the documentation.
		* 	Neither the name of the author nor the names of contributors may be used to endorse or 
			promote products derived from this software without specific prior written permission.
		* 	The use of any third party libraries, modules or data files provided with the source 
			form of the program must follow and respect the individual licensing terms of these 
			components. You do not have permission to modify or circumvent the licensing terms for 
			provided third party components unless the third party licenses specifically allow this. 
			If you do not agree to the terms of provided third party components then you do not have 
			permission to use them in your derived work(s).
		* 	The terms of this license do not apply to separate (but related) distributions if supplied 
			by the author(s), only to the contents of this source distribution, derived works, and the 
			binaries produced by either modified or unmodified versions of the source code. The terms 
			of this license do not also apply to any third party libraries or components provided with 
			the source distribution. Such third party elements are licensed under their own separate 
			agreements.

	THIS SOFTWARE IS PROVIDED BY DARRAGH COY ''AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, 
	BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
	ARE DISCLAIMED. IN NO EVENT SHALL DARRAGH COY BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
	EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
	OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 
	OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
	IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################
//
//
// Class Name:   GLWindow
// Purpose:      Static window class for setting up an OpenGL display. Handles all
//               of the mundane setup code behind the scenes.
//
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "GLWindow.h"

//------------------------------------------------------------------------------
// Static constants
//------------------------------------------------------------------------------

const char * GLWindow::WINDOW_CLASSNAME       = "DarraghsWindowClass";
const char * GLWindow::WINDOW_DEFAULT_TITLE   = "Untitled window";

//------------------------------------------------------------------------------
// Static variables
//------------------------------------------------------------------------------

bool                        GLWindow::fullscreenMode        = 0;    // Is the window in fullscreen ?
HINSTANCE                   GLWindow::programHandle         = 0;    // Handles
HWND                        GLWindow::windowHandle          = 0;
HDC                         GLWindow::deviceContext         = 0;
HGLRC                       GLWindow::openGLDeviceContext   = 0;
GLWindowEventListener *     GLWindow::eventListener         = 0;    // The window event listener object
int                         GLWindow::lastMouseX            = 0;
int                         GLWindow::lastMouseY            = 0;
int                         GLWindow::lastKeyPressed        = 0;
GLWindow::CursorType        GLWindow::windowCursor          = GLWindow::CURSOR_ARROW;   // The window cursor

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::windowDestroyed()
Purpose:  Called when the window is destroyed. Cleans up and releases resources.
          Clears hanging pointers.
Params:   None
Returns:  N/A

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::windowDestroyed()
{
    // Exit out of fullscreen mode if we are in it

    if ( fullscreenMode )
    {
        setFullscreen(false);
        fullscreenMode = 0;
    }

    // Release the device context and memory device context :

    ReleaseDC(windowHandle,deviceContext);

    // Release the openGL device context

    wglDeleteContext(openGLDeviceContext);

    // Clear the event listener pointer: do not delete because this is not the
    // windows responsibility.

    eventListener = 0;

    // Clear all handles

    programHandle         = 0;
    windowHandle          = 0;
    deviceContext         = 0;
    openGLDeviceContext   = 0;

    // Clear cursor type to default

    windowCursor = CURSOR_ARROW;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::createWindow()
Purpose:  Performs the main window setup and initialisation. must be called
          before we do anything with the window.
Params:   Window title, client width and height
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::createWindow( char * windowTitle , unsigned clientWidth , unsigned clientHeight )
{
    //--------------------------------------------------------------------------
    // Don't create if a window already exists!
    //--------------------------------------------------------------------------

    if ( exists() ) return;
    
    //--------------------------------------------------------------------------
    // Show this if an error occurs
    //--------------------------------------------------------------------------    
    
    #define CHECK(COND,ERR_MSG)						\
													\
		if ( ! (COND) )								\
		{											\
			MessageBox								\
			(										\
				NULL							,	\
				ERR_MSG							,	\
				"A fatal error has occured."	,	\
				MB_OK								\
			);										\
													\
			exit(1);								\
		}											\

    //--------------------------------------------------------------------------
    // Get program handle from windows
    //--------------------------------------------------------------------------

    programHandle = GetModuleHandle(NULL); CHECK(programHandle!=0,"Unable to get program handle")

    //--------------------------------------------------------------------------
    // Create and register window class
    //--------------------------------------------------------------------------

    // Create the window class and initialise all fields as zero

    WNDCLASSEX wndClass = {0};

    // If the windows class doesn't already exist then fill in the blanks and register class

    if ( ! GetClassInfoEx(programHandle,WINDOW_CLASSNAME,&wndClass) )
    {
       // Class not already registered: Now fill in the fields of the window class

       wndClass.cbSize         = sizeof(WNDCLASSEX);                        // size of window class struct
       wndClass.cbWndExtra     = 0;                                         // No extra window instance memory
       wndClass.hInstance      = programHandle;                             // Handle to program
       wndClass.lpfnWndProc    = windowProcedure;                           // Window procedure
       wndClass.lpszClassName  = WINDOW_CLASSNAME;                          // Window classname
       wndClass.hIcon          = LoadIcon(programHandle,IDI_APPLICATION);   // Default icon
       wndClass.hIconSm        = LoadIcon(programHandle,IDI_APPLICATION);   // Default icon (small)
       wndClass.hCursor        = LoadCursor(programHandle,IDC_ARROW);       // Default cursor
       wndClass.hbrBackground  = NULL;                                      // Application draws its own background
       wndClass.style          = CS_OWNDC;	                                // Class style

       // Try to register the class

       ATOM classAtom = RegisterClassEx(&wndClass); CHECK(classAtom!=0,"Unable to register window class")
    }

    //--------------------------------------------------------------------------
    // Create and show window
    //--------------------------------------------------------------------------

    // Create the actual window

    windowHandle = CreateWindowEx
    (
        0	,																	// Extended window style
        WINDOW_CLASSNAME,														// Window classname
        WINDOW_DEFAULT_TITLE,													// Default window title
        WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX | WS_VISIBLE	,	// Window style
        0,																		// Horizontal window position
        0,																		// Vertical window position
        800,																	// Window width
        600,																	// Window height
        NULL,																	// Parent window
        NULL,																	// Window menu
        programHandle,															// Program handle
        NULL																	// Pointer to window creation data
    );

    // Debug assertion
    
    CHECK(windowHandle!=0,"Unable to create window")

    // Set the default window cursor to an arrow:

    setCursor(CURSOR_ARROW);

    // Set the last known position of the mouse to 0,0 by default

    lastMouseX = 0;
    lastMouseY = 0;

    //--------------------------------------------------------------------------
    // Get the device context from windows
    //--------------------------------------------------------------------------

	deviceContext = GetDC(windowHandle); CHECK(deviceContext!=0,"Unable to get device context")

    //--------------------------------------------------------------------------
    // Show the window
    //--------------------------------------------------------------------------

    // show the window to the foreground and in focus

    ShowWindow(windowHandle,SW_SHOW);
    SetForegroundWindow(windowHandle);
    SetFocus(windowHandle);
    center();

    //--------------------------------------------------------------------------
    // Create an OpenGL rendering context and make it current
    //--------------------------------------------------------------------------

    // Fill a struct containing the desired pixel format for the window

    PIXELFORMATDESCRIPTOR pixelFormat = {0};

    pixelFormat.nSize       = sizeof(PIXELFORMATDESCRIPTOR);    // size of the struct
    pixelFormat.nVersion    = 1;                                // always 1
    pixelFormat.dwFlags    |= PFD_DRAW_TO_WINDOW;               // allow drawing to a window
    pixelFormat.dwFlags    |= PFD_DOUBLEBUFFER;                 // use double buffering
    pixelFormat.dwFlags    |= PFD_SUPPORT_OPENGL;               // support Opengl drawing
    pixelFormat.iPixelType  = PFD_TYPE_RGBA;                    // support none palettized pixels

    pixelFormat.cColorBits  = 32;                               // 32 bit color
    pixelFormat.cRedBits    = 8;                                // num bits for red
    pixelFormat.cGreenBits  = 8;                                // num bits for green
    pixelFormat.cBlueBits   = 8;                                // num bits for blue
    pixelFormat.cAlphaBits  = 8;                                // num bits for alpha

    pixelFormat.cAccumBits          = 32;                       // accumalation buffer: 32 bit color
    pixelFormat.cAccumRedBits       = 8;                        // accumalation buffer: num bits for red
    pixelFormat.cAccumGreenBits     = 8;                        // accumalation buffer: num bits for green
    pixelFormat.cAccumBlueBits      = 8;                        // accumalation buffer: num bits for blue
    pixelFormat.cAccumAlphaBits     = 8;                        // accumalation buffer: num bits for alpha

    pixelFormat.cDepthBits      = 24;                           // number of bits for depth buffer
    pixelFormat.cStencilBits    = 8;                            // number of bits in stencil buffer

    // Choose the pixel format that best matches the pixel format outlined above

    int chosenPixelFormat = ChoosePixelFormat(deviceContext,&pixelFormat);

    // Set the pixel format for the device context

    SetPixelFormat(deviceContext,chosenPixelFormat,&pixelFormat);

    // Create the openGL device context

    openGLDeviceContext = wglCreateContext(deviceContext); CHECK(openGLDeviceContext!=0,"Unable to create OpenGL device context")

    // Make the OpenGL rendering context current

    wglMakeCurrent(deviceContext,openGLDeviceContext);
    
    // Make sure vsync is on
    
    {   
		// Type for the wglSwapIntervalExt function:
		
		typedef BOOL ( APIENTRY * wglSwapIntervalEXT_function)( int interval );
		
		// Get the swap interval function:
     
		 wglSwapIntervalEXT_function fn = (wglSwapIntervalEXT_function) wglGetProcAddress("wglSwapIntervalEXT");
		 
		 // See if there:
		 
		 if ( fn != 0 )
		 {
			// Enable vysnc:
			
			fn(1);
		 }
	}

    // Set the viewport to the windows client size

    glViewport(0,0,getClientWidth(),getClientHeight());

    // Initialise the matrices for a 2D display

    glBeginDraw2d();

    //--------------------------------------------------------------------------
    // Miscellaneous:
    //--------------------------------------------------------------------------

    // Set the last key that was pressed to no key or 0:

    lastKeyPressed = 0;

    //--------------------------------------------------------------------------
    // Finally set the window title and the client width and height
    //--------------------------------------------------------------------------

    setTitle(windowTitle);
    setClientSize(clientWidth,clientHeight);
    
    //--------------------------------------------------------------------------
    // Done with this macro
    //--------------------------------------------------------------------------
    
    #undef CHECK
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::windowProcedure()
Purpose:  Window procedure for the OpenGL window. Calls any user event handlers
          if they have been specified also.
Params:   Window handle, message type, 1st message paramter,2nd message parameter
Returns:  Integer value representing outcome of message processing

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

LRESULT CALLBACK GLWindow::windowProcedure( HWND hwnd, UINT msg, WPARAM param1, LPARAM param2 )
{
    //--------------------------------------------------------------------------
    // Shorthand macro for callling the default windows procedure
    //--------------------------------------------------------------------------

    #define DEF_WIN_PROC return(DefWindowProc(hwnd,msg,param1,param2))

    //--------------------------------------------------------------------------
    // See what message we are looking at and respond appropriately
    //--------------------------------------------------------------------------

    switch ( msg )
    {
        //----------------------------------------------------------------------
        // Keyboard events
        //----------------------------------------------------------------------

        // Key down:

        case WM_KEYDOWN:
        {
            // Set the key code of the last key pressed by the user

            lastKeyPressed = param1;

            if ( eventListener )
            {
                 eventListener->keyboardPressed( KeyboardKey(param1) ); return 0;
            }
            else DEF_WIN_PROC;
        }

       // Key up:

       case WM_KEYUP:
       {

            if ( eventListener )
            {
                 eventListener->keyboardReleased( KeyboardKey(param1) ); return 0;
            }
            else DEF_WIN_PROC;

       }

        //----------------------------------------------------------------------
        // Mouse events
        //----------------------------------------------------------------------

        // Left Mouse down:

        case WM_LBUTTONDOWN:
        {

            // Save the last known position of the mouse

            lastMouseX = LOWORD(param2);
            lastMouseY = HIWORD(param2);

            // Send the event onto the event listener if there is one

            if ( eventListener )
            {
                eventListener->mousePressed(LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_LEFT); return 0;
            }
            else DEF_WIN_PROC;

        }

        // Right Mouse down:

        case WM_RBUTTONDOWN:
        {

            // Save the last known position of the mouse

            lastMouseX = LOWORD(param2);
            lastMouseY = HIWORD(param2);

            // Send the event onto the event listener if there is one

            if ( eventListener )
            {
                eventListener->mousePressed(LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_RIGHT); return 0;
            }
            else DEF_WIN_PROC;

        }

        // Middle Mouse down:

        case WM_MBUTTONDOWN:
        {

            // Save the last known position of the mouse

            lastMouseX = LOWORD(param2);
            lastMouseY = HIWORD(param2);

            // Send the event onto the event listener if there is one

            if ( eventListener )
            {
                eventListener->mousePressed(LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_MIDDLE);  return 0;
            }
            else DEF_WIN_PROC;

        }

        // Left Mouse up:

        case WM_LBUTTONUP:
        {

            // Save the last known position of the mouse

            lastMouseX = LOWORD(param2);
            lastMouseY = HIWORD(param2);

            // Send the event onto the event listener if there is one

            if ( eventListener )
            {
                eventListener->mouseReleased(LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_LEFT); return 0;
            }
            else DEF_WIN_PROC;

        }

        // Right Mouse up:

        case WM_RBUTTONUP:
        {

            // Save the last known position of the mouse

            lastMouseX = LOWORD(param2);
            lastMouseY = HIWORD(param2);

            // Send the event onto the event listener if there is one

            if ( eventListener )
            {
                eventListener->mouseReleased(LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_RIGHT); return 0;
            }
            else DEF_WIN_PROC;

        }

        // Middle Mouse up:

        case WM_MBUTTONUP:
        {

            // Save the last known position of the mouse

            lastMouseX = LOWORD(param2);
            lastMouseY = HIWORD(param2);

            // Send the event onto the event listener if there is one

            if ( eventListener )
            {
                eventListener->mouseReleased(LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_MIDDLE); return 0;
            }
            else DEF_WIN_PROC;

        }

        // Mouse moved

        case WM_MOUSEMOVE:
        {

            // Save the last known position of the mouse

            lastMouseX = LOWORD(param2);
            lastMouseY = HIWORD(param2);

            // Send the event onto the event listener if there is one

            if ( eventListener )
            {
                eventListener->mouseMoved(LOWORD(param2),HIWORD(param2)); return 0;
            }
            else DEF_WIN_PROC;

        }

        //----------------------------------------------------------------------
        // Window resized: adjust opengl viewport dimensions
        //----------------------------------------------------------------------

        case WM_SIZE:
        {

            // If an event listener is assigned to the window then call the resize function

            if ( eventListener ) eventListener->windowResized();

            // Resize the OpenGL viewport to client dimensions

            glViewport
            (
                0,                   // x position
                0,                   // y position
                getClientWidth(),    // width
                getClientHeight()    // height
            );

        return 0;

        }

        //----------------------------------------------------------------------
        // Repaint event: call user paint function if it exists:
        //----------------------------------------------------------------------

        case WM_PAINT:
        {
            // Try and set the cursor to current window cursor if over window client area

            if ( mouseInClientArea() ) setCursor(windowCursor);

            // If an event listener is assigned to the window then call the redrawing function of the listener

            if ( eventListener ) eventListener->windowRedrawing();

            // Call the default window procedure

            DEF_WIN_PROC;

        }

        //----------------------------------------------------------------------
        // Close event: window is closing
        //----------------------------------------------------------------------

        case WM_CLOSE:
        {

            // Call the closing event if there is an event listener

            if ( eventListener )
            {
                eventListener->windowClosing(); return 0;
            }
            else DEF_WIN_PROC;

        }

        //----------------------------------------------------------------------
        // Destroy event: window has been closed
        //----------------------------------------------------------------------

        case WM_DESTROY:
        {

            // Call the window closed event if it exists

            if ( eventListener ) eventListener->windowClosed();

            // Cleanup all window resources

            windowDestroyed();

            // Call the default window procedure

            DEF_WIN_PROC;

        }

        //----------------------------------------------------------------------
        // Default event
        //----------------------------------------------------------------------

           // Default

           default: DEF_WIN_PROC;

    } // end switch ( msg )

    //--------------------------------------------------------------------------
    // Undo this macro
    //--------------------------------------------------------------------------

    #undef DEF_WIN_PROC
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::run()
Purpose:  Processes events for the window and sends messages to the windows procedure.
Params:   None
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::run()
{
    // Declare a structure to hold a message

    static MSG message = {0};

    // Keep getting the next message from the message queue until messages for this window are processed

    while ( PeekMessage(&message,windowHandle,0,0,PM_REMOVE) )
    {
        // Convert virtual keycodes for letters to Ascii values

        TranslateMessage(&message);

        // Send the message to the windows procedure for processing

        DispatchMessage(&message);
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::requestRepaint()
Purpose:  Invalidates the window region and thus causes a repaint to happen...
Params:   None
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::requestRepaint()
{
     // Tell windows that the window has to be repainted

     InvalidateRect
       (
       windowHandle,  // window to repaint
       NULL,          // region to repaint: NULL repaints all
       false          // Don't repaint the background
       );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setTitle()
Purpose:  Sets the window title
Params:   Title
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setTitle( char * title  ){ SetWindowText(windowHandle,title); }

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setResizeable()
Purpose:  Sets whether the window is resizeable
Params:   True/false
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setResizeable( bool sizeable )
{
	// Save the current client size: we may need to restore it:
	
	int clientWidth		= getClientWidth();
	int clientHeight	= getClientHeight();

    // Get last window style:

    int lastWindowStyle = GetWindowLong( windowHandle , GWL_STYLE );

    // Switch on the thickframe attribute if sizeable, switch off if not:

    if ( sizeable )
    {
        // Switch on the sizeable attribute and maximize box

        lastWindowStyle |= WS_THICKFRAME;
        lastWindowStyle |= WS_MAXIMIZEBOX;
    }
    else
    {
        // Switch off the sizeable attribute if on and maximize box

        if ( lastWindowStyle & WS_THICKFRAME  ) lastWindowStyle ^= WS_THICKFRAME;
        if ( lastWindowStyle & WS_MAXIMIZEBOX ) lastWindowStyle ^= WS_MAXIMIZEBOX;
    }

    // Set new window style

    SetWindowLongPtr( windowHandle , GWL_STYLE		, lastWindowStyle	);
    SetWindowLongPtr( windowHandle , GWL_EXSTYLE	, 0					);
    
    // If there is any difference in client width or size then restore:
    
    if ( clientWidth != getClientWidth() || clientHeight != getClientHeight() )
    {
		setClientSize(clientWidth,clientHeight);   
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setSize()
Purpose:  Sets the window size
Params:   Width,height
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setSize ( int width , int height )
{
    // Abort if invalid dimensions given

    if ( width <= 0 || height <= 0 ) return;

    SetWindowPos
    (
        windowHandle,														// window handle
        HWND_TOP	,														// window z order
        0           ,														// window x position
        0           ,														// window y position
        width       ,														// window width
        height      ,														// window height
        SWP_NOZORDER | SWP_NOMOVE | SWP_NOCOPYBITS | SWP_FRAMECHANGED		// Retain z order and position
    );

    // Resize the OpenGL viewport

    glViewport( 0, 0, getClientWidth(), getClientHeight() );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setClientSize()
Purpose:  Sets the size of the window drawing area (client area)
          and resizes the window if necessary.
Params:   Client Width,client height
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setClientSize ( int clientWidth , int clientHeight )
{
    // Abort if invalid dimensions given

    if ( clientWidth <= 0 || clientHeight <= 0 ) return;

    // Get the window style:

    int windowStyle = GetWindowLongPtr(windowHandle,GWL_STYLE);

    // Get the extended window style:

    int windowStyleEx = GetWindowLongPtr(windowHandle,GWL_EXSTYLE);

    // Save desired client size here

    RECT windowSize;
    
    windowSize.left     = getX();
    windowSize.top      = getY();
    windowSize.right    = windowSize.left + clientWidth;
    windowSize.bottom   = windowSize.top  + clientHeight;
	
    // Get window to calculate a window size based on desired client size and save to 'windowSize'

    AdjustWindowRectEx
    (
       &windowSize,            // Rectangle to store new size in
       windowStyle,            // Window style to take into account
       false,					// Menu present flag: no menu
       windowStyleEx           // Extended window style to take into account
    );

    // Set the new window size to accomodate for desired client size

    setSize(windowSize.right-windowSize.left,windowSize.bottom-windowSize.top);

    // Resize the OpenGL viewport

    glViewport( 0, 0, clientWidth, clientHeight );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setPosition()
Purpose:  Sets the window position
Params:   xpos,ypox
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setPosition ( int x , int y )
{
    SetWindowPos
    (
        windowHandle,               // window handle
        HWND_TOP	,               // window z order
        x           ,               // window x position
        y           ,               // window y position
        0           ,               // window width
        0           ,               // window height
        SWP_NOZORDER | SWP_NOSIZE   // Retain z order and position
    );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setCursor()
Purpose:  Attempts to set the window cursor: will not work if cursor is not over window
Params:   Cursor type
Returns:  None

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setCursor ( CursorType cursor )
{
    // Store the coordinates of the client rectangle of the window here

    RECT clientRect;

    // Get the client rectangle from windows

    GetClientRect(windowHandle,&clientRect);

    // If the cursor is not in the client rectangle then do not change the cursor

    switch ( cursor )
    {
        case CURSOR_NONE:  SetCursor( LoadCursor(NULL,IDC_ICON ) ); windowCursor = cursor; break;
        case CURSOR_ARROW: SetCursor( LoadCursor(NULL,IDC_ARROW) ); windowCursor = cursor; break;
        case CURSOR_CROSS: SetCursor( LoadCursor(NULL,IDC_CROSS) ); windowCursor = cursor; break;
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getX()
Purpose:  Get the window position - x
Params:   None
Returns:  X position of window

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getX()
{
    // Get the windows position in screen coordinates

    RECT r; GetWindowRect( windowHandle, &r );

    // Return x position

    return(r.left);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getY()
Purpose:  Get the window position - y
Params:   None
Returns:  Y position of window

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getY()
{
    // Get the windows position in screen coordinates

    RECT r; GetWindowRect( windowHandle, &r );

    // Return x position

    return(r.top);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getWidth()
Purpose:  Get the window width
Params:   None
Returns:  Width of window

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getWidth()
{
    // Store window info here

    static WINDOWPLACEMENT winPlacement;

    // Get window info

    GetWindowPlacement(windowHandle,&winPlacement);

    // Return width

    return(winPlacement.rcNormalPosition.right - winPlacement.rcNormalPosition.left);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getHeight()
Purpose:  Get the window Height
Params:   None
Returns:  Height of window

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getHeight()
{
    // Store window info here

    static WINDOWPLACEMENT winPlacement;

    // Get window info

    GetWindowPlacement(windowHandle,&winPlacement);

    // Return Height

    return(winPlacement.rcNormalPosition.bottom - winPlacement.rcNormalPosition.top);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getClientWidth()
Purpose:  Get the width of drawing area in the window (client area)
Params:   None
Returns:  Client width

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getClientWidth()
{
    // Store client size info here

    static RECT clientRect;

    // Get window info

    GetClientRect(windowHandle,&clientRect);

    // Return client width

    return(clientRect.right);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getClientHeight()
Purpose:  Get the height of drawing area in the window (client area)
Params:   None
Returns:  Client height

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getClientHeight()
{
    // Store client size info here

    static RECT clientRect;

    // Get window info

    GetClientRect(windowHandle,&clientRect);

    // Return client height

    return(clientRect.bottom);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getScreenWidth()
Purpose:  Gets the width of the screen area
Params:   None
Returns:  Screen width

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getScreenWidth()
{
    return GetDeviceCaps(deviceContext,DESKTOPHORZRES);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getScreenHeight()
Purpose:  Gets the height of the screen area
Params:   None
Returns:  Screen height

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getScreenHeight()
{
    return GetDeviceCaps(deviceContext,DESKTOPVERTRES);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::isFullscreen()
Purpose:  Tells whether the window is in fullscreen mode or not
Params:   None
Returns:  if window is in fullscreen

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::isFullscreen()
{
    return fullscreenMode;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     Close, Minimise, maximise and restore functions
Purpose:  Does as above...
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::close()   { DestroyWindow(windowHandle);             }
void GLWindow::maximize(){ ShowWindow(windowHandle,SW_MAXIMIZE);    }
void GLWindow::minimize(){ ShowWindow(windowHandle,SW_MINIMIZE);    }
void GLWindow::restore (){ ShowWindow(windowHandle,SW_RESTORE) ;    }

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::center()
Purpose:  centers the window in the middle of the screen
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::center()
{
    // Center the window

    SetWindowPos
    (
        windowHandle,                               // handle to window
        HWND_TOPMOST,                               // new z order
        getScreenWidth()/2  - getWidth()/2 ,        // new x position
        getScreenHeight()/2 - getHeight()/2,        // new y position
        0,                                          // new x size: dont care, ignored
        0,                                          // new y size: dont care, ignored
        SWP_NOSIZE	|                               // dont resize
        SWP_FRAMECHANGED |                          // redraw frame
        SWP_NOZORDER	                            // dont change z order
    );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getMouseScreenX()
Purpose:  Gets the mouse position in screen coordinates
Params:   none
Returns:  x position of the mouse in screen coordinates

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getMouseScreenX()
{
    // Get the screen position of the mouse and return it

    POINT p; GetCursorPos(&p); return p.x;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getMouseScreenY()
Purpose:  Gets the mouse position in screen coordinates
Params:   none
Returns:  y position of the mouse in screen coordinates

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getMouseScreenY()
{
    // Get the screen position of the mouse and return it

    POINT p; GetCursorPos(&p); return p.y;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::centerMouse()
Purpose:  Centers the mouse in the middle of the windows client area
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::centerMouse()
{
    //--------------------------------------------------------------------------
    // Get the width and height of the screen
    //--------------------------------------------------------------------------

    int screenX = getScreenWidth();
    int screenY = getScreenHeight();

    //--------------------------------------------------------------------------
    // Now the mouse_event function needs normalised device coords. 65535 is the
    // extreme in the x and y directions and 0 is the minimum. Figure out the
    // scaling required for all coordinates in order to normalise their figures
    // to between 0 and 65535:
    //--------------------------------------------------------------------------

    float normaliseX = ( 65535 ) / float(screenX);
    float normaliseY = ( 65535 ) / float(screenY);

    //--------------------------------------------------------------------------
    // Get the windows position in screen coordinates
    //--------------------------------------------------------------------------

    int posX = getX();
    int posY = getY();

    //--------------------------------------------------------------------------
    // Get the thickness of a window border
    //--------------------------------------------------------------------------

    int borderX = GetSystemMetrics(SM_CXFRAME);
    int borderY = GetSystemMetrics(SM_CYFRAME);

    //--------------------------------------------------------------------------
    // Get the thickness of the window frame caption area:
    //--------------------------------------------------------------------------

    int menuY = GetSystemMetrics(SM_CYCAPTION);

    //--------------------------------------------------------------------------
    // Now figure out what x and y position the mouse should be at in order to be
    // in the center of the windows client area:
    //--------------------------------------------------------------------------

    int mouseX = posX + borderX + getClientWidth() / 2;
    int mouseY = posY + borderY + menuY + getClientHeight() / 2;

    //--------------------------------------------------------------------------
    // Now normalise these coordinates such that the bottom right of the screen
    // is at coordinate 65535, 65535:
    //--------------------------------------------------------------------------

    int normalisedMouseX = int(mouseX * normaliseX);
    int normalisedMouseY = int(mouseY * normaliseY);

    //--------------------------------------------------------------------------
    // Move the mouse to the center of the client area: synthesise a mouse movement
    //--------------------------------------------------------------------------

    mouse_event
    (
        MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,    // flags: move the mouse and give absolute position
        normalisedMouseX,                           // mouse x
        normalisedMouseY,                           // mouse y
        0,                                          // amount of wheel movement
        0                                           // extra application info
    );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setMousePosition()
Purpose:  Moves the mouse to the specified screen location
Params:   Screen position to move to
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setMousePosition( int screenX , int screenY )
{
	int screenW = getScreenWidth();
	int screenH = getScreenHeight();
	
	screenX <<= 16;		// Produce normalized coordinates
	screenY <<= 16;
	screenX /= screenW - 1;
	screenY /= screenH - 1;

    mouse_event
    (
        MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,    // flags: move the mouse and give absolute position
        screenX,									// mouse x
        screenY,									// mouse y
        0,                                          // amount of wheel movement
        0                                           // extra application info
    );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::restrictMouse()
Purpose:  Restricts mouse movement to the bounding box given.
Params:   coordinates of the bounding box.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::restrictMouse( int x , int y , int width , int height )
{
    // Makeup a rectangle for the mouse to restrict its movement:

    RECT r =
    {
        x,
        y,
        x + width,
        y + height
    };

    // Restrict the mouse movement to this rectangle

    ClipCursor(&r);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::restrictMouse()
Purpose:  un-restricts mouse movement from any bounding box it has previously
          been restricted to.
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::unRestrictMouse()
{
    // Un restrict the mouse movement

    ClipCursor(0);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getClientCenterX()
Purpose:  Gets the center of the client area in screen coordinates
Params:   none
Returns:  x coordinate of the center

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getClientCenterX()
{
    //--------------------------------------------------------------------------
    // Get the windows position in screen coordinates
    //--------------------------------------------------------------------------

    int posX = getX();

    //--------------------------------------------------------------------------
    // Get the thickness of a window border
    //--------------------------------------------------------------------------

    int borderX = GetSystemMetrics(SM_CXFRAME);

    //--------------------------------------------------------------------------
    // Now figure out where in the screen the center of the client area lies
    //--------------------------------------------------------------------------

    return posX + borderX + ( getClientWidth() / 2 ) - 1;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getClientCenterY()
Purpose:  Gets the center of the client area in screen coordinates
Params:   none
Returns:  y coordinate of the center

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getClientCenterY()
{
    //--------------------------------------------------------------------------
    // Get the windows position in screen coordinates
    //--------------------------------------------------------------------------

    int posY = getY();

    //--------------------------------------------------------------------------
    // Get the thickness of a window border
    //--------------------------------------------------------------------------

    int borderY = GetSystemMetrics(SM_CYFRAME);

    //--------------------------------------------------------------------------
    // Get the thickness of the window frame caption area:
    //--------------------------------------------------------------------------

    int menuY = GetSystemMetrics(SM_CYCAPTION);

    //--------------------------------------------------------------------------
    // Now figure out where in the screen the center of the client area lies
    //--------------------------------------------------------------------------

    return posY + borderY + menuY + ( ( getClientHeight() ) / 2 ) - 1;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::mouseInClientArea()
Purpose:  Tells if the mouse is in the client area of the window
Params:   none
Returns:  true if it is, false otherwise.

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::mouseInClientArea()
{
    //--------------------------------------------------------------------------
    // Get the window position in screen coordinates
    //--------------------------------------------------------------------------

    int posX = getX();
    int posY = getY();

    //--------------------------------------------------------------------------
    // Get the thickness of a window border
    //--------------------------------------------------------------------------

    int borderX = GetSystemMetrics(SM_CXFRAME);
    int borderY = GetSystemMetrics(SM_CYFRAME);

    //--------------------------------------------------------------------------
    // Get the thickness of the window frame caption area:
    //--------------------------------------------------------------------------

    int menuY = GetSystemMetrics(SM_CYCAPTION);

    //--------------------------------------------------------------------------
    // Get the current position of the mouse
    //--------------------------------------------------------------------------

    POINT mouse; GetCursorPos( & mouse );

    int mouseX = mouse.x;
    int mouseY = mouse.y;

    //--------------------------------------------------------------------------
    // Now see if it is in the client area:
    //--------------------------------------------------------------------------

    if ( mouseX <= posX + borderX                     ) return false;
    if ( mouseY <= posY + borderY + menuY             ) return false;

    if ( mouseX > posX + borderX + getClientWidth() + 1          ) return false;
    if ( mouseY > posY + borderY + menuY + getClientHeight() + 1 ) return false;

    // If we have got to here mouse is in the client area: return true

    return true;
}


/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setFullscreen()
Purpose:  Returns a fullscreen display mode compatible with the given resolution: 
		  returns false if this cannot be obtained
Params:   Desired resolution. Where to save device mode.
Returns:  False if a good mode cannot be found

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::getFullscreenMode( int resolution_x , int resolution_y , DEVMODE * device_mode )
{
	// Store if we found a suitable mode here:
	
	bool found_mode = false;
	
	// This is the current best mode with the highest refresh rate found:
	
	DEVMODE best_mode = {0};

    // Get current display settings from windows
    
    unsigned mode_num = 0;
    
    while 
    (
		EnumDisplaySettings
		(
			NULL		,      // use device that calling thread uses
			mode_num	,      // get the current settings
			device_mode        // where to save these settings
		)		
		!= FALSE
	)
	{
		// See if this mode matches the target resolution:
		
		if ( device_mode->dmPelsWidth == (unsigned) resolution_x && device_mode->dmPelsHeight == (unsigned) resolution_y )
		{
			// Match: see if we found a match before
			
			if ( found_mode == false )
			{
				// This is the best mode found:
				
				memcpy( &best_mode , device_mode , sizeof(DEVMODE) );
			}
			else
			{
				// See if this modes refresh rate is higher and 60hz or under: 60hz is the ideal refresh rate
				
				if 
				( 
					(( device_mode->dmDisplayFrequency > best_mode.dmDisplayFrequency ) && best_mode.dmDisplayFrequency < 60 )
					||
					(( device_mode->dmDisplayFrequency < best_mode.dmDisplayFrequency ) && best_mode.dmDisplayFrequency > 60 )
				)
				{				
					// This is the new best mode:
					
					memcpy( &best_mode , device_mode , sizeof(DEVMODE) );
				}
			}
			
			// Found a suitable display mode
			
			found_mode = true;
		}
	
		// Next mode
		
		mode_num++;
	}
	
	// If we found the best mode then copy it in:
	
	if ( found_mode )
	{
		memcpy( device_mode , &best_mode , sizeof(DEVMODE) );
	}
	
	// Return if we found a suitable mode
	
	return found_mode;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setFullscreen()
Purpose:  sets the window into fullscreen mode and back..
Params:   fullscreen, client width and height of fullscreen/windowed display
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setFullscreen( bool fullscreen , int resolution_x , int resolution_y )
{
     // store outcome of display change here

     int changeResult = 0;

     // See if fullscreen is set to true

     if ( fullscreen )
     {
		  // Get the best mode we can matching resolution, abort if not found:
		  
		  DEVMODE deviceMode = {0};
		  
		  if ( getFullscreenMode(resolution_x,resolution_y,&deviceMode) == false )
		  {
				// Abort:
				
				return;
		  }

          // Change the display mode into fullscreen and save the result of the change:

          changeResult = ChangeDisplaySettings
          (
              &deviceMode,                       // device settings
              CDS_FULLSCREEN | CDS_SET_PRIMARY   // switch to fullscreen
          );

          // Set the cursor to the normal arrow shape- eliminates loading cursor that sometimes occurs

          SetCursor(LoadCursor(NULL,IDC_ARROW));

          // Only change window settings if display change was successful

          if ( changeResult == 0 )
          {                          
              // Set the position of the window to 0,0

              SetWindowPos
              (
                windowHandle,   // window handle
                HWND_TOPMOST,   // z order
                0,              // x position
                0,              // y position
                resolution_x,   // size x
                resolution_y,   // size y
                0               // flags
              );
              
              // Set the window style to popup: popup windows stay in focus set to visible also.

              SetWindowLongPtr( windowHandle, GWL_STYLE   , WS_VISIBLE	| WS_POPUP	);
              SetWindowLongPtr( windowHandle, GWL_EXSTYLE , WS_EX_TOPMOST 			);

              // Set window full screen flag to true

              fullscreenMode = true;

              // Resize the opengl viewport by default
				
              glViewport(0,0,getClientWidth(),getClientHeight());             
          }
     }
     else
     {
          // Change the display back to windowed

          changeResult = ChangeDisplaySettings
              (
              NULL,  // device settings: return to previous display settings from the registry
              0      // no flags
              );

          // Only change window settings if display change was successful

          if ( changeResult == 0 )
          {
              // Set the window style back to normal

              SetWindowLong( windowHandle, GWL_STYLE   , WS_OVERLAPPEDWINDOW ^ WS_THICKFRAME ^ WS_MAXIMIZEBOX | WS_VISIBLE	);
              SetWindowLong( windowHandle, GWL_EXSTYLE , 0																	);
          
              // Set the client size to the screen resolution

              setClientSize( resolution_x , resolution_y );

              // Center the window

              center();

              // Set window full screen flag to false

              fullscreenMode = false;
          }
     }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setFullscreen()
Purpose:  Tests if the given fullscreen resolution is supported: returns true 
		  if this is the case
Params:   client width and height of fullscreen display
Returns:  True if the given mode is supported

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::testFullscreen( int resolution_x , int resolution_y )
{
	// Declare and fill in some of the fields in the DEVMODE struct

     DEVMODE deviceMode = {0};
     
	// Return if we can match this resolution
	  
	return getFullscreenMode(resolution_x,resolution_y,&deviceMode);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setGamma()
Purpose:  Sets the gamma correction value for the screen and color mappings
Params:   gamma value
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setGamma( float gamma )
{
    // If gamma is negative or greater than 2 then abort

    if ( gamma < 0 || gamma > 2 ) return;

    // See if the device context is null or not:

    if ( deviceContext != 0 )
    {
        // Make up screen to device color tables for r,g,b

        unsigned short colorTables[3][256];

        // Fill the table values:

        for ( unsigned i = 0 ; i <= 255 ; i++ )
        {
            // Compute the value of this r,g,b index and gamma correct

            unsigned value = i * int( gamma * 255 );

            // Clamp color intensity value to 0-0xffff

            if ( value > 0xffff ) value = 0xffff;

            // Save to r,g,b color tables:

            colorTables[2][i] =
            colorTables[1][i] =
            colorTables[0][i] = (unsigned short)(value);
        }

        // Set the color tables for the device

        SetDeviceGammaRamp(deviceContext,colorTables);
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::glBeginDraw2d()
Purpose:  Sets up the matrices for a 2D drawing session
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::glBeginDraw2d()
{
    // Set the viewport to the windows client size

    glViewport(0,0,getClientWidth(),getClientHeight());

    // Store projection matrix here

    float matrix[] =
    {
        1,0,0,0,
        0,1,0,0,
        0,0,1,0,
        0,0,0,1
    };

    // Set Matrix scaling so that one gl unit equals one pixel on screen:

    matrix[0] = 1.0f / ( getClientWidth()   * 0.5f );
    matrix[5] = 1.0f / ( getClientHeight()  * 0.5f );

    // Reverse postive y direction from going up the screen to going down.
    // This keeps things in line with the coord system windows uses (top left of screen is 0,0)

    matrix[5] = -matrix[5];

    // Select the Projection matrix for openGL and set our matrix to the one used

    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(matrix);

    // Have the output translated so that 0,0 is not the center of the viewport but the top left

    glTranslatef
    (
        -getClientWidth()   * 0.5f,
        -getClientHeight()  * 0.5f,
        0
    );

    // Select the model view matrix and load the identity transform

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::glBeginDraw3d()
Purpose:  Sets up the matrices for a 3D drawing session
Params:   Feeling of view, near plane distance, far plane distance
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::glBeginDraw3d(float fov,float nearPlaneDist,float farPlaneDist)
{
    // Set the viewport to the windows client size

    glViewport(0,0,getClientWidth(),getClientHeight());

    // Use the glu helper function to the set the perspective mode

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    gluPerspective
    (
        fov,                                                // feeling of view angle
        float(getClientWidth())/float(getClientHeight()),   // aspect ratio
        nearPlaneDist,
        farPlaneDist
    );

    // Select the model view matrix and load the identity transform

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();   
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::swapBuffers()
Purpose:  Swaps the back buffer onto the front buffer, thus completing a draw
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::swapBuffers()
{
    // Swap the back bufer onto the front buffer

    SwapBuffers(deviceContext);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::showQuestionBox()
Purpose:  Shows a yes/no question box
Params:   none
Returns:  Returns true if yes clicked, false if not

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::showQuestionBox( char * question , char * title )
{
    // Show the message box

    int answer = MessageBox
         (
           windowHandle,
           question,
           title,
           MB_YESNO | MB_ICONQUESTION | MB_TOPMOST | MB_APPLMODAL | MB_SETFOREGROUND
         );

    // Return answer

    if ( answer == IDYES ) return true; else return false;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::showMessageBox()
Purpose:  Shows a message box with specified text and title
Params:   none
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::showMessageBox( char * message , char * title )
{
    // Show the message box

    MessageBox
    (
        windowHandle,
        message,
        title,
        MB_OK | MB_ICONINFORMATION | MB_TOPMOST | MB_APPLMODAL | MB_SETFOREGROUND
    );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::keyboardKeyPressed()
Purpose:  Tells if a particular keyboard key is pressed
Params:   Key to check
Returns:  True if it is, false if it isn't

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::keyboardKeyPressed( KeyboardKey key )
{
    // Return whether the key is pressed or not

    return ( GetAsyncKeyState(key) & 0x80000000 ? true : false );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::keyboardKeyPressed()
Purpose:  Tells if a particular keyboard key is pressed
Params:   Key to check
Returns:  True if it is, false if it isn't

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::keyboardKeyPressed( char key )
{
    // Return whether the key is pressed or not

    return ( GetAsyncKeyState(key) & 0x80000000 ? true : false );
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::mouseButtonPressed()
Purpose:  Tells if a particular mouse button is pressed
Params:   Key to check
Returns:  True if it is, false if it isn't

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::mouseButtonPressed( MouseButton button )
{
    // Decide which button to check for:

    switch ( button )
    {
        case MOUSE_BUTTON_LEFT:

            // Check if left mouse button is pressed

            return ( GetAsyncKeyState(VK_LBUTTON) & 0x80000000 ? true : false );

        case MOUSE_BUTTON_RIGHT:

            // Check if right mouse button is pressed

            return ( GetAsyncKeyState(VK_RBUTTON) & 0x80000000 ? true : false );

        case MOUSE_BUTTON_MIDDLE:

            // Check if middle mouse button is pressed

            return ( GetAsyncKeyState(VK_MBUTTON) & 0x80000000 ? true : false );

        default:

            // Invalid mouse button: return false

            return false;
    }
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::fadeScreen()
Purpose:  Fades the screen to black in the given numebr of seconds.
Params:   Time to take fading out the screen.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::fadeScreen( float time )
{
    //--------------------------------------------------------------------------
    // If time is negative or zero then abort:
    //--------------------------------------------------------------------------

    if ( time <= 0 ) return;

    //--------------------------------------------------------------------------
    // If time is greater than 10 seconds then clamp to 10:
    //--------------------------------------------------------------------------

    if ( time >= 10 ) time = 10;

    //--------------------------------------------------------------------------
    // Save the current window event listener:
    //--------------------------------------------------------------------------

    GLWindowEventListener * previousListener = getEventListener();

    //--------------------------------------------------------------------------
    // Set window event listener to null:
    //--------------------------------------------------------------------------

    setEventListener(0);

    //--------------------------------------------------------------------------
    // Grab a copy of the screen:
    //--------------------------------------------------------------------------

    // Get the width and height of the client area of the screen

    unsigned screenW = getClientWidth();
    unsigned screenH = getClientHeight();

    //--------------------------------------------------------------------------
    // Save opengl matrices and enables state:
    //--------------------------------------------------------------------------

    glMatrixMode( GL_PROJECTION );  glPushMatrix();
    glMatrixMode( GL_MODELVIEW  );  glPushMatrix();

    glPushAttrib( GL_ENABLE_BIT );

    //--------------------------------------------------------------------------
    // Copy whatever is in the back buffer into the accumalation buffer:
    //--------------------------------------------------------------------------

    // Make sure we are reading from the back buffer:

    glReadBuffer(GL_BACK);

    // Grab the screen in the back buffer and put into the accumalation buffer: do a direct pixel copy with no shin-diggery...

    glAccum( GL_LOAD , 1 );

    // Make sure future writes are to the back buffer

    glDrawBuffer(GL_BACK);

    //--------------------------------------------------------------------------
    // Switch into a 2d drawing mode:
    //--------------------------------------------------------------------------

    glBeginDraw2d();

    //--------------------------------------------------------------------------
    // Setup opengl state:
    //--------------------------------------------------------------------------

    glDisable( GL_LIGHTING      );
    glDisable( GL_TEXTURE_2D    );
    glDisable( GL_CULL_FACE     );
    glDisable( GL_DEPTH_TEST    );
    glEnable ( GL_BLEND         );

    glBlendFunc( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA );

    //--------------------------------------------------------------------------
    // Set the time left to the end as the fade time:
    //--------------------------------------------------------------------------

    float timeLeft = time;

    //--------------------------------------------------------------------------
    // Create a timer to time the fade out:
    //--------------------------------------------------------------------------

    Timer timer;

    //--------------------------------------------------------------------------
    // Continously fade the screen until the time to fade is up:
    //--------------------------------------------------------------------------

    while ( timeLeft > 0 && GLWindow::exists() )
    {
        // Run the window for a while to keep it responsive:

        run();

        // Copy the grabbed screen from the accumaltion buffer onto the screen

        glAccum( GL_RETURN , 1 );

        // Draw a black alpha blended quad over the screen and gradually make more opaque as time passes:

        glColor4f
        (
            0,
            0,
            0,
            1.0f - timeLeft / time
        );

        glBegin(GL_QUADS);

            glVertex2f(0				,0				);
            glVertex2f(float(screenW)	,0				);
            glVertex2f(float(screenW)	,float(screenH)	);
            glVertex2f(0				,float(screenH)	);

        glEnd();

        // Swap the back buffer onto the front buffer:

        swapBuffers();

        // Update the timer:

        timer.update();

        // Decrement time left by time in between frames:

        timeLeft -= timer.getSeconds();
    }

    //--------------------------------------------------------------------------
    // Restore opengl matrices and enables state:
    //--------------------------------------------------------------------------

    glMatrixMode( GL_MODELVIEW  );  glPushMatrix();
    glMatrixMode( GL_PROJECTION );  glPushMatrix();
    glMatrixMode( GL_MODELVIEW  );

    glPopAttrib();

    //--------------------------------------------------------------------------
    // Restore the previous event listener:
    //--------------------------------------------------------------------------

    setEventListener(previousListener);
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::setEventListener()
Purpose:  Sets the current event listener for the window.
Params:   The event listener to receive window events.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::setEventListener( GLWindowEventListener * eventListener )
{
    GLWindow::eventListener = eventListener;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getEventListener()
Purpose:  Returns the current event listener for the window
Params:   none
Returns:  The window event listener

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

GLWindowEventListener * GLWindow::getEventListener()
{
    return eventListener;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::exists()
Purpose:  Simply tells if the window is still around
Params:   none
Returns:  True if it is, false if it isn't

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

bool GLWindow::exists() { return IsWindow(windowHandle) != 0; }

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::getLastKeyPressed()
Purpose:  Returns the last key pressed by the user and clears the last key pressed.
Params:   none
Returns:  The last key pressed. 0 if no key was pressed

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

int GLWindow::getLastKeyPressed()
{
    // Save the last key pressed:

    int key = lastKeyPressed;

    // Clear the last key pressed:

    lastKeyPressed = 0;

    // Return the key pressed:

    return key;
}

/*@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

Name:     GLWindow::clearScreen()
Purpose:  Clears the screen to a given color
Params:   Color to clear the screen to.
Returns:  none

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@*/

void GLWindow::clearScreen( float r , float g, float b )
{
    // Set the clear screen color:

    glClearColor(r,g,b,1);

    // Clear thes screen to the given color:

    glClear( GL_COLOR_BUFFER_BIT );
}
