/*
 * 4tech reusable win32 layer
 *   by Allen Webster
 * 08.20.2015
 *
 */

// TOP

#include <Windows.h>
#include <Windowsx.h>

struct Win32{
    HWND hwnd;
    HDC dc;
    bool keep_playing;
    
    Memory memory;
    Render_Target render_target;
    Mouse_State mouse, previous_mouse;
    
	HCURSOR cursor_ibeam;
	HCURSOR cursor_arrow;
	HCURSOR cursor_leftright;
	HCURSOR cursor_updown;
    
#if RENDER_MODE == OPENGL_RENDER
    HGLRC glcontext;
#endif
};

global Win32 win32;

internal void
win32_debug(char *msg){
    MessageBox(win32.hwnd,
               msg,
               "debug out",
               MB_OK);
}

#if RENDER_MODE == OPENGL_RENDER

internal void
win32_screen_init(){
    PIXELFORMATDESCRIPTOR format;
    int format_id;
    BOOL success;
    
    format.nSize = sizeof(format);
    format.nVersion = 1;
    format.dwFlags = PFD_SUPPORT_OPENGL | PFD_DRAW_TO_WINDOW | PFD_DOUBLEBUFFER;
    format.iPixelType = PFD_TYPE_RGBA;
    format.cColorBits = 32;
    format.cRedBits = 0;
    format.cRedShift = 0;
    format.cGreenBits = 0;
    format.cGreenShift = 0;
    format.cBlueBits = 0;
    format.cBlueShift = 0;
    format.cAlphaBits = 0;
    format.cAlphaShift = 0;
    format.cAccumBits = 0;
    format.cAccumRedBits = 0;
    format.cAccumGreenBits = 0;
    format.cAccumBlueBits = 0;
    format.cAccumAlphaBits = 0;
    format.cDepthBits = 16;
    format.cStencilBits = 0;
    format.cAuxBuffers = 0;
    format.iLayerType = PFD_MAIN_PLANE;
    format.bReserved = 0;
    format.dwLayerMask = 0;
    format.dwVisibleMask = 0;
    format.dwDamageMask = 0;
    
    win32.dc = GetDC(win32.hwnd);
    _Assert(win32.dc);
    format_id = ChoosePixelFormat(win32.dc, &format);
    _Assert(format_id != 0);
    success = SetPixelFormat(win32.dc, format_id, &format);
    _Assert(success == TRUE);
    
    win32.glcontext = wglCreateContext(win32.dc);
    wglMakeCurrent(win32.dc, win32.glcontext);
    glEnable(GL_SCISSOR_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    
    glClearColor(0,0,0,0);
    glFlush();
    SwapBuffers(win32.dc);
    ShowWindow(win32.hwnd, SW_SHOW);
}

internal void
win32_resize_screen(i32 w, i32 h){
    if (w > 0 && h > 0){
        glViewport(0, 0, w, h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, w, h, 0, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glScissor(0, 0, w, h);
        
        win32.render_target.w = w;
        win32.render_target.h = h;
    }
}

internal void
win32_redraw_screen(){
    glFlush();
    SwapBuffers(win32.dc);
}

internal void
win32_screen_free(){
    wglMakeCurrent(0, 0);
    wglDeleteContext(win32.glcontext);
}

#endif

internal void
win32_track_leave(){
    TRACKMOUSEEVENT track;
    track.cbSize = sizeof(track);
    track.dwFlags = TME_LEAVE;
    track.hwndTrack = win32.hwnd;
    TrackMouseEvent(&track);
}

internal u64
win32_get_time(){
    LARGE_INTEGER time;
    QueryPerformanceCounter(&time);
    return time.QuadPart;
}

LRESULT ColorWindowProc(
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam){
    LRESULT result = {};
    switch (uMsg){
    case WM_SIZE:
    {
        i32 w = LOWORD(lParam);
        i32 h = HIWORD(lParam);
        win32_resize_screen(w, h);
    }break;
        
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        BeginPaint(hwnd, &ps);
        
        App_Step_Data step;
        step.memory = win32.memory;
        step.render_target = win32.render_target;
        step.mouse = win32.mouse;
        App_Step_Out step_out = {};
        app_step(&step, &step_out);
        win32_redraw_screen();
        
        EndPaint(hwnd, &ps);
    }break;
    
    case WM_CLOSE:
    case WM_DESTROY:
    {
        win32.keep_playing = 0;
    }break;
    
    case WM_SYSKEYDOWN:
    case WM_SYSKEYUP:
    case WM_KEYDOWN:
    case WM_KEYUP:
    {
        
    }break;
    
	case WM_LBUTTONDOWN:
	{
		win32.mouse.l = 1;
	}break;
	case WM_RBUTTONDOWN:
	{
		win32.mouse.r = 1;
	}break;
	
	case WM_LBUTTONUP:
	{
		win32.mouse.l = 0;
	}break;
	case WM_RBUTTONUP:
	{
		win32.mouse.r = 0;
	}break;
	
    case WM_MOUSEMOVE:
    {
        win32.mouse.x = GET_X_LPARAM(lParam);
        win32.mouse.y = GET_Y_LPARAM(lParam);
        win32.mouse.in_window = 1;
        win32_track_leave();
    }break;
    
    case WM_MOUSEWHEEL:
    {
        win32.mouse.wheel = GET_WHEEL_DELTA_WPARAM(wParam);
    }break;
    
    case WM_MOUSELEAVE:
    {
        win32.mouse.in_window = 0;
    }break;
    
    case WM_KILLFOCUS:
    {
        win32.mouse = {};
    }break;
    
    default:
    {
        result = DefWindowProc(hwnd, uMsg, wParam, lParam);
    }break;
    }
    return result;
}

int
WinMain(
    HINSTANCE hInstance,
    HINSTANCE hPrevInstance,
    LPSTR lpCmdLine,
    int nCmdShow){
    win32 = {};
    
    WNDCLASSEX winclass;
    winclass.cbSize = sizeof(WNDCLASSEX);
    winclass.style = CS_OWNDC | CS_HREDRAW | CS_VREDRAW;
    winclass.lpfnWndProc = ColorWindowProc;
    winclass.cbClsExtra = 0;
    winclass.cbWndExtra = 0;
    winclass.hInstance = hInstance;
    winclass.hIcon = 0;
    winclass.hCursor = 0;
    winclass.hbrBackground = 0;
    winclass.lpszMenuName = 0;
    winclass.lpszClassName = "color-class";
    winclass.hIconSm = 0;
    
    ATOM register_result = RegisterClassEx(&winclass);
    _Assert(register_result);
    
    i32 w, h;
    w = 800;
    h = 600;
    
    RECT window_rect;
    window_rect.left = 0;
    window_rect.top = 0;
    window_rect.right = w;
    window_rect.bottom = h;
    
    AdjustWindowRect(&window_rect, WS_OVERLAPPEDWINDOW, 0);
    
    HWND hwnd =
        CreateWindowEx(0,
                       "color-class",
                       "4tech colors",
                       WS_OVERLAPPEDWINDOW,
                       CW_USEDEFAULT,
                       CW_USEDEFAULT,
                       window_rect.right - window_rect.left,
                       window_rect.bottom - window_rect.top,
                       0,
                       0,
                       hInstance,
                       0);
    _Assert(hwnd);
    
    win32.hwnd = hwnd;
    win32.dc = GetDC(win32.hwnd);
    win32_screen_init();
    
    GetClientRect(win32.hwnd, &window_rect);
    
    win32_resize_screen(window_rect.right - window_rect.left,
                        window_rect.bottom - window_rect.top);
    
    win32.memory.size = Mbytes(1);
    win32.memory.mem = VirtualAlloc((LPVOID)Tbytes(2),
                                    win32.memory.size,
                                    MEM_RESERVE|MEM_COMMIT,
                                    PAGE_READWRITE);
    _Assert(win32.memory.mem);
    
    win32.cursor_ibeam = LoadCursor(NULL, IDC_IBEAM);
    win32.cursor_arrow = LoadCursor(NULL, IDC_ARROW);
    win32.cursor_leftright = LoadCursor(NULL, IDC_SIZEWE);
    win32.cursor_updown = LoadCursor(NULL, IDC_SIZENS);
    
    u64 frame_target = MILLION / 30;
    u64 frame_start, frame_end;
    u64 frame_used;
    
    timeBeginPeriod(1);
    win32.keep_playing = 1;
    frame_start = win32_get_time();
    while (win32.keep_playing){
        MSG msg;
        win32.previous_mouse = win32.mouse;
        while (PeekMessage(&msg,
                           0,0,0,
                           PM_REMOVE)){
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
        win32.mouse.pl = win32.mouse.l && !win32.previous_mouse.l;
        win32.mouse.pr = win32.mouse.r && !win32.previous_mouse.r;
        
        App_Step_Data step;
        step.memory = win32.memory;
        step.render_target = win32.render_target;
        step.mouse = win32.mouse;
        
        App_Step_Out step_out = {};
        app_step(&step, &step_out);
        win32_redraw_screen();

        switch (step_out.mouse_cursor){
        case APP_MOUSE_CURSOR_IBEAM:
            SetCursor(win32.cursor_ibeam);
            break;
            
        case APP_MOUSE_CURSOR_ARROW:
            SetCursor(win32.cursor_arrow);
            break;
            
        case APP_MOUSE_CURSOR_LEFTRIGHT:
            SetCursor(win32.cursor_leftright);
            break;
            
        case APP_MOUSE_CURSOR_UPDOWN:
            SetCursor(win32.cursor_updown);
            break;
        }
        
        frame_end = win32_get_time();
        frame_used = frame_end - frame_start;
        if (frame_used < frame_target){
            Sleep((DWORD)(frame_target - frame_used) / 1000);
        }
        frame_start = win32_get_time();
    }
    
    return 0;
}

// BOTTOM