/*
	Trig Wars.
	Copyright (c) 2008, 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.
*/

//=====================================================================================================================
/// @brief		Class representing a Win32 window with a DirectX9 display
//=====================================================================================================================

#include "Precompiled.h"
#include "Windows\Window.h"
#include "Windows\WindowEventListener.h"
#include "Graphics\Display.h"

using namespace std;
using namespace Game;

//=====================================================================================================================
// Variables
//=====================================================================================================================

/// ATOM uniquely identifying the window class

ATOM Window::s_window_class_atom = 0;

/// Module handle for the program

HMODULE Window::s_module_handle = 0;

//=====================================================================================================================
// Constants
//=====================================================================================================================

/// Name of the window class

const char * Window::WIN_CLASS_NAME = "FrameworkWindowClass";

//=====================================================================================================================
/// @brief		Constructor. Creates the Window itself.
/// @param		title - title for the window
/// @param		fullscreen - whether the window is to be fullscreen or not
/// @param		size - client size of the window
/// @param		aa_level - level of anti aliasing to use
/// @remarks	The anti-aliasing level given may not be used due to hardware restrictions
//=====================================================================================================================

Window::Window( std::string title , bool fullscreen , const Vec2u & size , Display::AntialiasingLevel aa_level )
{
	// Clear window data

	m_window_handle		= 0; 
	m_dc				= 0;
	m_display			= 0;
	m_event_listener	= 0;

	// Setup

	RetrieveModuleHandle();		// Retrieve the module handle
	RegisterWindowClass();		// Register the window class
	SetupWindow(title,size);	// Create the actual window itself
	
	// Make sure client size is ok:
	
	SetClientSize(size);

	// Create a display:

	m_display = new Display(this,fullscreen,size,aa_level);
}

//=====================================================================================================================
/// @brief		Destructor. Destroys the window.
/// @pre		There should only be one reference to the display at this point
//=====================================================================================================================

Window::~Window()
{
	// Make sure window is destroyed:

	Close();
	
	// Remove the event listener:
	
	SetEventListener(0);
	
	// Make sure everything is freed up:

	OnWindowDestroyed();
	
	// Make sure ok:
	
	ENSURE( m_display->GetReferenceCount() == 1 , "There should only be one reference to the display at this point" )
	
	// Delete the display
	
	m_display->RemoveReference();	
}

//=====================================================================================================================
///	@brief		Called when the window is destroyed or deleted
/// @pre		There should be no event listener left when the window is destroyed
//=====================================================================================================================

void Window::OnWindowDestroyed()
{
	// Free handles:

	ReleaseDC( m_window_handle , m_dc );	
}

//=====================================================================================================================
///	@brief		Sets the current window event listener
/// @param		listener - event listener for the window
//=====================================================================================================================

void Window::SetEventListener( WindowEventListener * listener )
{
	// Add a reference to the event listener if there:
	
	if ( listener != 0 )
	{					
		listener->AddReference();
	}
	
	// Store old event listener here:
	
	WindowEventListener * old_listener = m_event_listener;
	
	// Save new window event listener

	m_event_listener = listener;
	
	// Remove reference to old listener if there:
	
	if ( old_listener )
	{
		old_listener->RemoveReference();
	}
}

//=====================================================================================================================
/// @brief	Creates the actual window for this class with the operating system. Also gets the DC for the window.
/// @param	title - title of the window 
/// @param	client_size - client size of the window
//=====================================================================================================================

void Window::SetupWindow( std::string title , const Vec2u & client_size )
{
	// Only do if we have a window class and a module handle:

	if ( s_window_class_atom != 0 && s_module_handle != 0 )
	{
		// Make the window:

		m_window_handle = CreateWindowExA
		(
			0														,	// Extended window style
			WIN_CLASS_NAME											,	// Name of the window class
			title.c_str()											,	// Window name
			WS_OVERLAPPEDWINDOW	^ ( WS_THICKFRAME | WS_MAXIMIZEBOX ),	// Window style
			0														,	// Window position x
			0														,	// Window position y
			client_size.x											,	// Window width
			client_size.y											,	// Window height
			0														,	// Parent window
			0														,	// Menu
			(HINSTANCE) s_module_handle								,	// Module handle
			0															// Creation params
		);

		// See if there is a window:

		if ( m_window_handle != 0 )
		{
			// Set extra window memory to point to this window:

			SetWindowLongPtrA( m_window_handle , 0 , (LONG) this );

			// Retrieve the dc for the window:

			m_dc = GetDC(m_window_handle);

			// Set the client size of the window:

			SetClientSize( client_size );

			// Center the window:

			Center();

			// Make visible:

			SetVisible(true);			
		}
	}
}

//=====================================================================================================================
/// @brief	Gets the handle for the program if not already done so
//=====================================================================================================================

void Window::RetrieveModuleHandle()
{
	// Don't do if we already have a module handle:

	if ( s_module_handle != 0 ) return;

	// Get program handle:

	s_module_handle = GetModuleHandle(0);
}

//=====================================================================================================================
/// @brief	Registers the window class for the window if not already done so
//=====================================================================================================================

void Window::RegisterWindowClass()
{
	// If already registered then don't bother:

	if ( s_window_class_atom != 0 ) return;

	// Get module handle if not already done so:

	RetrieveModuleHandle();

	// Abort if no module handle there:

	if ( s_module_handle == 0 ) return;

	// Create a window class structure:

	WNDCLASSEXA window_class = {0}; 

    window_class.cbSize			= sizeof(WNDCLASSEXA);
	window_class.style			= CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
	window_class.lpfnWndProc	= (WNDPROC) WindowProcedure;
    window_class.cbClsExtra		= 0;
    window_class.cbWndExtra		= sizeof(void*);
    window_class.hInstance		= (HINSTANCE) s_module_handle;
    window_class.hIcon			= 0;
    window_class.hCursor		= 0;
    window_class.hbrBackground	= 0;
    window_class.lpszMenuName	= 0;
    window_class.lpszClassName	= WIN_CLASS_NAME;
    window_class.hIconSm		= 0;

	// Register the window class:

	s_window_class_atom = RegisterClassExA(&window_class);
}

//=====================================================================================================================
/// @brief	Processes messages for all created windows.
/// @param	hwnd	- The window involved
/// @param	msg		- Message sent to the window
/// @param	wparam	- Message parameter 1
/// @param	lparam	- Message parameter 2
//=====================================================================================================================

LRESULT CALLBACK Window::WindowProcedure( HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam )
{
	// Get a pointer to the window in question from the extra window memory:

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

	// Bail if that is invalid:

	if ( window == 0 ) return DefWindowProc(hwnd,msg,wparam,lparam);

	// See what the message is:

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

		// Gained focus:

		case WM_SETFOCUS:
		{
			// Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnWindowFocusGained();
			}

		}	break;

		// Lost focus:

		case WM_KILLFOCUS:
		{
			// Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnWindowFocusLost();
			}

		}	break;

        //-------------------------------------------------------------------------------------------------------------
        // Keyboard events
        //-------------------------------------------------------------------------------------------------------------

        // Key down:

        case WM_KEYDOWN:
        {
			// Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnKeyboardPressed( (Keyboard::Key) wparam );
			}

        }   break;

       // Key up:

       case WM_KEYUP:
       {
		   // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnKeyboardReleased( (Keyboard::Key) wparam );
			}

       }    break;

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

        // Left Mouse down:

        case WM_LBUTTONDOWN:
        {
            // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMousePressed( LOWORD(lparam), HIWORD(lparam), VK_LBUTTON);
			}

        }   break;
		
        // Right Mouse down:		

        case WM_RBUTTONDOWN:
        {
            // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMousePressed( LOWORD(lparam), HIWORD(lparam), VK_RBUTTON );
			}

        }   break;
		
        // Middle Mouse down:		

        case WM_MBUTTONDOWN:
        {
            // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMousePressed( LOWORD(lparam), HIWORD(lparam), VK_MBUTTON );
			}

        }   break;
		
        // Left Mouse up:		

        case WM_LBUTTONUP:
        {
            // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMouseReleased( LOWORD(lparam), HIWORD(lparam), VK_LBUTTON );
			}

        }   break;
		
        // Right Mouse up:		

        case WM_RBUTTONUP:
        {
            // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMouseReleased( LOWORD(lparam), HIWORD(lparam), VK_RBUTTON );
			}

        }   break;
		
        // Middle Mouse up:		

        case WM_MBUTTONUP:
        {
            // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMouseReleased( LOWORD(lparam), HIWORD(lparam), VK_MBUTTON );
			}

        }   break;
		
        // Mouse moved:		

        case WM_MOUSEMOVE:
        {
            // Send the event onto the event listener if there is one

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMouseMoved( LOWORD(lparam), HIWORD(lparam) );
			}

        }   break;
		
		// Mouse wheel scrolled:

		case WM_MOUSEWHEEL:
		{
			// Get the wheel delta:
			
			int delta = GET_WHEEL_DELTA_WPARAM(wparam);
			
			// Convert to float from -1 to 1 , or greater for more than one scroll
			
			float delta_f = float( delta ) / float( WHEEL_DELTA );		

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

			if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnMouseWheelScrolled(delta_f);
			}

		}	break;

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

        case WM_SIZE:
        {

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

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnWindowResized();
			}
			
			// Reset the d3d device to resize the back buffer:
			
			if ( window->m_display ) window->m_display->Reset();

        }   break;

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

        case WM_PAINT:
        {
            // If an event listener is assigned to the window then call the redrawing function of the listener

            if ( window->m_event_listener ) 
			{
				// See if the device needs a reset:
				
				if ( window->m_display->RequiresReset() )
				{
					// Need to reset the device
				
					window->m_display->Reset();
				}
				else
				{
					// Redraw

					window->m_event_listener->OnWindowRedrawing();				
				}
				
				// Return 0 for this being done:
				
				return 0;
			}

            // Call the default window procedure

        }   break;

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

        case WM_CLOSE:
        {
            // Call the closing event if there is an event listener

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnWindowClosing();
			}

			// Close the window:

			DestroyWindow(hwnd);

			// Finish:

			return 0;
        }

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

        case WM_DESTROY:
        {
            // Call the window closed event if it exists

            if ( window->m_event_listener ) 
			{
				window->m_event_listener->OnWindowClosed();
			}

            // Cleanup all window resources

            window->OnWindowDestroyed();

            // Done:

			return 0;
        }
	}

	//-----------------------------------------------------------------------------------------------------------------
	// No action defined for window message: call default window procedure
	//-----------------------------------------------------------------------------------------------------------------

	return DefWindowProc(hwnd,msg,wparam,lparam);
}

//=====================================================================================================================
/// @brief		Tells if this window is valid and if the display for the window is setup right:
//	@returns	True if the window display is valid
//=====================================================================================================================

bool Window::IsValid() const
{
	// No program handle = fail

	if ( s_module_handle == 0 ) return false;

	// No window class = fail

	if ( s_window_class_atom == 0 ) return false;

	// No window = fail

	if ( m_window_handle == 0 ) return false;

	// No DC = fail

	if ( m_dc == 0 ) return false;

	// Valid window:

	return true;
}

//=====================================================================================================================
/// @brief		Tells if the window currently exists. Does not say if it is valid however.
//	@returns	True if the window currently exists
//=====================================================================================================================

bool Window::Exists() const
{
	// Return if window:

	return IsWindow(m_window_handle) != 0;
}

//=====================================================================================================================
/// @brief		Runs the window for a bit, performing some message processing.
//=====================================================================================================================

void Window::Run()
{
	// If window doesn't exist then abort

	if ( Exists() == false ) return;

	// Makeup a message struct

	MSG message = {0};

	// Have a gander for a messag:

	if ( PeekMessage(&message,m_window_handle,0,0,PM_REMOVE) != 0 )
	{
		// Produce character messages for ordinary typing:

		TranslateMessage(&message);

		// Off you go to WindowProcedure:

		DispatchMessage(&message);
	}
}


//=====================================================================================================================
/// @brief		Gets the window position in screen coordinates
/// @returns	Position of window in screen coordinates
//=====================================================================================================================

Vec2i Window::GetPosition() const
{
	// If no window return 0:

	if ( m_window_handle == 0 ) return Vec2i::Zero();

    // Get the windows position in screen coordinates

	RECT r = {0}; 
	
	// Get the position:

	if ( GetWindowRect(m_window_handle,&r) != 0 )
	{
		// Return the position position

		return Vec2i(r.left,r.top);
	}
	else
	{
		// Failed to get window position: return 0

		return Vec2i::Zero();
	}
}

//=====================================================================================================================
/// @brief		Gets the center of the client area in screen coordinates
/// @returns	Position of the client center in screen coordinates
//=====================================================================================================================

Vec2i Window::GetClientCenterScreen() const
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		// Get the windows position in screen coordinates

		Vec2i pos = GetPosition();
		
		// Get the client size:
		
		Vec2i size = GetClientSize();

		// Get the thickness of a window border

		int border_x = GetSystemMetrics(SM_CXFRAME);
		int border_y = GetSystemMetrics(SM_CYFRAME);

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

		int menu_y = GetSystemMetrics(SM_CYCAPTION);		
	
		// Now figure out where in the screen the center of the client area lies

		return Vec2i
		( 
			pos.x + border_x +			( size.x / 2 ) - 1 ,
			pos.y + border_y + menu_y + ( size.y / 2 ) - 1
		);
	}
	else
	{
		// Can't get center

		return Vec2i::Zero();
	}
}

//=====================================================================================================================
/// @brief		Gets the size of the window
/// @returns	Size of the window
//=====================================================================================================================

Vec2u Window::GetSize() const
{
	// If no window return 0:

	if ( m_window_handle == 0 ) return Vec2u::Zero();

    // Store window info here

	static WINDOWPLACEMENT placement = {0};

    // Get window info

    if ( GetWindowPlacement(m_window_handle,&placement) != 0 )
	{
		// Return size

		return Vec2u
		(
			placement.rcNormalPosition.right	- placement.rcNormalPosition.left	,
			placement.rcNormalPosition.bottom	- placement.rcNormalPosition.top
		);
	}
	else
	{
		// Failed to get window width: return 0

		return Vec2u::Zero();
	}
}

//=====================================================================================================================
/// @brief		Gets client size of the window
/// @returns	Client size of window
//=====================================================================================================================

Vec2u Window::GetClientSize() const
{
	// If no window return 0

	if ( m_window_handle == 0 ) return Vec2u::Zero();

	// Get window dimensions:

	RECT client_rect = {0};
	
	// See if this can happen:
	
	BOOL result = GetClientRect(m_window_handle,&client_rect);
	
	// See if that succeded:

	if ( result != FALSE )
	{
		// Return size:

		return Vec2u
		(
			client_rect.right	- client_rect.left	,
			client_rect.bottom	- client_rect.top
		);
	}
	else
	{
		// Couldn't get dimensions:

		return Vec2u::Zero();
	}
}

//=====================================================================================================================
/// @brief		Gets the size of the screen area
/// @returns	Screen width
//=====================================================================================================================

Vec2u Window::GetScreenSize()
{
	// Get the desktop window

	HWND desktop = GetDesktopWindow();
	
	// Get it's size:

	RECT r = {0}; 
	
	if ( GetWindowRect(desktop,&r) != 0 )
	{
		// Return it's size

		return Vec2u
		(
			r.right		- r.left	,
			r.bottom	- r.top
		);
	}
	else
	{
		// Failed: return 0

		return Vec2u::Zero();
	}
}

//=====================================================================================================================
/// @brief		Tells if the window is minimized
/// @returns	True if window is minimized
//=====================================================================================================================

bool Window::IsMinimized() const
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		// Have handle:

		return IsIconic(m_window_handle) != 0;
	}
	else
	{
		// Unable to tell: return false

		return false;
	}
}

//=====================================================================================================================
/// @brief		Tells if the window is maximised
/// @returns	True if window is maximized
//=====================================================================================================================

bool Window::IsMaximized() const
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		// Have handle:

		return IsZoomed(m_window_handle) != 0;
	}
	else
	{
		// Unable to tell: return false

		return false;
	}
}

//=====================================================================================================================
/// @brief		Tells if the window is currently focused
/// @returns	True if the window is in focus
//=====================================================================================================================

bool Window::HasFocus() const
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		// Get the handle of the current foreground window:

		HWND fgwin = GetForegroundWindow();

		// If the handle is this windows handle then return true

		if ( fgwin == m_window_handle ) return true; else return false;
	}
	else
	{
		// No window handle: return false

		return false;
	}
}

//=====================================================================================================================
/// @brief		Tells if the window is resizeable
/// @returns	True if the window is resizeable
//=====================================================================================================================

bool Window::IsResizeable() const
{
	// Only if we have a window:

	if ( m_window_handle != 0 )
	{
		// Get last window style by setting a new window style

		int laststy = GetWindowLongA( m_window_handle , GWL_STYLE );

		// Now see if both ws thick frame and maximize box are enabled: if so then it is resizeable

		if ( ( laststy & WS_THICKFRAME ) != 0 || ( laststy & WS_MAXIMIZEBOX ) != 0 || ( laststy & WS_SIZEBOX ) != 0 )
		{
			// Window is resizable: return true

			return true;
		}
		else
		{
			// Window not resizeable: return false

			return false;
		}
	}
	else
	{
		// Not resizeable:

		return false;
	}
}

//=====================================================================================================================
/// @brief		Gets the window title
/// @returns	Title of the window
//=====================================================================================================================

std::string Window::GetTitle() const
{
	// See if window handle:

	if ( m_window_handle != 0 )
	{
		// Store title here:

		char buffer[1024]; buffer[0] = 0;

		// Grab that title:

		GetWindowTextA( m_window_handle, (LPSTR) buffer, 1023 );

		// Return the title:

		return string(buffer);
	}
	else
	{
		// No title:

		return string("");
	}
}

//=====================================================================================================================
/// @brief	Closes the window
//=====================================================================================================================

void Window::Close()
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		DestroyWindow(m_window_handle);
	}
}

//=====================================================================================================================
/// @brief	Maximizes the window
//=====================================================================================================================

void Window::Maximize()
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		ShowWindow(m_window_handle,SW_MAXIMIZE);
	}
}

//=====================================================================================================================
/// @brief	Minimizes the window
//=====================================================================================================================

void Window::Minimize()
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		ShowWindow(m_window_handle,SW_MINIMIZE);
	}
}

//=====================================================================================================================
/// @brief	Restores the window
//=====================================================================================================================

void Window::Restore()
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		ShowWindow(m_window_handle,SW_RESTORE);
	}
}

//=====================================================================================================================
/// @brief	Puts the window into focus
//=====================================================================================================================

void Window::GiveFocus()
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		if ( HasFocus() == false ) SetForegroundWindow(m_window_handle);
	}
}

//=====================================================================================================================
/// @brief	Centers the window
//=====================================================================================================================

void Window::Center()
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		// Get screen size:
		
		Vec2i screen_size = GetScreenSize();
		
		// Get window size:
		
		Vec2i win_size = GetSize();
	
		// Center the window

		SetWindowPos
		(
			m_window_handle,							// handle to window
			HWND_TOP,									// new z order
			( screen_size.x - win_size.x ) / 2 ,		// new x position
			( screen_size.y - win_size.y ) / 2 ,		// new y position
			0,                                          // new x size: dont care, ignored
			0,                                          // new y size: dont care, ignored
			SWP_NOSIZE	|                               // dont resize
			SWP_NOZORDER |	                            // dont change z order
			SWP_NOACTIVATE								// dont activate if not active
		);
	}
}

//=====================================================================================================================
/// @brief	Invalidates the window region and thus causes a repaint to happen.
//=====================================================================================================================
 
void Window::RequestRepaint()
{
	// Only if there is a window handle:

	if ( m_window_handle != 0 )
	{
		// Tell windows that the window has to be repainted

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

//=====================================================================================================================
/// @brief	Sets the title for the window
/// @param	title - title for the window
//=====================================================================================================================

void Window::SetTitle( std::string title )
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		SetWindowTextA( m_window_handle, (LPSTR) title.c_str() );
	}
}

//=====================================================================================================================
/// @brief	Sets whether the window is resizeable
/// @param	sizeable - if the window is to be resizeable or not
//=====================================================================================================================

void Window::SetResizeable( bool sizeable )
{
	// Only if we have a window handle 
	
	if ( m_window_handle != 0 )
	{
		// Get last window style:

		int style = GetWindowLongA( m_window_handle , GWL_STYLE );

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

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

			style |= WS_THICKFRAME;
			style |= WS_MAXIMIZEBOX;

		}
		else
		{
			// Switch off the sizeable attribute if on and maximize box

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

		// Set new window style

		SetWindowLongA( m_window_handle , GWL_STYLE , style );
	}
}

//=====================================================================================================================
/// @brief	Sets whether the window is visible
/// @param	visible - whether the window will be set to visible or not
//=====================================================================================================================

void Window::SetVisible( bool visible )
{
	// Only if we have a window handle 
	
	if ( m_window_handle != 0 )
	{
		// See if the window is to be visible or not:

		if ( visible == false )
		{
			SetWindowPos( m_window_handle , HWND_TOP , 0 , 0 , 0 , 0 , SWP_HIDEWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER );
		}
		else
		{
			SetWindowPos( m_window_handle , HWND_TOP , 0 , 0 , 0 , 0 , SWP_SHOWWINDOW | SWP_NOMOVE | SWP_NOSIZE | SWP_NOOWNERZORDER );
		}
	}
}

//=====================================================================================================================
/// @brief	Sets the total window size
/// @param	size - new size of the window
//=====================================================================================================================

void Window::SetSize( const Vec2u & size )
{
	// Only if we have a window handle 
	
	if ( m_window_handle != 0 )
	{
		// Dont do if invalid dimensions given

		if ( size.x > 0 && size.y > 0 )
		{			
			SetWindowPos
			(
				m_window_handle	,               // window handle
				HWND_TOPMOST	,               // window z order
				0				,               // window x position
				0				,               // window y position
				size.x			,               // window width
				size.y			,               // window height
				SWP_NOZORDER |					// Retain z order and position
				SWP_NOMOVE |					// No movement
				SWP_NOACTIVATE					// No activation
			);
		}
	}
}

//=====================================================================================================================
/// @brief	Sets the size of the window drawing area (client area)
/// @param	client_size - new client size for the window
//=====================================================================================================================

void Window::SetClientSize( const Vec2u & client_size )
{
	// Only if we have a window handle 
	
	if ( m_window_handle != 0 )
	{
		// Dont do if invalid dimensions given

		if ( client_size.x > 0 && client_size.y > 0 )
		{
			// Get the window style:

			int style = GetWindowLong(m_window_handle,GWL_STYLE);

			// Get the extended window style:

			int stylex = GetWindowLong(m_window_handle,GWL_EXSTYLE);
			
			// Get current position:
			
			Vec2i pos = GetPosition();

			// Save desired client size here

			RECT r;

			r.left     = pos.x;
			r.top      = pos.y;
			r.right    = r.left + client_size.x;
			r.bottom   = r.top  + client_size.y;

			// Get window to calculate a window size based on desired client size

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

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

			SetSize( Vec2u( r.right - r.left , r.bottom - r.top ) );
		}
	}
}

//=====================================================================================================================
/// @brief	Sets the window position in screen coords
/// @param	position - new position for the window
//=====================================================================================================================

void Window::SetPosition( const Vec2i & position )
{
	// Only if we have a window handle 
	
	if ( m_window_handle != 0 )
	{
		SetWindowPos
		(
			m_window_handle	,       // window handle
			HWND_TOP		,       // window z order
			position.x		,       // window x position
			position.y		,       // window y position
			0				,       // window width
			0				,       // window height
			SWP_NOZORDER |			// Retain z order and position
			SWP_NOSIZE |			// No sizing
			SWP_NOACTIVATE			// No activation
		);
	}
}

//=====================================================================================================================
/// @brief		Converts the given screen position into client coordinates
/// @param		screen_pos - position in screen coordinates
/// @returns	position in client coordinates
//=====================================================================================================================

Vec2i Window::ToClientCoords( const Vec2i & screen_pos ) const
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		// Store point here:

		POINT p = {0}; 
		
		p.x = screen_pos.x;
		p.y = screen_pos.y;

		// Do the conversion:

		if ( ScreenToClient(m_window_handle,&p) != 0 )
		{
			return Vec2i(p.x,p.y);
		}
		else
		{
			return Vec2i::Zero();
		}	
	}
	else
	{
		// No window handle:

		return Vec2i::Zero();
	}
}

//=====================================================================================================================
/// @brief		Converts the given client position into screen coordinates
/// @param		client_pos - position in screen coordinates
/// @returns	position in screen coordinates
//=====================================================================================================================

Vec2i Window::ToScreenCoords( const Vec2i & client_pos ) const
{
	// Only if we have a window handle:

	if ( m_window_handle != 0 )
	{
		// Store point here:

		POINT p = {0}; 
		
		p.x = client_pos.x;
		p.y = client_pos.y;

		// Do the conversion:

		if ( ClientToScreen(m_window_handle,&p) != 0 )
		{
			return Vec2i
			(
				p.x,
				p.y
			);
		}
		else
		{
			return Vec2i::Zero();
		}	
	}
	else
	{
		// No window handle:

		return Vec2i::Zero();
	}		
}
