// define our window width and height variables
int ww, wh;


/*************************************************************************
WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    
    This function is called every time Windows wants to tell our window
    something. hWnd is the handle to our window, uMsg is what Windows is
    telling us, and wParam and lParam are extra parameters for that message
    (ex. new width and height when our window is resized).

*************************************************************************/
LONG WINAPI WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { 
    
    // PAINTSTRUCT: temporary variable for painting to the window
    static PAINTSTRUCT ps;

    // The window may have received a message, so lets check what it 
    // is and do stuff accordingly
    switch(uMsg) {

        case WM_PAINT:
            // window is being drawn, so call our Display() function to handle it.
            
            BeginPaint(hWnd, &ps);      // tell Windows to paint our window
            Display();                  // run the function to display our game
            EndPaint(hWnd, &ps);        // now tell Windows we're done painting our window
            return 0;

        case WM_SIZE:
            // window was resized, so call our Resize() function to handle it.
            Resize(LOWORD(lParam), HIWORD(lParam));
            
            // tell Windows to repaint our window because it has changed
            PostMessage(hWnd, WM_PAINT, 0, 0);
            return 0;

        case WM_CLOSE:
            PostQuitMessage(0);
            return 0;
    }

    return DefWindowProc(hWnd, uMsg, wParam, lParam); 
} 


/*************************************************************************
CreateOpenGLWindow(char* title, int x, int y, int width, int height)
    
    This function handles creating the actual window. It's called by the WinMain
    function which will be defined a little later.
    
    Creating a window requires a WNDCLASS (window class) Object. In this class
    we tell Windows about our window's cursor, icon, etc. It also tells Windows
    what function to call if anything happens to our window, in this case the 
    function is WindowProc();. 
    
    Creating an OpenGL window also requires setting a PIXELFORMATDESCRIPTOR, 
    which tells Windows exactly what kind of window we want to use OpenGL in,
    whether its 16bit color, 24bit color, etc.
    
    Finally, this function will return the handle to our window (hWnd) so we can
    work with it.

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

HWND CreateOpenGLWindow(char* title, int x, int y, int width, int height) {
    int         pf;
    HDC         hDC;
    HWND        hWnd;
    WNDCLASS    wc;
    static HINSTANCE hInstance = 0;

    /* only register the window class once - use hInstance as a flag. */
    if (!hInstance) {
        hInstance = GetModuleHandle(NULL);
        wc.style         = CS_OWNDC;
        wc.lpfnWndProc   = (WNDPROC)WindowProc;
        wc.cbClsExtra    = 0;
        wc.cbWndExtra    = 0;
        wc.hInstance     = hInstance;
        wc.hIcon         = LoadIcon(NULL, IDI_WINLOGO);
        wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
        wc.hbrBackground = NULL;
        wc.lpszMenuName  = NULL;
        wc.lpszClassName = "OpenGL";

        if (!RegisterClass(&wc)) {
            MessageBox(NULL, "RegisterClass() failed", "Error", MB_OK);
            return NULL;
        }
    }

    // create the window using our "OpenGL" window class.
    
    hWnd = CreateWindow("OpenGL", title, WS_OVERLAPPEDWINDOW |
            WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
            x, y, width, height, NULL, NULL, hInstance, NULL);


    // if the window wasn't created for some reason, quit with an error
    // message.
    if (hWnd == NULL) {
        MessageBox(NULL, "CreateWindow() failed", "Error", MB_OK);
        return NULL;
    }

    // get the current "device context". don't worry about what this is.
    hDC = GetDC(hWnd);

    // set up our PIXELFORMATDESCRIPTOR properties
    PIXELFORMATDESCRIPTOR pfd = { 
        sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd 
        1,                     // version number 
        PFD_DRAW_TO_WINDOW |   // support window 
        PFD_SUPPORT_OPENGL |   // support OpenGL 
        PFD_DOUBLEBUFFER,      // double buffered 
        PFD_TYPE_RGBA,         // RGBA type 
        24,                    // 24-bit color depth 
        0, 0, 0, 0, 0, 0,      // color bits ignored 
        0,                     // no alpha buffer 
        0,                     // shift bit ignored 
        0,                     // no accumulation buffer 
        0, 0, 0, 0,            // accum bits ignored 
        32,                    // 32-bit z-buffer 
        0,                     // no stencil buffer 
        0,                     // no auxiliary buffer 
        PFD_MAIN_PLANE,        // main layer 
        0,                     // reserved 
        0, 0, 0                // layer masks ignored 
    }; 

    // tell windows to choose a pixel format based on what we want
    pf = ChoosePixelFormat(hDC, &pfd);
    if (pf == 0) {
        MessageBox(NULL, "ChoosePixelFormat() failed", "Error", MB_OK); 
        return 0;
    } 
    
    // now set the pixel format
    if (SetPixelFormat(hDC, pf, &pfd) == false) {
        MessageBox(NULL, "SetPixelFormat() failed", "Error", MB_OK);
        return 0;
    } 

    // describe the pixelformat for our device context
    DescribePixelFormat(hDC, pf, sizeof(PIXELFORMATDESCRIPTOR), &pfd);

    // release the device context
    ReleaseDC(hWnd, hDC);

    // return our created window's handle (hWnd)
    return hWnd;
}    


/*************************************************************************
WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, 
        LPSTR lpszCmdLine, int nCmdShow)
    
    WinMain is the standard function to start a Windows program. In here we
    create the window, set everything up, and then enter a loop. This loop 
    checks for messages sent to our window, handles them, does our game-specific
    calculations, and displays our OpenGL stuff.
    
    Also in this loop we run two of our own functions. One is Display(), which 
    executes all the OpenGL commands we want to use. After doing Display(), we
    tell OpenGL to glFinish up anything it still has to do, and then swap our 
    buffers. The second is Idle(), which handles moving objects around, getting 
    keyboard input, etc.
    
    So what exactly is swapping buffers? When we draw stuff to the screen, 
    the fastest way to do it is to use two buffers. The first buffer is what
    is actually on the screen. The second is a "back buffer". We draw all our 
    new stuff to this back buffer, and then we dump everything from it onto the
    screen buffer.

*************************************************************************/
int APIENTRY WinMain(HINSTANCE hCurrentInst, HINSTANCE hPreviousInst, 
                     LPSTR lpszCmdLine, int nCmdShow) {
    HDC hDC;                /* device context */
    HGLRC hRC;              /* opengl context */
    HWND  hWnd;             /* window */
    MSG   msg;              /* message */

    
    // call our CreateOpenGLWindow function
    hWnd = CreateOpenGLWindow("Simple OpenGL App", 0, 0, 1024, 768);
    if (hWnd == NULL) exit(1);

    // get the device context of our created window
    hDC = GetDC(hWnd);
    
    // call a Windows GL (wgl) function to get a Rendering Context
    // this is for windows to know what window we're rendering on with OpenGL
    hRC = wglCreateContext(hDC);
    
    // tell Windows to make our new Rendering Context current
    wglMakeCurrent(hDC, hRC);

    // Run our Init function to set up OpenGL settings and other stuff
    Init();

    // show and update our window
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);


    // Now we enter our main loop. At this point, the window is made and
    // we're all set to render
    
    while (1) {
        while(PeekMessage(&msg, hWnd, 0, 0, PM_NOREMOVE)) {
            
            // if there's a message for us, then translate it and 
            // dispatch it
            if(GetMessage(&msg, hWnd, 0, 0)) {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            } 
            
            // otherwise, the window has been closed and isn't
            // receiving any more messages
            else {
                goto quit;
            }
        }
        // Call the display function
        Display();
        
        // Tell OpenGL to finish up all it's instructions before moving on
        glFinish();
        
        // Swap our display buffers
        SwapBuffers(hDC);
        
        // Run other game-specific calculations
        Idle();
    }

quit:

    // The user closed the window, so we have to shut down.
    
    // make no OpenGL context current, we can't shut down a rendering context
    // if it's still active.
    wglMakeCurrent(NULL, NULL);
    
    // release our Device Context
    ReleaseDC(hWnd, hDC);
    
    // Delete our rendering context
    wglDeleteContext(hRC);
    
    // destroy our window
    DestroyWindow(hWnd);

    // return something-or-other =]
    return msg.wParam;
}






/*************************************************************************
Init()
	
	This function is called to initialize OpenGL variables for our program.
	You can also put anything else you want to be done before the program 
	starts going.

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

void Init() {

    // clear the screen with a black color
    glClearColor (0, 0, 0, 1);
    glDrawBuffer(GL_BACK);
    glEnable(GL_LIGHTING);
    glEnable(GL_DEPTH_TEST);
    glShadeModel(GL_SMOOTH);

    glEnable(GL_POINT_SMOOTH);
    glEnable(GL_POLYGON_SMOOTH);
    glEnable(GL_BLEND);
    glEnable(GL_ALPHA);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glAlphaFunc(GL_GEQUAL, 0.05);
    glPointSize(4);
    glLineWidth(4);

    glEnable(GL_COLOR_MATERIAL);
    glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE);
    
}



/*************************************************************************
Resize(int w, int h)    
	
	This function is called whenever our window gets resized or moved. OpenGL
	requires that we tell it where the new rendering position is, otherwise 
	it'll keep rendering in spots that don't belong to our window.

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

void Resize(int w, int h) {
    ww = w;
    wh = h;
    glViewport(0, 0, ww, wh);
}






/*************************************************************************
Display()   
	
	This function executes gl commands to display stuff onscreen. It also
	sets up the position of the viewer and other display-related stuff.
	In this tutorial, nothing is placed in here to keep it simple.

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

void Display() {

	// clear the window to our glClearColor
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

}






/*************************************************************************
Idle()
	
	This function is used to calculate player movement, AI, collision 
	detection, etc. It could be placed in the Display() function, but
	that just mixes things up.

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

void Idle() {

}