#include <windows.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <string>
#include <iostream>
#include "qr_common_vectors.h"
#include "qr_events.h"
#include "qr_driver_base.h"
#include "qr_opengl_common.h"
#include "qr_win32_driver.h"
namespace qr
{
    namespace internal
    {
        driver *driver_instance = NULL; //hack to alow access to class instance from windows callback
/*********************************************************************/
        driver::driver(
            vector2d_i viewp_aspr, float project_width, bool fullscreen,
            vector2d_i size_hint, bool vsync, int multisample)
        {
        // store a copy of the aspect ratio
            this->viewp_aspr    = viewp_aspr;
            this->project_width = project_width;
            this->multisample   = multisample;
            this->vsync         = vsync;
            this->fullscreen    = fullscreen;
        // Check if there is already a driver instance
            if(driver_instance == NULL)
                driver_instance=this;
            else
            {
                std::cout<<"A driver already exists\n";
                throw 1;
            }
            
        // Find the current screen resolution
            vector2d_i screen_size = get_screen_res();
        // If alt res is 0 or full screen mode is enabled, set the window resolution to
        // the screen resolution
            if((size_hint.X == 0 && size_hint.Y == 0) || this->fullscreen == true)
                this->window_size = screen_size;
            else
                this->window_size = size_hint;
        // Set up window
            create_window();
        }
/*********************************************************************/
        void driver::create_window()
        {
        // Register window class
            register_class();
            vector2d_i screen_res = this->get_screen_res();
            set_window_size(screen_res);
            if(this->fullscreen)
                enable_fullscreen();
            make_window();
        //create device context
            this->h_dc=GetDC(h_wnd);
        //get device context
            this->h_dc=GetDC(h_wnd);
            set_pix_format();
        // Create and activate GL context
            this->h_rc=wglCreateContext(h_dc);
            wglMakeCurrent(h_dc,h_rc);
        // Make the window visible
            ShowWindow(h_wnd,SW_SHOW); 
            SetForegroundWindow(h_wnd);
            SetFocus(h_wnd);
        // Resize gl viewport
            resize_viewport();
            set_projection();
            init();
        }
/*********************************************************************/
        void driver::destroy_window()
        {
            if (fullscreen)
            {
                ChangeDisplaySettings(NULL,0);  
                ShowCursor(TRUE);              
            }
            if (h_rc)                    
            {
                wglMakeCurrent(NULL,NULL);
                wglDeleteContext(h_rc);   
            }
            if (h_dc)
            {
                ReleaseDC(h_wnd,h_dc);
            }
            if (h_wnd)
            {
                DestroyWindow(h_wnd);
            }
            UnregisterClass("Quad-ren",h_instance);
        }
/*********************************************************************/
        bool driver::get_event(event **nevent)
        {
            MSG     msg;
            *nevent = NULL;
            if (PeekMessage(&msg,h_wnd,0,0,PM_REMOVE))
            {
                std::cout<<"msg"<<msg.message<<" \n";
                if (msg.message == WM_QUIT)
                {
                    std::cout<<"quit\n";
                    return false;
                }
                else if (msg.message == WM_KEYDOWN)
                {
                    std::cout<<"key pressed"<<msg.wParam<<"\n";
                    *(nevent) = new keydown_event(msg.wParam);
                }
                else if (msg.message == WM_KEYUP)
                {
                    std::cout<<"key released"<<msg.wParam<<"\n";
                    *(nevent) = new keyup_event(msg.wParam);
                }
                else
                {
                    TranslateMessage(&msg);
                    DispatchMessage(&msg);   
                }
            }
       //     std::cout<<nevent;
            return true;
        }
/*********************************************************************/
        LRESULT CALLBACK driver::WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
        {
            switch (uMsg)                                   // Check For Windows Messages
            {
                case WM_SYSCOMMAND:                         // Intercept System Commands
                {
                    switch (wParam)                         // Check System Calls
                    {
                        case SC_SCREENSAVE:                 // Screensaver Trying To Start?
                        case SC_MONITORPOWER:               // Monitor Trying To Enter Powersave?
                        return 0;                           // Prevent From Happening
                    }
                    break;                                  // Exit
                }
                
                case WM_CLOSE:                              // Did We Receive A Close Message?
                {
                    std::cout<<"quit\n";
                    PostQuitMessage(0);                     // Send A Quit Message
                    return 0; 
                }
                case WM_PAINT:                        
                {
                    driver_instance->draw();
                    return 0;  
                }
                case WM_SIZE:                        
                {
                    driver_instance->set_window_size(qr::vector2d_i(LOWORD(lParam), HIWORD(lParam)));
                    driver_instance->resize_viewport();
                    driver_instance->draw();
                    return 0;                         
                }
            }
            return DefWindowProc(hWnd,uMsg,wParam,lParam);
        }
/*********************************************************************/
        void driver::register_class()
        {
        // Set up window class
            this->h_instance = GetModuleHandle(NULL);
            wc.style         = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
            wc.lpfnWndProc   = (WNDPROC) WndProc;
            wc.cbClsExtra    = 0;
            wc.cbWndExtra    = 0;
            wc.hInstance     = this->h_instance;
            wc.hIcon         = LoadIcon(NULL,   IDI_WINLOGO);
            wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
            wc.hbrBackground = NULL;
            wc.lpszMenuName  = NULL;
            wc.lpszClassName = "Quad-ren";
            if(!RegisterClass(&wc))
            {
                std::cout<<"Failed to register window class\n";
                throw int(1);
            }
        }
/*********************************************************************/
        void driver::make_window()
        {
            int dw_ex_style = 0;
            int dw_style   = 0;
            if (fullscreen)                                             // Are We Still In Fullscreen Mode?
            {
                dw_ex_style = WS_EX_APPWINDOW;                              // Window Extended Style
                dw_style    = WS_POPUP;                                       // Windows Style
                ShowCursor(false);                                      // Hide Mouse Pointer
            }
            else
            {
                dw_ex_style = WS_EX_APPWINDOW | WS_EX_WINDOWEDGE;           // Window Extended Style
                dw_style    = WS_OVERLAPPEDWINDOW;                            // Windows Style
            }
        // Create window
            if (!(h_wnd=CreateWindowEx(
                dw_ex_style,                          // Extended Style For The Window
                "Quad-ren",                           // Class Name
                "",                                   // Window title
                dw_style |
                WS_CLIPSIBLINGS |                     // Required Window Style
                WS_CLIPCHILDREN,                      // Required Window Style
                0, 0,                                 // Window Position
                this->window_size.X,                  // Calculate Window Width
                this->window_size.Y,                  // Calculate Window Height
                NULL,                                 // No Parent Window
                NULL,                                 // No Menu
                h_instance,                           // Instance
                NULL)))                               // Don't Pass Anything To WM_CREATE
            {
                std::cout<<"Failed to create window\n";
                throw int(1);
            }
        }
/*********************************************************************/
        void driver::set_pix_format()
        {
            static  PIXELFORMATDESCRIPTOR pfd=              // pfd Tells Windows How We Want Things To Be
            {
                sizeof(PIXELFORMATDESCRIPTOR),              // Size Of This Pixel Format Descriptor
                1,                                          // Version Number
                PFD_DRAW_TO_WINDOW |                        // Format Must Support Window
                PFD_SUPPORT_OPENGL |                        // Format Must Support OpenGL
                PFD_DOUBLEBUFFER,                           // Must Support Double Buffering
                PFD_TYPE_RGBA,                              // Request An RGBA Format
                24,                                       // Select Our 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,                                 // Accumulation Bits Ignored
                16,                                         // 16Bit Z-Buffer (Depth Buffer)  
                0,                                          // No Stencil Buffer
                0,                                          // No Auxiliary Buffer
                PFD_MAIN_PLANE,                             // Main Drawing Layer
                0,                                          // Reserved
                0, 0, 0                                     // Layer Masks Ignored
            };
            unsigned int PixelFormat = ChoosePixelFormat(h_dc,&pfd);
            SetPixelFormat(h_dc, PixelFormat,&pfd);
        }
/*********************************************************************/
        void driver::enable_fullscreen()
        {
            if (fullscreen)
            {
                vector2d_i screen_res = this->get_screen_res();
                DEVMODE dmScreenSettings;                               // Device Mode
                memset(&dmScreenSettings, 0, sizeof(dmScreenSettings)); // Makes Sure Memory's Cleared
                dmScreenSettings.dmSize=sizeof(dmScreenSettings);       // Size Of The Devmode Structure
                dmScreenSettings.dmPelsWidth    = screen_res.X;                // Selected Screen Width
                dmScreenSettings.dmPelsHeight   = screen_res.Y;               // Selected Screen Height
                dmScreenSettings.dmBitsPerPel   = 32;                   // Selected Bits Per Pixel
                dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT;
                // Try To Set Selected Mode And Get Results.  NOTE: CDS_FULLSCREEN Gets Rid Of Start Bar.
                if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL)
                {
                    // If The Mode Fails, Offer Two Options.  Quit Or Use Windowed Mode.
                    if (MessageBox(NULL,"The Requested Fullscreen Mode Is Not Supported By\nYour Video Card. Use Windowed Mode Instead?","NeHe GL",MB_YESNO|MB_ICONEXCLAMATION)==IDYES)
                    {
                        fullscreen=FALSE;       // Windowed Mode Selected.  Fullscreen = FALSE
                    }
                    else
                    {
                        // Pop Up A Message Box Letting User Know The Program Is Closing.
                        MessageBox(NULL,"Program Will Now Close.","ERROR",MB_OK|MB_ICONSTOP);
                        throw 1;
                        //return FALSE;                                   // Return FALSE
                    }
                }
            }
        }
/*********************************************************************/
        void driver::set_window_size(vector2d_i new_window_size)
        {
            RECT        window_rect;
            window_rect.left   = 0;
            window_rect.right  = new_window_size.X;
            window_rect.top    = 0;
            window_rect.bottom = new_window_size.Y;
       // Adjust window size to account for borders.
            AdjustWindowRectEx(&window_rect, WS_OVERLAPPEDWINDOW, false, WS_EX_APPWINDOW);
            this->window_size.X = window_rect.right  - window_rect.left;
            this->window_size.Y = window_rect.bottom - window_rect.top;
        }
    }
}
int main()
{
    qr::internal::driver *drv = new qr::internal::driver(qr::vector2d_i(16,9), 10);
    qr::event *nevent;
    while(drv->get_event(&nevent))
    {
        if(nevent != NULL && nevent->get_type() == qr::EV_KEYDOWN)
        {
            qr::keydown_event *devent = (qr::keydown_event*) nevent;
            if(devent->get_keysym() == VK_SPACE)
                drv->toggle_fullscreen();
        }
        drv->draw();
    }
}