/*
	Wheels Of Steel
	Copyright (c) 2006, 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:   Window
// Purpose:      Win32 windowing and drawing functions in one class.
//               The class also handles bitmap loading and can automatically
//               setup an openGL display.
//
//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

#include "Window.h"

//##############################################################################
//
// Static and constant variables
//
//##############################################################################

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

//##############################################################################
//
// Window functions
//
//##############################################################################

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::Window()
Purpose:  Overloaded Constructor: sets up window class and sets title and size
Params:   Window title
Returns:  N/A

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

Window::Window(char * title , int width , int height)
{
    // Zero class memory: initialise everything as zero...

    memset(this,0,sizeof(Window));

    // Setup the window

    initWindow();
    setTitle(title);
    setClientSize(width,height);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::~Window()
Purpose:  Destructor: cleans up after window is done
Params:   None
Returns:  N/A

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

Window::~Window()
{
    // Delete all created gdi objects

    DeleteObject(brushHandle);
    DeleteObject(penHandle);
    DeleteObject(fontHandle);

    // Release the device context and memory device context :

    DeleteDC(memoryDeviceContext);
    DeleteDC(bitmapDeviceContext);
    ReleaseDC(windowHandle,deviceContext);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::initWindow()
Purpose:  Performs the main window setup and initialisation
Params:   None
Returns:  N/A

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::initWindow()
{
   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // Initialise class and get program handle
   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

   // Get program handle from windows

   programHandle = GetModuleHandle(NULL);

   // If we cant get it then quit

   if ( programHandle == NULL ) PROGRAM_ERROR("Cannot 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     = sizeof(Window*);                                   // Allocate memory for each window to hold a C++ instance pointer
       wndClass.hInstance      = programHandle;                                     // Handle to program
       wndClass.lpfnWndProc    = Window::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: abort program on failure

       if ( ! RegisterClassEx(&wndClass) ) PROGRAM_ERROR("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 ,			// Window style
        0,																// Horizontal window position
        0,																// Vertical window position
        320,															// Window width
        240,															// Window height
        NULL,															// Parent window
        NULL,															// Window menu
        programHandle,													// Program handle
        NULL															// Pointer to window creation data
    );

    // If window not created successfully then abort

    if ( windowHandle == NULL ) PROGRAM_ERROR("Failed creating window");

    // Add the pointer to this instance of the Window class to the extra window attributes:

    SetWindowLong(windowHandle,0,(long)(this));

    // Set the default window cursor to an arrow:

    setCursor(CURSOR_ARROW);

   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // Setup device context
   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // Get the device context (front buffer) for rendering and validate

    deviceContext = GetDC(windowHandle);

    if ( deviceContext == NULL ) PROGRAM_ERROR("Cannot get device context for window");

    // Create a backbuffer (memory device context) and bitmap context and validate

    memoryDeviceContext = CreateCompatibleDC(deviceContext);
    bitmapDeviceContext = CreateCompatibleDC(deviceContext);

    if ( memoryDeviceContext == NULL ) PROGRAM_ERROR("Cannot create memory device context for window");
    if ( bitmapDeviceContext == NULL ) PROGRAM_ERROR("Cannot create bitmap device context for window");

    // Create the memory device bitmap. This will be the bitmap actually drawn to. A normal GDI back buffer.
    
    memoryDeviceBitmap = CreateCompatibleBitmap
    (
        deviceContext,
        getClientWidth(),
        getClientHeight()
    );
        
    if ( memoryDeviceBitmap == NULL ) PROGRAM_ERROR("Cannot create memory device bitmap");

    // Select the memory device bitmap into the memory device context

    SelectObject(memoryDeviceContext,memoryDeviceBitmap);

    // Set background mode as transparent: stops filling in between line dashes and filling in around text.

    SetBkMode(memoryDeviceContext,TRANSPARENT);

   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // Setup drawing objects
   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // Create and select the drawing pen

    pen.lopnStyle   = PS_SOLID;    // logical pen style
    pen.lopnWidth.x = 1;           // logical pen width: specified in a point struct-why?
    pen.lopnColor   = 0x00ffffff;  // logical pen color: black

    penHandle = CreatePenIndirect(&pen);

    if ( penHandle == NULL ) PROGRAM_ERROR("Cannot create pen for window");

    SelectObject(memoryDeviceContext,penHandle);

    // Create the drawing brush

    brush.lbStyle = BS_HOLLOW  ;                // brush filled ?
    brush.lbColor = 0x00ffffff ;                // fill color
    brush.lbHatch = HS_CROSS   ;                // Pattern fill type (if used)

    brushHandle = CreateBrushIndirect(&brush);  // Create brush with windows

    if ( brushHandle == NULL ) PROGRAM_ERROR("Cannot create brush for window");

    SelectObject(memoryDeviceContext,brushHandle);

    // Create the font

    font.lfHeight           = 16;                           // font height
    font.lfWidth            = 12;                           // font width
    font.lfEscapement       = 0;                            // angle base line of text makes with x-axis (tenths of degrees)
    font.lfOrientation      = 0;                            // angle text makes with x-axis (tenths of degrees)
    font.lfWeight           = FW_NORMAL;                    // font weight
    font.lfItalic           = FALSE;                        // Italics?
    font.lfUnderline        = FALSE;                        // Underline?
    font.lfStrikeOut        = FALSE;                        // StrikeOut?
    font.lfCharSet          = ANSI_CHARSET;                 // character set
    font.lfOutPrecision     = OUT_DEFAULT_PRECIS;           // character drawing precision: use default
    font.lfClipPrecision    = CLIP_DEFAULT_PRECIS;          // character clipping precision: use default
    font.lfQuality          = PROOF_QUALITY;                // Good quality for text drawn
    font.lfPitchAndFamily   = DEFAULT_PITCH | FF_DONTCARE;  // Don't care about pitch and family

    strcpy_s(font.lfFaceName,32,"Times new roman");         // set font name
    SetTextColor(memoryDeviceContext,0x00ffffff);           // set font color
    fontHandle = CreateFontIndirect(&font);                 // Create font with windows

    if ( fontHandle == NULL ) PROGRAM_ERROR("Cannot create font for window");   // Check if font is ok

    SelectObject(memoryDeviceContext,fontHandle);     // Select font for use

   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   // Show the window
   //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // show the window to the foreground and in focus

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::windowProcedure()
Purpose:  Static window procedure for all windows: calls public event handlers
          (if specified) for each window.
Params:   Window handle, message type, 1st message paramter,2nd message parameter
Returns:  Integer value representing outcome of message processing

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

LRESULT CALLBACK Window::windowProcedure( HWND hwnd, UINT msg, WPARAM param1, LPARAM param2 )
{
    // Get a pointer to the c++ instance of the window: pointer is stored as
    // extra info in windows instance of the window...

    Window * window = (Window*)( GetWindowLong(hwnd,0) );

    // Ignore event macro: just call default windows procedure

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

    // If no window pointer set (as with window creation) just call the
    // default window procedure...

    if ( window )
    {

        // We have a window pointer: go to appropriate case statement and call
        // window implemented event handler if it exists...

        switch ( msg )
        {

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

                case WM_PAINT:

                    // Set the window cursor to the currently selected cursor
                    // type in case the cursor has changed:

                    window->setCursor(window->windowCursor);

                    // If a user defined paint function exists then call it:

                    if ( window->event_paint ) window->event_paint(*window);

                    // Swap the GDI back buffer onto the front buffer

                    window->swapBuffers();

                    // Call the default window procudure

                    IGNORE_EVENT;

            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Keyboard events
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

               // Key down:

               case WM_KEYDOWN:

                    if ( window->event_keyDown )
                    {
                         window->event_keyDown(*window,(Window::KeyboardKey)(param1)); return 0;
                    }
                    else IGNORE_EVENT;

               // Key up:

               case WM_KEYUP:

                    if ( window->event_keyUp )
                    {
                         window->event_keyUp(*window,(Window::KeyboardKey)(param1)); return 0;
                    }
                    else IGNORE_EVENT;

            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Mouse events
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

               // Left Mouse down:

               case WM_LBUTTONDOWN:

                    if ( window->event_mouseDown )
                    {
                        window->event_mouseDown(*window,LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_LEFT); return 0;
                    }
                    else IGNORE_EVENT;

               // Right Mouse down:

               case WM_RBUTTONDOWN:

                    if ( window->event_mouseDown )
                    {
                        window->event_mouseDown(*window,LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_RIGHT); return 0;
                    }
                    else IGNORE_EVENT;

               // Middle Mouse down:

               case WM_MBUTTONDOWN:

                    if ( window->event_mouseDown )
                    {
                        window->event_mouseDown(*window,LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_MIDDLE);  return 0;
                    }
                    else IGNORE_EVENT;

               // Left Mouse up:

               case WM_LBUTTONUP:

                    if ( window->event_mouseUp )
                    {
                        window->event_mouseUp(*window,LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_LEFT); return 0;
                    }
                    else IGNORE_EVENT;

               // Right Mouse up:

               case WM_RBUTTONUP:

                    if ( window->event_mouseUp )
                    {
                        window->event_mouseUp(*window,LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_RIGHT); return 0;
                    }
                    else IGNORE_EVENT;

               // Middle Mouse up:

               case WM_MBUTTONUP:

                    if ( window->event_mouseUp )
                    {
                        window->event_mouseUp(*window,LOWORD(param2),HIWORD(param2),MOUSE_BUTTON_MIDDLE); return 0;
                    }
                    else IGNORE_EVENT;

                // Mouse moved

               case WM_MOUSEMOVE:

                    if ( window->event_mouseMove )
                    {
                        window->event_mouseMove(*window,LOWORD(param2),HIWORD(param2)); return 0;
                    }
                    else IGNORE_EVENT;

            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Window resized: adjust memory device bitmap to correct size:
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

               case WM_SIZE:

                    // If the device context has not been created yet then ignore

                    if ( window->deviceContext == NULL || window->memoryDeviceContext ==  NULL ) return 0;

                    // Delete the old memory device bitmap

                    DeleteObject(window->memoryDeviceBitmap);

                    // Create the new memory device bitmap. This will be the bitmap actually drawn to..

                    window->memoryDeviceBitmap = CreateCompatibleBitmap
                    (
                        window->deviceContext,
                        window->getClientWidth (),
                        window->getClientHeight()
                    );

                    // Select the memory device bitmap into the memory device context

                    SelectObject(window->memoryDeviceContext,window->memoryDeviceBitmap);

                    // If the user size function exists then call it..

                    if ( window->event_resize ) window->event_resize(*window);

                    // Returnt the default window procedure

                    IGNORE_EVENT;

            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Close and destroy events
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                 case WM_CLOSE:

                      if ( window->event_closing )
                      {
                           window->event_closing(*window); return 0;
                      }
                      else IGNORE_EVENT;

                 case WM_DESTROY:

                      if ( window->event_closed )
                      {
                           window->event_closed(*window); return 0;
                      }
                      else IGNORE_EVENT;

            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            // Default event
            //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

               // Default

               default: IGNORE_EVENT;


        } // end switch ( msg )
    }
    else
    {
        // Couldn't get c++ window pointer, call default winProc...

        IGNORE_EVENT;

    } // end else of if ( window )

    // Undo ignore event macro

    #undef IGNORE_EVENT
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::run() const
{
    // 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:     Window::checkError()
Purpose:  Checks for an error with windows and prints it out if there is one.
          Then clears the error state.
Params:   String for the start of the message
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::checkError(char * msgStart)
{
    //--------------------------------------------------------------------------
    // Get the last error code from windows.
    //--------------------------------------------------------------------------

    int error = GetLastError();

    //--------------------------------------------------------------------------
    // If there is an error then get the corresponding message for it from windows.
    //--------------------------------------------------------------------------

    if  ( error != NO_ERROR )
    {
        //----------------------------------------------------------------------
        // Define a buffer to hold an error message
        //----------------------------------------------------------------------

        char messageBuf[8192] = {0};

        //----------------------------------------------------------------------
        // Concatenate the msgStart string to the start of the message and a colon
        //----------------------------------------------------------------------

        strcat_s(messageBuf,8192,msgStart);
        strcat_s(messageBuf,8192,":");

        //----------------------------------------------------------------------
        // Get the error message from windows
        //----------------------------------------------------------------------

        FormatMessage
        (
            FORMAT_MESSAGE_FROM_SYSTEM,         // Get the error message from the system
            0,                                  // No source text
            error,                              // error message ID
            0,                                  // Dont care about language ID
            messageBuf + strlen(messageBuf),    // output buffer
            2048,                               // maximum size of message string
            0                                   // no arguments to insert into message
        );

        //----------------------------------------------------------------------
        // Display the error
        //----------------------------------------------------------------------

        this->showErrorBox(messageBuf);

        //----------------------------------------------------------------------
        // Clear the error
        //----------------------------------------------------------------------

        SetLastError(0);
    }

}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::clearError()
Purpose:  Clears the error state with the operating system.
Params:   None
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::clearError()
{
    // Clear the error state with windows

    SetLastError(0);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::beginDraw()
Purpose:  Starts a drawing session by clearing the screen.
Params:   RGB color to clear to.
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::clear( char clearcol_r , char clearcol_g , char clearcol_b ) const
{
     // Structure to hold brush and pen for clearing the screen with

     static LOGBRUSH clearBrush = { BS_SOLID , 0x00ffffff , 0  };
     static LOGPEN   clearPen   = { PS_SOLID , 1  , 0x00ffffff };

     // Set the color of the clear brush and pen

     clearBrush.lbColor = RGB(clearcol_r,clearcol_g,clearcol_b);
     clearPen.lopnColor = RGB(clearcol_r,clearcol_g,clearcol_b);

     // Create the brush and pen with windows

     HBRUSH hClearBrush = CreateBrushIndirect(&clearBrush);
     HPEN   hClearPen   = CreatePenIndirect(&clearPen);

     // Select the newly created brush and pen

     SelectObject(memoryDeviceContext,hClearBrush);
     SelectObject(memoryDeviceContext,hClearPen);

     // Clear the screen

     drawRectangle(0,0,getClientWidth(),getClientHeight());

     // Delete the old brush and pen now that we are done

     DeleteObject(hClearBrush);
     DeleteObject(hClearPen);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::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:     Window::endDraw()
Purpose:  Finishes up the drawing session by copying the backbuffer contents into
          the front buffer.
Params:   None
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::swapBuffers()
{
    // Clear any previous errors

    clearError();

    // Copy the memory device context(backbuffer) contents onto the
    // screen (the device context or front buffer)

    BitBlt
    (
        deviceContext,          // Destination dc
        0,                      // Destination rect left x
        0,                      // Destination rect top y
        getClientWidth(),       // Destination rect width
        getClientHeight(),      // Destination rect height
        memoryDeviceContext,    // Source dc
        0,                      // Source dc left x
        0,                      // Source dc top  y
        SRCCOPY	                // Raster op: direct pixel copy
    );

    // Check for a bit blit error

    checkError("Blit back buffer to front buffer");
}


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setResizeable( bool sizeable )
{
    // Get last window style by setting a new window style

    int lastWindowStyle = SetWindowLong( windowHandle , GWL_STYLE , 0 );

    // 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

    SetWindowLong( windowHandle , GWL_STYLE , lastWindowStyle );

    // Hack: for some reason the window doesn't redisplay itself correctly
    // after resizing. repainting doesn't seem to work but after the user drags
    // the window a few times or re-gains focus the display is ok.
    // If this function is called however, everything seems to be fine. Why?

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::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   // Retain z order and position
    );
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::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 Window::setClientSize ( int clientWidth , int clientHeight )
{
    // Abort if invalid dimensions given

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

    // Get the window style by setting it:

    int windowStyle = SetWindowLong(windowHandle,GWL_STYLE,WS_OVERLAPPEDWINDOW);

    SetWindowLong(windowHandle,GWL_STYLE,windowStyle);

    // Get the extended window style by setting it:

    int windowStyleEx = SetWindowLong(windowHandle,GWL_EXSTYLE,WS_EX_OVERLAPPEDWINDOW);

    SetWindowLong(windowHandle,GWL_EXSTYLE,windowStyleEx);

    // 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);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::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:     Window::setCursor()
Purpose:  Attempts to set the window cursor: will not work if cursor is not over window
Params:   Cursor type
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setCursor ( CursorType 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:     Window::getX()
Purpose:  Get the window position - x
Params:   None
Returns:  X position of window

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

int Window::getX() const
{
    // Store window info here

    static WINDOWPLACEMENT winPlacement;

    // Get window info

    GetWindowPlacement(windowHandle,&winPlacement);

    // Return x position

    return(winPlacement.rcNormalPosition.left);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

int Window::getY() const
{
    // Store window info here

    static WINDOWPLACEMENT winPlacement;

    // Get window info

    GetWindowPlacement(windowHandle,&winPlacement);

    // Return x position

    return(winPlacement.rcNormalPosition.top);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

int Window::getWidth() const
{
    // Store window info here

    static WINDOWPLACEMENT winPlacement;

    // Get window info

    GetWindowPlacement(windowHandle,&winPlacement);

    // Return width

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

int Window::getHeight() const
{
    // Store window info here

    static WINDOWPLACEMENT winPlacement;

    // Get window info

    GetWindowPlacement(windowHandle,&winPlacement);

    // Return Height

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

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

    static RECT clientRect;

    // Get window info

    GetClientRect(windowHandle,&clientRect);

    // Return client width

    return(clientRect.right);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

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

    static RECT clientRect;

    // Get window info

    GetClientRect(windowHandle,&clientRect);

    // Return client height

    return(clientRect.bottom);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

bool Window::isFullscreen() const
{
    return fullscreenMode;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::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:     setFullscreen()
Purpose:  sets the window into fullscreen mode and back..
Params:   fullscreen, client width and height of fullscreen/windowed display
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFullscreen( bool fullscreen , int resolution_x , int resolution_y )
{
     // Declare and fill in some of the fields in the DEVMODE struct

     DEVMODE deviceMode = {0};

     // Get current display settings from windows

     EnumDisplaySettings
     (
        NULL,                       // use device that calling thread uses
        ENUM_CURRENT_SETTINGS,      // get the current settings
        &deviceMode                 // where to save these settings
     );

     // Fill in the rest of the settings manually

     deviceMode.dmSize              = sizeof(DEVMODE);
     deviceMode.dmBitsPerPel        = 32;
     deviceMode.dmPelsWidth	        = resolution_x;
     deviceMode.dmPelsHeight        = resolution_y;

     // store outcome of display change here

     int changeResult = 0;

     // See if fullscreen is set to true

     if ( fullscreen )
     {
          // Use the width, height and bpp values if switch to fullscreen

          deviceMode.dmFields |= DM_BITSPERPEL;
          deviceMode.dmFields |= DM_PELSWIDTH;
          deviceMode.dmFields |= DM_PELSHEIGHT;

          // 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;
          }
     }
     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_VISIBLE );
              SetWindowLong( windowHandle, GWL_EXSTYLE , WS_EX_OVERLAPPEDWINDOW );

              // 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;
          }

     }

     // Print an error message if the change wasn't successful

     switch ( changeResult )
     {
       case DISP_CHANGE_RESTART    : PROGRAM_FAULT("Window::setFullscreen() - Computer must be restarted to change display mode.");break;
       case DISP_CHANGE_BADFLAGS   : PROGRAM_FAULT("Window::setFullscreen() - Invalid set of display flags passed in.");break;
       case DISP_CHANGE_FAILED	   : PROGRAM_FAULT("Window::setFullscreen() - Failed to set specified display mode");break;
       case DISP_CHANGE_BADMODE	   : PROGRAM_FAULT("Window::setFullscreen() - Unsupported display mode");break;
       case DISP_CHANGE_NOTUPDATED : PROGRAM_FAULT("Window::setFullscreen() - Unable to write display settings to the registry");break;
     }

}


/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::loadBitmapFromData
Purpose:  Creates a bitmap from a user array of 32-bit pixels in the ARGB format
Params:   Pointer to pixels, width and height of image, if image is transparent
Returns:  Pointer to a bitmap

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

Bitmap * Window::loadBitmap(const unsigned int * data, int width, int height, bool transparent)
{
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Load bitmap
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // Create a bitmap object

    Bitmap * bitmap = new Bitmap();

    // Zero bitmap memory

    memset(bitmap,0,sizeof(Bitmap));

    // If the pointer to the data is null then abort

    if ( data == 0 ) return bitmap;

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Fill in the bitmap info manually
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    bitmap->bitmapInfo.bmWidth          = width;                    // width of image
    bitmap->bitmapInfo.bmHeight         = height;                   // height of image
    bitmap->bitmapInfo.bmWidthBytes     = width * sizeof(int);      // size of each scanline in bytes
    bitmap->bitmapInfo.bmPlanes         = 1;                        // number of color planes
    bitmap->bitmapInfo.bmBitsPixel      = 32;                       // number of bits per pixel
    bitmap->bitmapInfo.bmBits           = (void*)data;              // pointer to the bitmap data

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Save a local copy of the bitmap bits in the bitmap object for future retrieval
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // Allocate memory for the bitmap bits

    bitmap->bitmapBits = new unsigned int[width*height];

    // If allocation failed raise a serious error:

    if ( bitmap->bitmapBits == 0 ) PROGRAM_ERROR("Not enough memory to hold bitmap!");

    // Copy the data passed in to the bitmap object

    memcpy(bitmap->bitmapBits,data,sizeof(unsigned int)*width*height);

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Create the windows GDI bitmap
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // Create the bitmap indirectly using the bitmap info struct

    bitmap->bitmapHandle = CreateBitmapIndirect(&(bitmap->bitmapInfo));

    // If the bitmap wasn't successfully created then return:

    if ( bitmap->bitmapHandle == NULL ) return bitmap;

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Make GDI bitmap transparent if transparent is specified
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // If bitmap is transparent acreate the mask:

    if ( transparent == true )
    {
        // Hold the transparent color here: 0xff000000 signifies that it isn't transparent

        unsigned int transparentColor = 0xff000000;

        // Create a black and white bitmap that will serve as the mask

        bitmap->bitmapMaskHandle = CreateBitmap
        (
          width,                 // width of bitmap
          height,                // height of bitmap
          1,                     // Number of color planes
          1,                     // Number of bits used per pixel
          NULL                   // No initial bitmap data: random values instead
        );

        // Create a temporary memory device context for the original bitmap compatible with the screen
        // NULL means just make compatible with screen DC...

        HDC tempDC1 = CreateCompatibleDC(NULL);

        // Create another temporary device context for the mask

        HDC tempDC2 = CreateCompatibleDC(NULL);

        // Select the original bitmap into DC1 and the mask into DC2

        SelectObject(tempDC1,bitmap->bitmapHandle);
        SelectObject(tempDC2,bitmap->bitmapMaskHandle);

        // Get the transparent color: bottom right pixel of the image

        transparentColor = GetPixel(tempDC1,bitmap->getWidth()-1,bitmap->getHeight()-1);

        // Set the background color of DC1 to the bottom right pixel of the color bitmap.
        // When the color bmp is blitted onto the black and white mask, the transparent
        // color will be blitted as a 1 bit, all other colors will be a 0 bit:

        SetBkColor( tempDC1, transparentColor );

        // Blit from the color bitmap to the black and white bitmap (mask) and thus
        // create the mask for determining which pixels are transparent and opaque:

         BitBlt
         (
           tempDC2,                 // Dest dc
           0,                       // position in destination dc
           0,                       // position in destination dc
           width,                   // destination rect size
           height,                  // destination rect size
           tempDC1,                 // Source dc
           0,                       // Upper left corner of source: x
           0,                       // Upper left corner of source: y
           SRCCOPY	                // Direct pixel copy
         );

        // Set the background color of DC1 to white. We want the background area
        // (1 bits) to be blitted as white onto the color image. If this color is
        // not set then the blit result is indeterminable...

        SetBkColor(tempDC1,0x00ffffff);

        // Set all transparent pixels in the color image to white by doing a boolean OR
        // of the mask with the color image:

         BitBlt
         (
           tempDC1,                 // Dest dc
           0,                       // position in destination dc
           0,                       // position in destination dc
           width,                   // destination rect size
           height,                  // destination rect size
           tempDC2,                 // Source dc
           0,                       // Upper left corner of source: x
           0,                       // Upper left corner of source: y
           SRCPAINT				    // Do a boolean OR of soruce with the dest image
         );

        // Invert the mask so that the transparent parts are black: this will be n
        // needed for when drawing:

         BitBlt
         (
           tempDC2,             // Dest dc
           0,                   // position in destination dc
           0,                   // position in destination dc
           width,               // destination rect size
           height,              // destination rect size
           tempDC2,             // Source dc
           0,                   // Upper left corner of source: x
           0,                   // Upper left corner of source: y
           DSTINVERT		    // Invert the colors of the image
         );

        // Cleanup by deleting the two temporary DCs

        DeleteDC(tempDC1);
        DeleteDC(tempDC2);

    } // end if ( transparent)

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Return pointer to the newly created bitmap
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    return bitmap;

}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::loadBitmap
Purpose:  Creates a bitmap and loads image from file
Params:   file to load, if the bitmap is transparent (bottom right pixel is used as transparent color)
Returns:  Pointer to a bitmap

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

Bitmap * Window::loadBitmap( char * fileName, bool transparent )
{
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Load bitmap
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // Create a bitmap object

    Bitmap * bitmap = new Bitmap();

    // Zero bitmap memory

    memset(bitmap,0,sizeof(Bitmap));

    // If file name is null then abort

    if ( fileName == 0 ) return bitmap;

    // Load the specified bitmap:

    bitmap->bitmapHandle = (HBITMAP)LoadImage
    (
        programHandle,               // module handle
        fileName,                    // name of file to load
        IMAGE_BITMAP,                // image type
        0,                           // image width:  use normal image width
        0,                           // image height: use normal image height
        LR_LOADFROMFILE	             // load from file, not resource
    );

    // If the loading failed then abort and just return bitmap pointer

    if ( bitmap->bitmapHandle == NULL ) return bitmap;

    // Get the bitmap info from windows and save in the BITMAP struct

    GetObject(bitmap->bitmapHandle,sizeof(BITMAP),&bitmap->bitmapInfo);

    // Check for a windows error

    checkError(fileName);

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Load a local copy of the bitmap bits and save in the bitmap object
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // Declare an array to hold bitmap data

    bitmap->bitmapBits = new unsigned int[bitmap->getWidth()*bitmap->getHeight()];

    // If memory allocation failed then raise a serious error

    if ( bitmap->bitmapBits == 0 ) PROGRAM_ERROR("Cannot allocate enough memory for bitmap!");

    // Bitmap info struct to hold desired format for bitmap data

    BITMAPINFO bitmapInfo = {0};

    // Set the bitmap info header fields

    bitmapInfo.bmiHeader.biSize         = sizeof(BITMAPINFOHEADER);                             // size of bitmap info header struct
    bitmapInfo.bmiHeader.biWidth        = bitmap->getWidth();                                   // width of bitmap
    bitmapInfo.bmiHeader.biHeight       = -bitmap->getHeight();                                 // height of bitmap: must be negative for a top down DI
    bitmapInfo.bmiHeader.biPlanes       = 1;                                                    // num color planes is always 1
    bitmapInfo.bmiHeader.biBitCount     = 32;                                                   // number of bits used per pixel
    bitmapInfo.bmiHeader.biCompression  = BI_RGB;                                               // uncompressed bitmap format
    bitmapInfo.bmiHeader.biSizeImage    = bitmap->getWidth()*bitmap->getHeight()*sizeof(int);   // size in bytes of the image
    bitmapInfo.bmiHeader.biXPelsPerMeter= bitmap->getWidth();                                   // pixels (width) per meter? just use bmp width
    bitmapInfo.bmiHeader.biYPelsPerMeter= bitmap->getHeight();                                  // pixels (height) per meter? just use bmp height
    bitmapInfo.bmiHeader.biClrUsed      = 0;                                                    // how many possible color values are used? 0 means all
    bitmapInfo.bmiHeader.biClrImportant = 0;                                                    // number of colors that are important when displaying. 0 means all.

    // Get the bitmap bits from windows

    GetDIBits
        (
        deviceContext,                  // device context
        bitmap->bitmapHandle,           // handle to the bitmap
        0,                              // scan line to start on in destination
        bitmap->getHeight(),            // number of scanlines to copy
        bitmap->bitmapBits,             // array to copy bits to
        &bitmapInfo,                    // bitmap info structure
        DIB_RGB_COLORS	                // format of bmi colors member of bitmap structure
        );

    // Set alpha values of the texture

    {
        // Get the 'transparent' color: the bottom right pixel

        unsigned int transparentColor = bitmap->bitmapBits[ (bitmap->getHeight()-1) * (bitmap->getWidth()-1) ];

        // Pointer to current pixel

        unsigned int * pPixel = bitmap->bitmapBits;

        // Set transparency for every pixel

        if  ( transparent )
        {
            // Transparent bmp

            for ( int i = ( bitmap->getWidth() )*(bitmap->getHeight() ) ; i > 0 ; i-- )
            {

                // If transparent and pixel matches transparent color set alpha to 0
                // otherwise set a to 255

                if ( *pPixel == transparentColor )
                {
                    *pPixel &= 0x00ffffff;
                }
                else
                {
                    *pPixel |= 0xff000000;
                }

                // Move pointer onto next pixel

                *pPixel++;
            }

        }
        else
        {
            // Non transparent bmp

            for ( int i = ( bitmap->getWidth() )*(bitmap->getHeight() ) ; i > 0 ; i-- )
            {
                *pPixel++ |= 0xff000000;
            }
        }
    }

    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Make bitmap transparent if transparent is specified
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    // If bitmap is transparent then create the mask:

    if ( transparent == true )
    {

        // Hold the transparent color here: 0xff000000 signifies that it isn't transparent

        unsigned int transparentColor = 0xff000000;

        // Create a black and white bitmap that will serve as the mask

        bitmap->bitmapMaskHandle = CreateBitmap
        (
          bitmap->getWidth(),    // width of bitmap
          bitmap->getHeight(),   // height of bitmap
          1,                     // Number of color planes
          1,                     // Number of bits used per pixel
          NULL                   // No initial bitmap data: random values instead
        );

        // Check for a windows error

        checkError(fileName);

        // Create a temporary memory device context for the original bitmap compatible with the screen

        HDC tempDC1 = CreateCompatibleDC(deviceContext);

        // Create another temporary device context for the mask

        HDC tempDC2 = CreateCompatibleDC(deviceContext);

        // Select the original bitmap into DC1 and the mask into DC2

        SelectObject(tempDC1,bitmap->bitmapHandle);
        SelectObject(tempDC2,bitmap->bitmapMaskHandle);

        // Get the transparent color: bottom right pixel of the image

        transparentColor = GetPixel(tempDC1,bitmap->getWidth()-1,bitmap->getHeight()-1);

        // Set the background color of DC1 to the bottom right pixel of the color bitmap.
        // When the color bmp is blitted onto the black and white mask, the transparent
        // color will be blitted as a 1 bit, all other colors will be a 0 bit:

        SetBkColor( tempDC1, transparentColor );

        // Blit from the color bitmap to the black and white bitmap (mask) and thus
        // create the mask for determining which pixels are transparent and opaque:

         BitBlt
         (
           tempDC2,                 // Dest dc
           0,                       // position in destination dc
           0,                       // position in destination dc
           bitmap->getWidth(),      // destination rect size
           bitmap->getHeight(),     // destination rect size
           tempDC1,                 // Source dc
           0,                       // Upper left corner of source: x
           0,                       // Upper left corner of source: y
           SRCCOPY	                // Direct pixel copy
         );

        // Check for a windows error

        checkError(fileName);

        // Set the background color of DC1 to white. We want the background area
        // (1 bits) to be blitted as white onto the color image. If this color is
        // not set then the blit result is indeterminable...

        SetBkColor(tempDC1,0x00ffffff);

        // Set all transparent pixels in the color image to white by doing a boolean OR
        // of the mask with the color image:

         BitBlt
         (
           tempDC1,                 // Dest dc
           0,                       // position in destination dc
           0,                       // position in destination dc
           bitmap->getWidth(),      // destination rect size
           bitmap->getHeight(),     // destination rect size
           tempDC2,                 // Source dc
           0,                       // Upper left corner of source: x
           0,                       // Upper left corner of source: y
           SRCPAINT				    // Do a boolean OR of soruce with the dest image
         );

        // Check for a windows error

        checkError(fileName);

        // Invert the mask so that the transparent parts are black: this will be n
        // needed for when drawing:

         BitBlt
         (
           tempDC2,             // Dest dc
           0,                   // position in destination dc
           0,                   // position in destination dc
           bitmap->getWidth(),  // destination rect size
           bitmap->getHeight(), // destination rect size
           tempDC2,             // Source dc
           0,                   // Upper left corner of source: x
           0,                   // Upper left corner of source: y
           DSTINVERT		    // Invert the colors of the image
         );

        // Check for a windows error

        checkError(fileName);

        // Cleanup by deleting the two temporary DCs

        DeleteDC(tempDC1);
        DeleteDC(tempDC2);

    } // end if ( transparent)


    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    // Return pointer to the newly created bitmap
    //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    return bitmap;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

bool Window::showQuestionBox( char * question , char * title )
{
    // Set showing dialog box flag to true

    showingDialogBox = true;

    // Show the message box

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

    // Set showing dialog box flag to false

    showingDialogBox = false;

    // Return answer

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::showMessageBox( char * message , char * title )
{
    // Set showing dialog box flag to true

    showingDialogBox = true;

    // Show the message box

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

    // Set showing dialog box flag to false

    showingDialogBox = false;

}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::showErrorBox()
Purpose:  Shows a warning box displaying an error message
Params:   none
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::showErrorBox( char * message )
{
    // Set showing dialog box flag to true

    showingDialogBox = true;

    // Show the message box

    MessageBox
    (
        windowHandle,
        message,
        "A non fatal program error has occured",
        MB_OK | MB_ICONWARNING | MB_TOPMOST | MB_APPLMODAL | MB_SETFOREGROUND
    );

    // Set showing dialog box flag to false

    showingDialogBox = false;

}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawPixel()
Purpose:  Puts a pixel to the screen
Params:   X,Y position on the screen, RGB components of the pixel color
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawPixel( int x, int y, char r, char g, char b ) const
{
     // Use the Win32 function to draw the pixel

     SetPixel
     (
       memoryDeviceContext,
       x,
       y,
       RGB(r,g,b)
     );
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawBitmap()
Purpose:  Draws a bitmap to the screen (either solid or transparent)
Params:   Bitmap, x and y position.
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawBitmap(Bitmap * bitmap , int x , int y) const
{
     // If bitmap pointer is invalid then abort

     if ( bitmap == NULL ) return;

     // If invalid bitmap handle then abort

     if ( bitmap->bitmapHandle == NULL ) return;

     // See if bitmap is transparent:

     if ( bitmap->isTransparent() )
     {
         // Select the bitmap mask into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapMaskHandle);

         // For some reason the text color of the memory dc is used as the background
         // color of the mask. change to black and save old color.

         int textColor = SetTextColor(memoryDeviceContext,0);

         // Transparent BMP: first blit the mask onto the backbuffer using bitwise OR. Solid parts
         // will be white and translucent parts black:

         BitBlt
         (
           memoryDeviceContext, // Dest dc
           x,                   // position in destination dc
           y,                   // position in destination dc
           bitmap->getWidth(),  // destination rect size
           bitmap->getHeight(), // destination rect size
           bitmapDeviceContext, // Source dc
           0,                   // Upper left corner of source: x
           0,                   // Upper left corner of source: y
           SRCPAINT		        // Bitwise OR with destination
         );

         // Select the color bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Blit the color bmp onto the backbuffer using a bitwise AND. A transparent sprite will result.

         BitBlt
         (
           memoryDeviceContext, // Dest dc
           x,                   // position in destination dc
           y,                   // position in destination dc
           bitmap->getWidth(),  // destination rect size
           bitmap->getHeight(), // destination rect size
           bitmapDeviceContext, // Source dc
           0,                   // Upper left corner of source: x
           0,                   // Upper left corner of source: y
           SRCAND			    // Bitwise AND with destination
         );

         // Restore previous text color

         SetTextColor(memoryDeviceContext,textColor);

    }
    else
    {
         // Solid BMP: Select the bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Bit blit from the memory device context to the screen

         BitBlt
         (
           memoryDeviceContext, // Dest dc
           x,                   // position in destination dc
           y,                   // position in destination dc
           bitmap->getWidth(),  // destination rect size
           bitmap->getHeight(), // destination rect size
           bitmapDeviceContext, // Source dc
           0,                   // Upper left corner of source: x
           0,                   // Upper left corner of source: y
           SRCCOPY	            // Direct pixel copy
         );
    }
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawBitmap()
Purpose:  Draws a scaled bitmap to the screen (either solid or transparent)
          OPTIMISATION FOR GAME: now only draws the portion of the image which
          is onscreen. stops the game grounding to a near halt when we get close
          to trees and such.
Params:   Bitmap, x and y position.
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawBitmap(Bitmap * bitmap , int x , int y , int width , int height ) const
{
     //-------------------------------------------------------------------------
     // Validate params first
     //-------------------------------------------------------------------------

     // If bitmap pointer is invalid then abort

     if ( bitmap == NULL ) return;

     // If invalid bitmap handle then abort

     if ( bitmap->bitmapHandle == NULL ) return;

     // If invalid output scale then abort

     if ( width < 1 || height < 1 ) return;

     //-------------------------------------------------------------------------
     // Drawing coordinates: may be modified depending if certain parts of the
     // bitmap are offscreen in order to save on fill rate. windows must only
     // do per pixel clipping because when drawing scaled images the frame rate
     // slows down enormously.. this code whilst not producing the most accurate
     // results, stops the game from grounding to a halt..
     //-------------------------------------------------------------------------

     int src_x = 0;                     // source bitmap: x position of part to copy from in the image
     int src_y = 0;                     // source bitmap: y position of part to copy from in the image
     int src_w = bitmap->getWidth();    // source bitmap: width  of part to copy from image
     int src_h = bitmap->getHeight();   // source bitmap: height of part to copy from image

     int dst_x = x;         // output x position
     int dst_y = y;         // output y position
     int dst_w = width;     // output width
     int dst_h = height;    // output height

     //-------------------------------------------------------------------------
     // Check to see if dst_x is past left of screen
     //-------------------------------------------------------------------------

     if ( dst_x < 0 )
     {
         // Outside of screen: modify coordinates

        src_x -= int( float(dst_x) * ( float(src_w) / float(dst_w) ) ); // modify source x accordingly: taking into account original image scale wrt output scale
        src_w += int( float(dst_x) * ( float(src_w) / float(dst_w) ) ); // modify source w accordingly: taking into account original image scale wrt output scale
        dst_w += dst_x;                                                 // modify dest   w accordingly

        // If the source width is now zero or less then abort drawing

        if ( src_w <= 0 ) return;

        // clamp the destination x to 0

        dst_x = 0;
     }

     //-------------------------------------------------------------------------
     // Check to see if dst_y is past top of screen
     //-------------------------------------------------------------------------

     if ( dst_y < 0 )
     {
         // Outside of screen: modify coordinates

        src_y -= int( float(dst_y) * ( float(src_h) / float(dst_h) ) ); // modify source y accordingly: taking into account original image scale wrt output scale
        src_h += int( float(dst_y) * ( float(src_h) / float(dst_h) ) ); // modify source h accordingly: taking into account original image scale wrt output scale
        dst_h += dst_y;                                                 // modify dest   h accordingly

        // If the source height is now zero or less then abort drawing

        if ( src_h <= 0 ) return;

        // clamp the destination y to 0

        dst_y = 0;
     }

     //-------------------------------------------------------------------------
     // Check to see if dst_y + dst_h is past bottom of screen
     //-------------------------------------------------------------------------

     // Get amount the image is offscreen: will be negative or zero if not off screen

     int offscr_amt_y = dst_y + dst_h - getClientHeight();

     // Now see if the image is offscreen

     if ( offscr_amt_y > 0 )
     {
         src_h -= int( float(offscr_amt_y) * ( float(src_h) / float(dst_h) ) );   // modify source h accordingly: taking into account original image scale wrt output scale
         dst_h -= offscr_amt_y;                                                   // modify dest h accordingly
     }

     //-------------------------------------------------------------------------
     // Check to see if dst_x + dst_w is past right of screen
     //-------------------------------------------------------------------------

     // Get amount the image is offscreen: will be negative or zero if not off screen

     int offscr_amt_x = dst_x + dst_w - getClientWidth();

     // Now see if the image is offscreen

     if ( offscr_amt_x > 0 )
     {
         src_w -= int( float(offscr_amt_x) * ( float(src_w) / float(dst_w) ) );   // modify source w accordingly: taking into account original image scale wrt output scale
         dst_w -= offscr_amt_x;                                                   // modify dest w accordingly
     }

     //-------------------------------------------------------------------------
     // Draw the bitmap
     //-------------------------------------------------------------------------

     // See if bitmap is transparent:

     if ( bitmap->isTransparent() )
     {
         // Select the bitmap mask into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapMaskHandle);

         // For some reason the text color of the memory dc is used as the background
         // color of the mask. change to black and save old color.

         int textColor = SetTextColor(memoryDeviceContext,0);

         // Transparent BMP: first blit the mask onto the backbuffer using bitwise OR. Solid parts
         // will be white and translucent parts black:

         StretchBlt
         (
           memoryDeviceContext,     // Dest dc
           dst_x,                   // position in destination dc
           dst_y,                   // position in destination dc
           dst_w,                   // destination rect size
           dst_h,                   // destination rect size
           bitmapDeviceContext,     // Source dc
           src_x,                   // Upper left corner of source: x
           src_y,                   // Upper left corner of source: y
           src_w,                   // Width of source rectangle
           src_h,                   // Height of source rectangle
           SRCPAINT		            // Bitwise OR with destination
         );

         // Select the color bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Blit the color bmp onto the backbuffer using a bitwise AND. A transparent sprite will result.

         StretchBlt
         (
           memoryDeviceContext,     // Dest dc
           dst_x,                   // position in destination dc
           dst_y,                   // position in destination dc
           dst_w,                   // destination rect size
           dst_h,                   // destination rect size
           bitmapDeviceContext,     // Source dc
           src_x,                   // Upper left corner of source: x
           src_y,                   // Upper left corner of source: y
           src_w,                   // Width of source rectangle
           src_h,                   // Height of source rectangle
           SRCAND			        // Bitwise AND with destination
         );

         // Restore previous text color

         SetTextColor(memoryDeviceContext,textColor);
    }
    else
    {
         // Solid BMP: Select the bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Bit blit from the memory device context to the screen

         StretchBlt
         (
           memoryDeviceContext,     // Dest dc
           dst_x,                   // position in destination dc
           dst_y,                   // position in destination dc
           dst_w,                   // destination rect size
           dst_h,                   // destination rect size
           bitmapDeviceContext,     // Source dc
           src_x,                   // Upper left corner of source: x
           src_y,                   // Upper left corner of source: y
           src_w,                   // Width of source rectangle
           src_h,                   // Height of source rectangle
           SRCCOPY	                // Direct pixel copy
         );
    }
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawBitmapSection()
Purpose:  Draws a specified setion of a bitmap to the screen (either solid or transparent)
Params:   Bitmap, x and y position, x/y position of section in source bitmap
          width and height of section in source bitmap
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawBitmapSection
(
    class Bitmap * bitmap,
    int destination_x,
    int destination_y,
    int source_x,
    int source_y,
    int source_width,
    int source_height

) const
{
     // If bitmap pointer is invalid then abort

     if ( bitmap == NULL ) return;

     // If invalid bitmap handle then abort

     if ( bitmap->bitmapHandle == NULL ) return;

     // Clamp source x and y to bitmap size

     source_x = source_x <  0 ? 0 : source_x;
     source_y = source_y <  0 ? 0 : source_y;
     source_x = source_x >= bitmap->getWidth () ? bitmap->getWidth()-1  : source_x;
     source_y = source_y >= bitmap->getHeight() ? bitmap->getHeight()-1 : source_y;

     // Make sure width and height of section do not over extend the bitmap

     if ( source_x + source_width  > bitmap->getWidth () ) source_width  = bitmap->getWidth()  - source_x;
     if ( source_y + source_height > bitmap->getHeight() ) source_height = bitmap->getHeight() - source_y;

     // See if bitmap is transparent:

     if ( bitmap->isTransparent() )
     {
         // Select the bitmap mask into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapMaskHandle);

         // For some reason the text color of the memory dc is used as the background
         // color of the mask. change to black and save old color.

         int textColor = SetTextColor(memoryDeviceContext,0);

         // Transparent BMP: first blit the mask onto the backbuffer using bitwise OR. Solid parts
         // will be white and translucent parts black:

         BitBlt
         (
           memoryDeviceContext, // Dest dc
           destination_x,       // position in destination dc
           destination_y,       // position in destination dc
           source_width,        // destination rect size
           source_height,       // destination rect size
           bitmapDeviceContext, // Source dc
           source_x,            // Upper left corner of source: x
           source_y,            // Upper left corner of source: y
           SRCPAINT		        // Bitwise OR with destination
         );

         // Select the color bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Blit the color bmp onto the backbuffer using a bitwise AND. A transparent sprite will result.

         BitBlt
         (
           memoryDeviceContext, // Dest dc
           destination_x,       // position in destination dc
           destination_y,       // position in destination dc
           source_width,        // destination rect size
           source_height,       // destination rect size
           bitmapDeviceContext, // Source dc
           source_x,            // Upper left corner of source: x
           source_y,            // Upper left corner of source: y
           SRCAND			    // Bitwise AND with destination
         );

         // Restore previous text color

         SetTextColor(memoryDeviceContext,textColor);

    }
    else
    {
         // Solid BMP: Select the bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Bit blit from the memory device context to the screen

         BitBlt
         (
           memoryDeviceContext, // Dest dc
           destination_x,       // position in destination dc
           destination_y,       // position in destination dc
           source_width,        // destination rect size
           source_height,       // destination rect size
           bitmapDeviceContext, // Source dc
           source_x,            // Upper left corner of source: x
           source_y,            // Upper left corner of source: y
           SRCCOPY	            // Direct pixel copy
         );
    }

}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawBitmap()
Purpose:  Draws a specified section of the bitmap,scaled, to the screen (either solid or transparent)
Params:   Bitmap, x and y position. Start of section x/y, width/height of section,
          output width and height
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawBitmapSection
(
    class Bitmap * bitmap,
    int destination_x,
    int destination_y,
    int destination_width,
    int destination_height,
    int source_x,
    int source_y,
    int source_width,
    int source_height

) const
{
     // If bitmap pointer is invalid then abort

     if ( bitmap == NULL ) return;

     // If invalid bitmap handle then abort

     if ( bitmap->bitmapHandle == NULL ) return;

     // If invalid output scale then abort

     if ( destination_width < 1 || destination_height < 1 ) return;

     // Clamp source x and y to bitmap size

     source_x = source_x <  0 ? 0 : source_x;
     source_y = source_y <  0 ? 0 : source_y;
     source_x = source_x >= bitmap->getWidth () ? bitmap->getWidth()-1  : source_x;
     source_y = source_y >= bitmap->getHeight() ? bitmap->getHeight()-1 : source_y;

     // Make sure width and height of section do not over extend the bitmap

     if ( source_x + source_width  > bitmap->getWidth () ) source_width  = bitmap->getWidth()  - source_x;
     if ( source_y + source_height > bitmap->getHeight() ) source_height = bitmap->getHeight() - source_y;

     // See if bitmap is transparent:

     if ( bitmap->isTransparent() )
     {
         // Select the bitmap mask into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapMaskHandle);

         // For some reason the text color of the memory dc is used as the background
         // color of the mask. change to black and save old color.

         int textColor = SetTextColor(memoryDeviceContext,0);

         // Transparent BMP: first blit the mask onto the backbuffer using bitwise OR. Solid parts
         // will be white and translucent parts black:

         StretchBlt
         (
           memoryDeviceContext,     // Dest dc
           destination_x,           // position in destination dc
           destination_y,           // position in destination dc
           destination_width,       // destination rect size
           destination_height,      // destination rect size
           bitmapDeviceContext,     // Source dc
           source_x,                // Upper left corner of source: x
           source_y,                // Upper left corner of source: y
           source_width,            // Width of source rectangle
           source_height,           // Height of source rectangle
           SRCPAINT		            // Bitwise OR with destination
         );

         // Select the color bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Blit the color bmp onto the backbuffer using a bitwise AND. A transparent sprite will result.

         StretchBlt
         (
           memoryDeviceContext,     // Dest dc
           destination_x,           // position in destination dc
           destination_y,           // position in destination dc
           destination_width,       // destination rect size
           destination_height,      // destination rect size
           bitmapDeviceContext,     // Source dc
           source_x,                // Upper left corner of source: x
           source_y,                // Upper left corner of source: y
           source_width,            // Width of source rectangle
           source_height,           // Height of source rectangle
           SRCAND			        // Bitwise AND with destination
         );

         // Restore previous text color

         SetTextColor(memoryDeviceContext,textColor);

    }
    else
    {
         // Solid BMP: Select the bitmap into the bitmap dc

         SelectObject(bitmapDeviceContext,bitmap->bitmapHandle);

         // Bit blit from the memory device context to the screen

         StretchBlt
         (
           memoryDeviceContext,     // Dest dc
           destination_x,           // position in destination dc
           destination_y,           // position in destination dc
           destination_width,       // destination rect size
           destination_height,      // destination rect size
           bitmapDeviceContext,     // Source dc
           source_x,                // Upper left corner of source: x
           source_y,                // Upper left corner of source: y
           source_width,            // Width of source rectangle
           source_height,           // Height of source rectangle
           SRCCOPY	                // Direct pixel copy
         );
    }
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawText()
Purpose:  Draws the specified text (single-line) at specified position
Params:   Text,x position,y position
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawText(char * text , int x , int y )
{
    // If no text then abort

    if ( text == NULL ) return;

    // Draw the text

    TextOut
    (
        memoryDeviceContext,  // device context
        x,                    // X position
        y,                    // Y position
        text,                 // String
        strlen(text)          // Number of chars to draw
    );
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawText()
Purpose:  Draws the specified text (multi-line) at specified position, within
          rectangle boundarys and with selected alignment.
Params:   Text,text rectangle position and size, alignment mode
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawText(char * text , int x , int y , int rect_width , int rect_height , TextAlignmentMode alignment )
{
    // If no text then abort

    if ( text == NULL ) return;

    // If rectangle dimensions invalid then abort

    if ( rect_width <= 0 || rect_height <= 0 ) return;

    // If invalid text alignment mode then default to left

    if ( alignment < TEXT_ALIGNMENT_LEFT || alignment > TEXT_ALIGNMENT_RIGHT ) alignment = TEXT_ALIGNMENT_LEFT;

    // Construct a rectangle holding the specified text rectangle dimensions

    static RECT textRectangle;

    textRectangle.left   = x;
    textRectangle.top    = y;
    textRectangle.right  = rect_width;
    textRectangle.bottom = rect_height;

    // Draw the text

    DrawText
    (
        memoryDeviceContext,  // device context
        text,                 // text to draw
        strlen(text),         // lenght of text
        &textRectangle,       // boundary to draw within
        alignment |           // flags: alignment mode
        DT_NOPREFIX	|         // do no menomic processing: no special formatting directives within the text!
        DT_WORDBREAK          // If a word extends past the boundary move onto next line
    );
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawTextCentered()
Purpose:  Draws the specified text (single-line) at specified y position, centred horizontally
Params:   Text,y position
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawTextCentered(char * text , int y )
{
    // Get width of the drawing area

    int clientWidth = getClientWidth();

    // Get width of text to be drawn

    int textWidth = getTextWidth(text);

    // Draw the text

    TextOut
    (
        memoryDeviceContext,              // device context
        clientWidth/2 - textWidth/2,      // X position
        y,                                // Y position
        text,                             // String
        strlen(text)                      // Number of chars to draw
    );
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawTextCentered()
Purpose:  Draws the specified text (single-line) centred horizontally & vertically
Params:   Text
Returns:  None

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawTextCentered(char * text)
{
    // Get width and height of the drawing area

    int clientWidth  = getClientWidth();
    int clientHeight = getClientHeight();

    // Get width of text to be drawn

    int textWidth  = getTextWidth(text);
    int textHeight = getTextHeight(text);

    // Draw the text

    TextOut
    (
        memoryDeviceContext,              // device context
        clientWidth /2 - textWidth /2,    // X position
        clientHeight/2 - textHeight/2,    // Y position
        text,                             // String
        strlen(text)                      // Number of chars to draw
    );
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFontColor()
Purpose:  Sets the drawing color for text
Params:   Red,green and blue values
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFontColor(char r,char g,char b)
{
    SetTextColor(memoryDeviceContext,RGB(r,g,b));
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFontSize()
Purpose:  Sets the size of the text
Params:   Width and height
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFontSize (int width , int height )
{
     // Make sure width and height are OK!

     if ( width < 1 || height < 1 ) return;

     // Delete old font

     DeleteObject(fontHandle);

     // Set new font settings

     font.lfWidth    = width;
     font.lfHeight   = height;

     // Register our logical font with windows

     fontHandle = CreateFontIndirect(&font);

     // Select new font

     SelectObject(memoryDeviceContext,fontHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFontWeight()
Purpose:  Sets the thickness of the text
Params:   Font weight
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFontWeight (int weight)
{
     // Clamp weight to 100-900 range allowed by windows

     weight = weight < 100 ? 100 : weight;
     weight = weight > 900 ? 900 : weight;

     // Delete old font

     DeleteObject(fontHandle);

     // Set new font weight

     font.lfWeight = weight;

     // Register our logical font with windows

     fontHandle = CreateFontIndirect(&font);

     // Select new font

     SelectObject(memoryDeviceContext,fontHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::getTextWidth()
Purpose:  Returns the width in pixels of a string of text with the current font
Params:   String of text
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

int Window::getTextWidth( char * text ) const
{
    // Check for null pointer first:

    if ( text == NULL ) return 0;

    // Store text size here

    static SIZE size;

    // Get the text size

    GetTextExtentPoint32
    (
      memoryDeviceContext,
      text,
      strlen(text),
      &size
    );

    // Return text width

    return size.cx;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::getTextHeight()
Purpose:  Returns the height in pixels of a string of text with the current font
Params:   String of text
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

int Window::getTextHeight( char * text ) const
{
    // Check for null pointer first:

    if ( text == NULL ) return 0;

    // Store text size here

    static SIZE size;

    // Get the text size

    GetTextExtentPoint32
    (
      memoryDeviceContext,
      text,
      strlen(text),
      &size
    );

    // Return text height

    return size.cy;
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFontName()
Purpose:  Sets the font face of the text
Params:   Font name
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFontName(char * fontName)
{
     // Return if null pointer

     if ( fontName == NULL ) return;

     // Return if lenght of name > 31 chars

     if ( strlen(fontName) > 31 ) return;

     // Delete old font

     DeleteObject(fontHandle);

     // Set font face name of our logical font

     strcpy_s(font.lfFaceName,32,fontName);

     // Register our logical font with windows

     fontHandle = CreateFontIndirect(&font);

     // Select new font

     SelectObject(memoryDeviceContext,fontHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFontItalic()
Purpose:  Sets whether font is italic
Params:   True/false
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFontItalic(bool italic)
{
     // Delete old font

     DeleteObject(fontHandle);

     // Set whether font is italic

     font.lfItalic = italic;

     // Register our logical font with windows

     fontHandle = CreateFontIndirect(&font);

     // Select new font

     SelectObject(memoryDeviceContext,fontHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFontUnderline()
Purpose:  Sets whether font is underlined
Params:   True/false
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFontUnderline(bool underlined)
{
     // Delete old font

     DeleteObject(fontHandle);

     // Set whether font is underlined

     font.lfUnderline = underlined;

     // Register our logical font with windows

     fontHandle = CreateFontIndirect(&font);

     // Select new font

     SelectObject(memoryDeviceContext,fontHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFontStrikeOut()
Purpose:  Sets whether font is struck out
Params:   True/false
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFontStrikeOut(bool strikeOut)
{
     // Delete old font

     DeleteObject(fontHandle);

     // Set whether font is stuck out

     font.lfStrikeOut = strikeOut;

     // Register our logical font with windows

     fontHandle = CreateFontIndirect(&font);

     // Select new font

     SelectObject(memoryDeviceContext,fontHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setLineWidth()
Purpose:  Set the thickness of the lines drawn
Params:   X1,Y1,X2,Y2- line coords
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setLineWidth(int width)
{
     // If width is invalid abort

     if ( width <= 0 ) return;

     // Delete old pen

     DeleteObject(penHandle);

     // Set pen thickness

     pen.lopnWidth.x = width;

     // Register our logical pen with windows

     penHandle = CreatePenIndirect(&pen);

     // Select new pen

     SelectObject(memoryDeviceContext,penHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setLineColor()
Purpose:  Set the color of lines drawn
Params:   red,green,blue values
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setLineColor(unsigned char r,unsigned char g,unsigned char b)
{
     // Delete old pen

     DeleteObject(penHandle);

     // Set pen color

     pen.lopnColor = RGB(r,g,b);

     // Register our logical pen with windows

     penHandle = CreatePenIndirect(&pen);

     // Select new pen

     SelectObject(memoryDeviceContext,penHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setLinePattern()
Purpose:  Set the style of the line pattern
Params:   line pattern
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setLinePattern( Window::LinePattern pattern)
{
     // Delete old pen

     DeleteObject(penHandle);

     // Set line pattern for the logical pen

     pen.lopnStyle = pattern;

     // Register our logical pen with windows

     penHandle = CreatePenIndirect(&pen);

     // Select new pen

     SelectObject(memoryDeviceContext,penHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFillMode()
Purpose:  Set the fill mode for shapes
Params:   Fill mode
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFillMode( Window::FillMode fillMode )
{
     // Delete old brush

     DeleteObject(brushHandle);

     // Set fill mode logical brush

     brush.lbStyle = fillMode;

     // Register our logical brush with windows

     brushHandle = CreateBrushIndirect(&brush);

     // Select new brush

     SelectObject(memoryDeviceContext,brushHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFillColor()
Purpose:  Set the fill color for shapes
Params:   Fill color: r,g,b values
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFillColor( char r , char g , char b )
{
     // Delete old brush

     DeleteObject(brushHandle);

     // Set color for logical brush

     brush.lbColor = RGB(r,g,b);

     // Register our logical brush with windows

     brushHandle = CreateBrushIndirect(&brush);

     // Select new brush

     SelectObject(memoryDeviceContext,brushHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::setFillPattern()
Purpose:  Set the fill pattern for shapes
Params:   Fill pattern type
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setFillPattern( Window::FillPattern fillPattern )
{
     // Delete old brush

     DeleteObject(brushHandle);

     // Set pattern style for the logical brush

     brush.lbHatch = fillPattern;

     // Register our logical brush with windows

     brushHandle = CreateBrushIndirect(&brush);

     // Select new brush

     SelectObject(memoryDeviceContext,brushHandle);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawLine()
Purpose:  Draws a line to the screen
Params:   X1,Y1,X2,Y2- line coords
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawLine(int x1,int y1,int x2,int y2) const
{
     // Move to start of line point

     MoveToEx(memoryDeviceContext,x1,y1,NULL);

     // Draw from start to end point

     LineTo(memoryDeviceContext,x2,y2);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawRectangle()
Purpose:  Draws a rectangle to the screen
Params:   Coords of top left and bottom right point
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawRectangle(int x1,int y1,int x2,int y2) const
{
   // Draw the rectangle

   Rectangle(memoryDeviceContext,x1,y1,x2,y2);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawEllipse()
Purpose:  Draws an ellipse to the screen
Params:   Coords of top left and bottom right point
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawEllipse(int x1,int y1,int x2,int y2) const
{
   // Draw the ellipse

   Ellipse(memoryDeviceContext,x1,y1,x2,y2);
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::drawPolygon()
Purpose:  Draws a polygon to the screen
Params:   Coordinates of the polygon x1,y1,x2,y2 etc.. , number of points
Returns:  none

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::drawPolygon(int xyCoords[],int numPoints) const
{
     // If pointer is invalid then abort

    if ( xyCoords == NULL ) return;

    // If number of points is invalid then abort

    if ( numPoints < 3 ) return;

    // Draw the polygon

    Polygon
    (
        memoryDeviceContext,
        (POINT*)(xyCoords),
        numPoints
    );

}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::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 Window::keyboardKeyPressed( Window::KeyboardKey key ) const
{
    // Return whether the key is pressed or not:
    // If key is down most significant bit is set.

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::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 Window::keyboardKeyPressed( char key ) const
{
    // Return whether the key is pressed or not:
    // If key is down most significant bit is set.

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

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Name:     Window::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 Window::mouseButtonPressed( Window::MouseButton button ) const
{
    // 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;
    }
}

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Various small state querying functions: self explanatory...

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

bool Window::exists() const { return IsWindow(windowHandle) != FALSE; }

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Various event handler setting functions: all expect pointers to appropriate functions

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

void Window::setEvent_resize      ( void (*function)(Window & window ) )                                      {this->event_resize    = function;}
void Window::setEvent_paint       ( void (*function)(Window & window ) )                                      {this->event_paint     = function;}
void Window::setEvent_closing     ( void (*function)(Window & window ) )                                      {this->event_closing   = function;}
void Window::setEvent_closed      ( void (*function)(Window & window ) )                                      {this->event_closed    = function;}
void Window::setEvent_keyDown     ( void (*function)(Window & window, Window::KeyboardKey key) )              {this->event_keyDown   = function;}
void Window::setEvent_keyUp       ( void (*function)(Window & window, Window::KeyboardKey key) )              {this->event_keyUp     = function;}
void Window::setEvent_mouseDown   ( void (*function)(Window & window, int x , int y , MouseButton button ) )  {this->event_mouseDown = function;}
void Window::setEvent_mouseUp     ( void (*function)(Window & window, int x , int y , MouseButton button ) )  {this->event_mouseUp   = function;}
void Window::setEvent_mouseMove   ( void (*function)(Window & window, int x , int y ) )                       {this->event_mouseMove = function;}

