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

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

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

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

#ifndef GLWINDOW_H
#define GLWINDOW_H

//##############################################################################
// C Includes
//##############################################################################

// standard libarary includes

#include <cstdarg>
#include <ctime>
#include <cstdlib>
#include <cassert>
#include <cmath>

// Windows include

#include <Windows.h>

// OpenGL includes

#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glext.h>

//##############################################################################
// Forward references
//##############################################################################

class GLWindowEventListener;
class Timer;

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

class GLWindow
{

 public:

        //----------------------------------------------------------------------
        // Enums
        //----------------------------------------------------------------------

        // Mouse buttons

        enum MouseButton
        {
             MOUSE_BUTTON_LEFT   = 0x01,
             MOUSE_BUTTON_RIGHT  = 0x02,
             MOUSE_BUTTON_MIDDLE = 0x04
        };

        // Cursor types

        enum CursorType
        {
            CURSOR_NONE ,
            CURSOR_ARROW,
            CURSOR_CROSS
        };

        // Keyboard special key codes: letters are all received as uppercase ascii

        enum KeyboardKey
        {
            KEYBOARD_BACKSPACE       = 0x08,
            KEYBOARD_TAB    	     = 0x09,
            KEYBOARD_RETURN          = 0x0D,
            KEYBOARD_SHIFT	         = 0x10,
            KEYBOARD_CTRL	         = 0x11,
            KEYBOARD_ALT	         = 0x12,
            KEYBOARD_PAUSE	         = 0x13,
            KEYBOARD_CAPSLOCK	     = 0x14,
            KEYBOARD_ESCAPE	         = 0x1B,
            KEYBOARD_SPACE	         = 0x20,
            KEYBOARD_PAGE_UP	     = 0x21,
            KEYBOARD_PAGE_DOWN	     = 0x22,
            KEYBOARD_END	         = 0x23,
            KEYBOARD_HOME	         = 0x24,
            KEYBOARD_LEFT	         = 0x25,
            KEYBOARD_UP	             = 0x26,
            KEYBOARD_RIGHT	         = 0x27,
            KEYBOARD_DOWN	         = 0x28,
            KEYBOARD_INSERT	         = 0x2D,
            KEYBOARD_DELETE	         = 0x2E,
            KEYBOARD_NUMPAD_0	     = 0x60,
            KEYBOARD_NUMPAD_1	     = 0x61,
            KEYBOARD_NUMPAD_2	     = 0x62,
            KEYBOARD_NUMPAD_3	     = 0x63,
            KEYBOARD_NUMPAD_4	     = 0x64,
            KEYBOARD_NUMPAD_5	     = 0x65,
            KEYBOARD_NUMPAD_6	     = 0x66,
            KEYBOARD_NUMPAD_7	     = 0x67,
            KEYBOARD_NUMPAD_8	     = 0x68,
            KEYBOARD_NUMPAD_9	     = 0x69,
            KEYBOARD_MULTIPLY	     = 0x6A,
            KEYBOARD_ADD	         = 0x6B,
            KEYBOARD_SUBTRACT	     = 0x6D,
            KEYBOARD_DECIMAL	     = 0x6E,
            KEYBOARD_DIVIDE	         = 0x6F,
            KEYBOARD_F1	             = 0x70,
            KEYBOARD_F2	             = 0x71,
            KEYBOARD_F3	             = 0x72,
            KEYBOARD_F4	             = 0x73,
            KEYBOARD_F5	             = 0x74,
            KEYBOARD_F6	             = 0x75,
            KEYBOARD_F7	             = 0x76,
            KEYBOARD_F8	             = 0x77,
            KEYBOARD_F9	             = 0x78,
            KEYBOARD_F10	         = 0x79,
            KEYBOARD_F11	         = 0x7A,
            KEYBOARD_F12	         = 0x7B
        };

        //----------------------------------------------------------------------
        // Functions
        //----------------------------------------------------------------------

        // Creates the window: must be called first

        static void createWindow
        (
            char *      windowTitle ,   // title of window
            unsigned    clientWidth ,   // width of drawing area
            unsigned    clientHeight    // height of drawing area
        );

        static void run();          // Do message processing and input etc. for window
        static void swapBuffers();  // Swap the back buffer onto the front buffer

        // Set fullscreen or not

        static void setFullscreen
        (
            bool fullscreen  = true ,
            int resolution_x = 640  ,
            int resolution_y = 480
        );
        
        // Tests if the given fullscreen resolution is supported: returns true if this is the case
        
        static bool testFullscreen( int resolution_x , int resolution_y );
        
        // Returns a fullscreen display mode compatible with the given resolution: returns false if this cannot be obtained
        
        static bool getFullscreenMode( int resolution_x , int resolution_y , DEVMODE * device_mode );

        // Set the gamma value for the screen: may or may not work depending on hardware support.

        static void setGamma( float gamma );

        // Query window state

        static bool exists();              // does the window actually exist ?
        static int  getX();                // Window position
        static int  getY();                // Window position
        static int  getWidth();            // Window size
        static int  getHeight();           // Window size
        static int  getClientWidth();      // Window size (drawing area)
        static int  getClientHeight();     // Window size (drawing area)
        static int  getScreenWidth();      // Get the width of the screen
        static int  getScreenHeight();     // Get the height of the screen
        static bool isFullscreen();        // Is the window in fullscreen mode ?

        // Get the last known mouse position: in client area coordinates

        static inline int getMouseX(){ return lastMouseX; }
        static inline int getMouseY(){ return lastMouseY; }

        // Get the position of the mouse in screen coordinates

        static int getMouseScreenX();
        static int getMouseScreenY();

        // Center the mouse position in the middle of the client area

        static void centerMouse();

        // Moves the mouse by the specified amount in pixels

        static void setMousePosition( int screenX , int screenY );

        // Restricts the mouse movement to the bounding box given:

        static void restrictMouse( int x , int y , int width , int height );

        // Un-restricts the mouse movement:

        static void unRestrictMouse();

        // Get the center of the client area in screen coordinates:

        static int getClientCenterX();
        static int getClientCenterY();

        // Tells whether the mouse is in the client area of the window

        static bool mouseInClientArea();

        // OpenGL related functions

        static void glBeginDraw3d(float fov,float nearPlaneDist,float farPlaneDist);   // set up and initialise the matrices for drawing in 3D
        static void glBeginDraw2d();                                                   // set up and initialise the matrices for drawing in 2D

        // Clear the screen to a given color:

        static void clearScreen( float r , float g, float b );

        // Show a question box and a message box

        static bool showQuestionBox ( char * question , char * title );
        static void showMessageBox  ( char * message  , char * title );

        // Window settings

        static void setResizeable  ( bool sizeable );                       // set if window is resizeablee
        static void setSize        ( int width , int height );              // set window size
        static void setClientSize  ( int clientWidth , int clientHeight );  // set window drawing area size (window automatically resizes too)
        static void setPosition    ( int x , int y );                       // set window position
        static void setTitle       ( char * title  );                       // sets window title
        static void setCursor      ( CursorType cursor );                   // sets the window cursor

        // Minimize, maximize, restore, repaint , close , center , and request repaint of the window

        static void close();
        static void minimize();
        static void maximize();
        static void restore();
        static void center();
        static void requestRepaint();

        // Query if mouse and keyboard keys are pressed

        static bool keyboardKeyPressed( char key );
        static bool keyboardKeyPressed( GLWindow::KeyboardKey key       );
        static bool mouseButtonPressed( GLWindow::MouseButton button    );

        // Gets the last key pressed by the user. Returns 0 if no key was pressed. This function also clears the last key that was pressed.

        static int getLastKeyPressed();

        // Fades the current screen to black in the given time in seconds. Maximum fade time is 10 seconds:

        static void fadeScreen( float time );

        // Set and get the current event listener: window is not responsible for
        // cleaning up after this object so code outside must be used to delete it.

        static void setEventListener( GLWindowEventListener * eventListener );
        static GLWindowEventListener * getEventListener();

        // Get the windows handle for the window

        static inline HWND getWindowHandle(){ return windowHandle; }

//##############################################################################
//##############################################################################
//##############################################################################
//##############################################################################

 protected:

        //----------------------------------------------------------------------
        // Constants
        //----------------------------------------------------------------------

        static const char * WINDOW_CLASSNAME;
        static const char * WINDOW_DEFAULT_TITLE;

        //----------------------------------------------------------------------
        // Variables
        //----------------------------------------------------------------------

        // Windows handles:

        static HINSTANCE    programHandle;          // Process handle
        static HWND         windowHandle;           // Window handle
        static HDC          deviceContext;          // Device context
        static HGLRC        openGLDeviceContext;    // OpenGL Hardware rendering context

        // Last known position of the mouse

        static int lastMouseX;
        static int lastMouseY;

        // Miscellaneous

        static bool         fullscreenMode;         // Is window in fullscreen mode?
        static CursorType   windowCursor;           // Cursor to use for the window:
        static int          lastKeyPressed;         // The last key that was pressed by the user. 0 when no key.

        // The current event listener for the window

        static GLWindowEventListener * eventListener;

        //----------------------------------------------------------------------
        // Functions
        //----------------------------------------------------------------------

        // Releasing of window resources: general cleanup

        static void windowDestroyed();

        // Window procedure: not a member function but must be common to all instances

        static LRESULT CALLBACK windowProcedure( HWND , UINT , WPARAM , LPARAM );

}; // end of: class Window{}

//##############################################################################
// C++ includes
//##############################################################################

#include "GLWindowEventListener.h"
#include "Timer.h"

#endif // end of: ifndef WINDOW_H
