/*
	Asteroids
    Copyright (C) 2006  Darragh Coy.

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

/*##############################################################################

Module name: GLWin
Purpose:     Intialises the Win32 window and setups up OpenGL. Handles shutdown
             also. This code was written from consulation of the Win32 API
             documentation and the OpenGL 'red book'.

##############################################################################*/

#include "GLWin.h"

/*******************************************************************************
Private module prototypes: Not for outside use!
*******************************************************************************/

void GLWin_CreateClass();            // Register a windows 'class' with windows
void GLWin_CreateWindow();           // Actual window creation
void GLWin_InitOpenGL();             // Initialise OpenGL
void GLWin_ShutDownOpenGL();         // Shutdown OpenGL

// Windows message loop: retrieves messages belonging to the programs thread
// and dispatches them so they get to the windows procedure.

void GLWin_MessageLoop();

// Windows Procedure: handles window events

LRESULT CALLBACK GLWin_WinProc( HWND, UINT , WPARAM , LPARAM );

/*******************************************************************************
Variables
*******************************************************************************/

HINSTANCE   prgHandle = NULL;      // Pointer to program in memory (needed to create window)
WNDCLASSEX  winClass;              // Window class for this window
HWND        winHandle = NULL;      // Handle to the created window
HDC         hwContext = NULL;      // Handle to the hardware rendering context of the window
HGLRC       glContext = NULL;      // Handle to program OpenGL rendering context.
bool        runProgram;            // Should the program continue running ?

// State of all keys

bool GLWin_Keydown[256] = { false };

// Last key pressed: if zero then means none. Used for name input. Set to zero
// after read a character.

char GLWin_LastKey = 0;

// Is the program in a paused state ?

bool programPaused = false;

/*******************************************************************************

Name:       GLWin_Message
Purpose:    Shows a simple message box
Parameters: String pointer
Returns:    None

*******************************************************************************/

void GLWin_Message ( char * message , char * title )
{
     // Pause the program

     GLWin_PauseProgram();

     // Show the message box using the windows message box function

     MessageBoxA
         (
         winHandle ,
         message ,
         title   ,
         MB_OK
         );

     // Resume the program

     GLWin_ResumeProgram();
}

/*******************************************************************************

Name:       GLWin_Prompt
Purpose:    Shows a simple prompt box asking a question
Parameters: String pointer
Returns:    True if yes option selected, false if not

*******************************************************************************/

bool GLWin_Prompt ( char * message , char * title )
{
     // Pause the program

     GLWin_PauseProgram();

     // Show the message box using the windows message box function

     int choice = MessageBoxA
         (
         winHandle ,
         message ,
         title   ,
         MB_YESNO
         );

     // Resume the program

     GLWin_ResumeProgram();

     // If yes button pressed return true. Otherwise return

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

/*******************************************************************************

Name:       GLWin_Init
Purpose:    Handles the creation of the window and initialisation of OpenGL
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_Init ( void )
{
     // Get the Handle or pointer to the program from windows

     prgHandle = (HINSTANCE) GetModuleHandle( NULL );

     // Ensure that program handle is OK

     if ( ! prgHandle )
     {
          // If got a NULL pointer then exit immediately

          GLWin_Message("Unable to create window","Error"); return;
     }

     // Create the Window Class

     GLWin_CreateClass();

     // Create the Window

     GLWin_CreateWindow();

     // Initialise OpenGL

     GLWin_InitOpenGL();

     // Initialise game fonts

     GLDrawString_Init(hwContext);

     // Set the program to run

     runProgram = true;

     // Enter the windows messaging loop. Only returns on program quit

     GLWin_MessageLoop();
}

/*******************************************************************************

Name:       GLWin_CreateClass
Purpose:    Creates and registers the windows class with windows. All windows
            must have a window class.
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_CreateClass ( void )
{
     // Reset window class memory to zero initially. Good idea in case something
     // is forgotten to be filled in.

     memset( & winClass , 0 , sizeof winClass );

     // Begin making settings to the window class

     winClass.cbSize        = sizeof winClass;                  // Class size
     winClass.cbClsExtra    = 0;                                // No extra memory in class struct
     winClass.cbWndExtra    = 0;                                // No extra memory for window
     winClass.style         = CS_HREDRAW | CS_VREDRAW;          // Style: redraw on arbitrary window move
     winClass.lpfnWndProc   = GLWin_WinProc;                    // Pointer to window procedure
     winClass.hInstance     = prgHandle;                        // Handle to program
     winClass.hIcon         = LoadIcon( NULL , IDI_WINLOGO );   // Program Icon
     winClass.hCursor       = LoadCursor( NULL , IDC_ARROW );   // Program cursor
     winClass.hbrBackground = NULL;                             // Manually redraw background: No brush
     winClass.lpszMenuName  = NULL;                             // No menu
     winClass.lpszClassName = "DarWin";                         // Window classname
     winClass.hIconSm       = LoadIcon( NULL , IDI_WINLOGO );   // Small window icon

     // Register the window class with windows:

     RegisterClassEx(&winClass);
}

/*******************************************************************************

Name:       GLWin_CreateWindow
Purpose:    Actually creates the Win32 window using the registered window class
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_CreateWindow ( void )
{
     // Decide on window style first:

     int winStyle = 0;

     winStyle |= WS_CAPTION;  // Window has a title bar
     winStyle |= WS_SYSMENU;  // Window with title bar and exit button, but no resize!
     winStyle |= WS_VISIBLE;  // Want a visible window!

     // Create the window and save its handle

     winHandle = CreateWindowEx
        (
        0,                         // Extended window style settings. Don't care about this
        winClass.lpszClassName,    // Window class name
        WINDOW_TITLE,              // Window title as defined in this modules header
        winStyle,                  // Regular window style
        0,                         // Window position ,x
        0,                         // Window position ,y
        WINDOW_X,                  // Window width
        WINDOW_Y,                  // Window height
        NULL,                      // No parent window
        NULL,                      // No menu
        prgHandle,                 // Handle to program
        NULL                       // No pointer to window creation data
        );

     // Show the window

     ShowWindow( winHandle , SW_SHOWNORMAL );

}

/*******************************************************************************

Name:       GLWin_MessageLoop
Purpose:    This is the main 'Thread' for the program. It runs continuoulsy
            until a WM_QUIT message is found in the programs message queue.
            This function simply runs constantly and dispatches any messages found
            to the window procedure for processing. It is an essential piece
            of code for any Win32 app.
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_MessageLoop ( void )
{
     // Hold the next message in the queue here

     MSG message;

     // Keep looping until runProgram is false

     while ( runProgram )
     {
          // Get the next message

          GetMessage( &message , NULL , 0 , 0 );

          // Dispatch this message to the window procedure

          DispatchMessage(&message);

		  // Request a window repaint:

		  InvalidateRect(winHandle,0,false);
     }
}

/*******************************************************************************

Name:       GLWin_InitOpenGL
Purpose:    Sets up the OpenGL display on the window
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_InitOpenGL ( void )
{
     // Get the hardware rendering context of the window. this presumably is a
     // pointer to the device which renders the window. Neeeded for OpenGL intialisation

     hwContext = GetDC( winHandle );

     // Create a PIXELFORMATDESCRIPTOR structure. This structure describes
     // the screen mode to windows...

     PIXELFORMATDESCRIPTOR pixFormat;

     // Clear all pixel format information initially

     memset( &pixFormat , 0 , sizeof pixFormat );

     // Setup the pixel format settings:

     pixFormat.cDepthBits  = 16;  // Depth buffer: number of depth bits
     pixFormat.cColorBits  = 16;  // Amount of color bits

     pixFormat.nSize       = sizeof pixFormat; // Size of the pixel format
     pixFormat.nVersion    = 1;                // Should always be set to 1- purpose unknown..

     pixFormat.dwFlags |= PFD_DRAW_TO_WINDOW;  // allowed draw to the window
     pixFormat.dwFlags |= PFD_SUPPORT_OPENGL;  // support OpenGL drawing
     pixFormat.dwFlags |= PFD_DOUBLEBUFFER;    // allow double buffering
     pixFormat.dwFlags |= PFD_SWAP_EXCHANGE;   // When swapping buffers exchange pointers, not data
     pixFormat.iPixelType = PFD_TYPE_RGBA;     // Standard RGBA pixel data

     // Get the closest possible pixel format to the one specified above for this
     // hardware device

     int pixelFormatBest = ChoosePixelFormat(hwContext,&pixFormat);

     // Set the pixel format

     SetPixelFormat( hwContext , pixelFormatBest , &pixFormat );

     // Create a new OpenGL rendering context

     glContext = wglCreateContext( hwContext );

     // Make the OpenGL rendering context active

     wglMakeCurrent( hwContext , glContext );
}

/*******************************************************************************

Name:       GLWin_ShutDownOpenGL
Purpose:    Shuts down OpenGL and frees the device rendering the window
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_ShutDownOpenGL ( void )
{
     // Destroy the OpenGL rendering context

     wglDeleteContext(glContext);
}

/*******************************************************************************

Name:       GLWin_PauseProgram
Purpose:    Pauses the game and hides the window
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_PauseProgram ( void )
{
   // Hide the window if it has been created

   if ( winHandle ) ShowWindow(winHandle,SW_HIDE);

   // Set program as paused

   programPaused = true;
}

/*******************************************************************************

Name:       GLWin_ResumeProgram
Purpose:    Resumes the game and reshows window
Parameters: None
Returns:    None

*******************************************************************************/

void GLWin_ResumeProgram ( void )
{
   // UnHide the window if it has been created

   if ( winHandle ) ShowWindow(winHandle,SW_SHOW);

   // Set program as paused

   programPaused = false;

   // Unpause the timer to discount any time passed

   Timer_Unpause();
}

/*******************************************************************************

Name:       GLWin_WinProc
Purpose:    This is where the beef is. All the events that happen to the window
            get sent here in the form of messages. These messages can either
            be processed by the application or handled by the default window procedure,
            which is part of the windows library.
Parameters: (1) Handle the window
            (2) Message identifier
            (3) First window paramter, 16 bit or one word
            (4) Second window parameter, 32 bit or long
Returns:    Error code, much like main. Mostly just return 0 for success

*******************************************************************************/

LRESULT CALLBACK GLWin_WinProc ( HWND winHandle, UINT message, WPARAM param1, LPARAM param2 )
{
    /***************************************************************************
    Decide what to do based on message#
    ***************************************************************************/

    switch ( message )
    {

       /************************************************************************
       Repaint method: do the game and redraw the screen
       ************************************************************************/

       case WM_PAINT:
       {
          // Do the game loop: but onlu if game isn't paused...

          if ( ! programPaused ) Game_DoGameLoop();

       }break;

       /************************************************************************
       Key pressed down. Save key state. Virual key code is in first param
       ************************************************************************/

       case WM_KEYDOWN:
       {
            // Set the last key pressed

            GLWin_LastKey = (char)(param1&0xff);

            // Set this key as pressed and return 0

            GLWin_Keydown[param1] = true;  return 0;

       }break;

       /************************************************************************
       Key released. Save key state. Virual key code is in first param
       ************************************************************************/

       case WM_KEYUP:
       {
            // Set this key as not pressed and return 0

            GLWin_Keydown[param1] = false;  return 0;

       }break;


       /************************************************************************
       Window closing: confirm quit with the user
       ************************************************************************/

       case WM_CLOSE:
       {
           // Ask the user does s/he want to quit

           bool quit = GLWin_Prompt
           (
              "Quit the game: are you sure ? ",
              "Confirm quit"
           );

           // If quit confirmed then shutdown OpenGL and destroy the window

           if ( quit )
           {
                // Shutdown OpenGL

                GLWin_ShutDownOpenGL();

                // Finish the game

                Game_End();

                // Ask windows to destroy the window

                DestroyWindow( winHandle );
           }
           else
           {
                // If the user hasn't quit then unpause the timer to discount time spent in dialog

                Timer_Unpause();
           }

           // Return 0 for success

           return 0;

       }break;

       /************************************************************************
       Window created: begin the game
       ************************************************************************/

       case WM_CREATE:
       {
            // Initialise the game

            Game_Init(); return 0;

       }break;

       /************************************************************************
       Window destroyed: terminate the program
       ************************************************************************/

       case WM_DESTROY:
       {
           // Stop program execution

           runProgram = false; return 0;

       }break;

    } // End switch ( message )

    // Call the default window procedure and return its result if we have not processed the message

    return DefWindowProc(winHandle,message,param1,param2);
}
