/*
 * General render target operations.
 */

// TOP

Bank_Begin_Setup_Sig(begin_setup){
    Asset_Bank_Setup setup = {0};
    setup.bank = bank;
    setup.trans_part = trans_part;
    setup.system = system;
    return(setup);
}

Bank_End_Setup_Sig(end_setup){}

Bank_Bind_Font_Sig(bind_font){
    System_API *system = setup->system;
    Asset_Bank *bank = setup->bank;
    Partition *part = setup->trans_part;
    Font *font = 0;
    i32 i = 0;
    
    font = get_font(bank, font_id);
    if (font){
		// TODO(tyler): Implementation of distance field text rendering for size-agnostic fonts
        // TODO(allen): Pick a good size for the texture based
        // on the size we want the font to be.
        // TODO(allen): Background asset streaming.
        Temp_Memory temp = begin_temp(part);
        u8 *temp_bitmap = push_array(part, u8, 512*512);
        File_Dump dump = system->DBG_dump_begin(file_name);
        u8 *ttf_buffer = push_array(part, u8, dump.size);
        
        if (system->DBG_dump_end(dump, ttf_buffer)){
            stbtt_fontinfo fontinfo;
            i32 ascent;
            f32 scale;
            f32 pixel_height = 16.f;
            
            stbtt_InitFont(&fontinfo, ttf_buffer, 0);
            scale = stbtt_ScaleForPixelHeight(&fontinfo, pixel_height);
            stbtt_GetFontVMetrics(&fontinfo, &ascent, 0, 0);
            
            font->texture_w = 512;
            font->texture_h = 512;
            font->pull_up = ascent*scale;
            font->id = font_id;
            
            stbtt_BakeFontBitmap(ttf_buffer, 0, pixel_height,
                                 temp_bitmap, font->texture_w, font->texture_h,
                                 0, 128, font->chardata, part);
            
            for (i = 0; i < 128; ++i){
                font->advance[i] = font->chardata[i].xadvance;
            }
            
            glGenTextures(1, &font->texture_id);
            glBindTexture(GL_TEXTURE_2D, font->texture_id);
            
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
            
            glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, 512, 512,
                         0, GL_ALPHA, GL_UNSIGNED_BYTE,
                         temp_bitmap);
            
#ifdef DEVELOPER
            {
                GLenum error = glGetError();
                GLenum copy = error;
                AllowLocal(copy);
            }
#endif
        }
        
        end_temp(temp);
    }
}

struct Bmp_Data{
	i32 w, h, byte_pitch;
	u16 bpp;
	u8 *data;
};

struct Bmp_File_Info{
	u16 type;
	u32 file_size;
	u16 res1, res2;
	u32 off_bits;

	u32 struct_size;
	i32 w, h;
	u16 planes;
	u16 bpp;
	u32 compression;
	u32 image_size;
	i32 xppm;
	i32 yppm;
	u32 colors_used;
	u32 colors_important;
};

b32
bmp_read(u8 *src_buffer, i32 size, Bmp_Data *bmp, Bmp_File_Info *bmp_info){
    u8 *ptr = src_buffer;
    b32 result = 0;
    
    bmp_info->type = PtrRead(ptr, u16);
    bmp_info->file_size = PtrRead(ptr, u32);
    bmp_info->res1 = PtrRead(ptr, u16);
    bmp_info->res2 = PtrRead(ptr, u16);
    bmp_info->off_bits = PtrRead(ptr, u32);
    
    if (bmp_info->type == 0x4D42){
        bmp_info->struct_size = PtrRead(ptr, u32);
        bmp_info->w = PtrRead(ptr, i32);
        bmp_info->h = PtrRead(ptr, i32);
        bmp_info->planes = PtrRead(ptr, u16);
        bmp_info->bpp = PtrRead(ptr, u16);
        bmp_info->compression = PtrRead(ptr, u32);
        bmp_info->image_size = PtrRead(ptr, u32);
        bmp_info->xppm = PtrRead(ptr, i32);
        bmp_info->yppm = PtrRead(ptr, i32);
        bmp_info->colors_used = PtrRead(ptr, u32);
        bmp_info->colors_important = PtrRead(ptr, u32);
        
        bmp->w = bmp_info->w;
        bmp->h = bmp_info->h;
        bmp->byte_pitch = bmp_info->image_size / bmp->h;
        bmp->bpp = bmp_info->bpp;
        
        if (bmp->byte_pitch == bmp->w*4){
            bmp->data = src_buffer + bmp_info->off_bits;
            result = 1;
        }
    }
    
    return(result);
}

struct POT_Texture{
    void *data;
    u32 w, h;
};

u32
round_up_pot(u32 x){
    --x;
    x |= (x >> 1);
    x |= (x >> 2);
    x |= (x >> 4);
    x |= (x >> 8);
    x |= (x >> 16);
    ++x;
    return(x);
}

POT_Texture
pot_bmp_texture(Partition *part, void *data, i32 w, i32 h, i32 pitch){
    POT_Texture result = {0};
    char *src_line = 0;
    char *dst_line = 0;
    char *pixel = 0;
    char r,g,b,a;
    i32 dst_pitch = 0;
    i32 y = 0, x = 0;
    
    result.w = round_up_pot((u32)w);
    result.h = round_up_pot((u32)h);
    dst_pitch = result.w*4;
    align_partition(part, 4);
    result.data = push_array(part, char, result.h*dst_pitch);
    memset(result.data, 0, result.h*dst_pitch);
    
    src_line = (char*)data;
    dst_line = (char*)result.data;
    
    for (y = 0; y < h; ++y){
        memcpy(dst_line, src_line, pitch);
        
        pixel = dst_line;
        for (x = 0; x < w; ++x){
            b = pixel[0];
            g = pixel[1];
            r = pixel[2];
            a = pixel[3];
            
            pixel[0] = r;
            pixel[1] = g;
            pixel[2] = b;
            pixel[3] = a;
            
            pixel += 4;
        }
        
        src_line += pitch;
        dst_line += dst_pitch;
    }
    
    return(result);
}

u32
load_bmp(System_API *system, Partition *part, Image *image, i32 image_id, char *file_name){
    u32 prev_tex_id = 0;
    if (image){
        // TODO(allen): Background asset streaming.
        Bmp_Data bmp;
        Bmp_File_Info bmp_info;
        
        Temp_Memory temp = begin_temp(part);
        File_Dump dump = system->DBG_dump_begin(file_name);
        u8 *bmp_buffer = push_array(part, u8, dump.size);
        if (system->DBG_dump_end(dump, bmp_buffer)){
            if (bmp_read(bmp_buffer, dump.size, &bmp, &bmp_info)){
                POT_Texture texture =
                    pot_bmp_texture(part, bmp.data, bmp.w, bmp.h, bmp.byte_pitch);
                
                image->tex_x = (f32)bmp.w/(f32)texture.w;
                image->tex_y = (f32)bmp.h/(f32)texture.h;
                image->width = (f32)bmp.w;
                image->height = (f32)bmp.h;
                image->id = image_id;
                
                prev_tex_id = image->texture_id;
                
                glGenTextures(1, &image->texture_id);
                glBindTexture(GL_TEXTURE_2D, image->texture_id);
                
                
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
                glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
                
                
                glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
                             texture.w, texture.h,
                             0, GL_RGBA, GL_UNSIGNED_BYTE,
                             texture.data);
            }
        }
        end_temp(temp);
    }
    return(prev_tex_id);
}

Bank_Bind_BMP_Sig(bind_bmp){
    Asset_Bank *bank = setup->bank;
    Partition *part = setup->trans_part;
    Image *image = get_image(bank, image_id);
    load_bmp(setup->system, part, image, image_id, file_name);
}

Bank_Replace_BMP_Sig(replace_bmp){
    u32 prev_tex_id = 0;
    Image *image = get_image(bank, image_id);
    prev_tex_id = load_bmp(system, part, image, image_id, file_name);
    if (prev_tex_id){
        glDeleteTextures(1, &prev_tex_id);
    }
}

#include <windows.h>
#include "cd_windows_render_vars.h"

#include "cd_gl_defines.h"

static void*
win32_load_gl_always(char *name, HMODULE module){
    void *p = (void *)wglGetProcAddress(name), *r = 0;
    if(p == 0 ||
       (p == (void*)0x1) || (p == (void*)0x2) || (p == (void*)0x3) ||
       (p == (void*)-1) ){
        r = (void *)GetProcAddress(module, name);
    }
    else{
        r = p;
    }
    return(r);
}

// TODO(allen): Need to move anything that couples
// rendering to platform into a separate layer.
Init_Sig(init){
    Win32_Render_Vars *vars = (Win32_Render_Vars*)render_vars;
    
    // GL context initialization
    {
        PIXELFORMATDESCRIPTOR format;
        int format_id;
        BOOL success;
        HDC dc;
        
        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 = 24;
        format.cStencilBits = 8;
        format.cAuxBuffers = 0;
        format.iLayerType = PFD_MAIN_PLANE;
        format.bReserved = 0;
        format.dwLayerMask = 0;
        format.dwVisibleMask = 0;
        format.dwDamageMask = 0;
        
        dc = GetDC(vars->hwnd);
        Assert(dc);
        format_id = ChoosePixelFormat(dc, &format);
        Assert(format_id != 0);
        success = SetPixelFormat(dc, format_id, &format);
        Assert(success == TRUE);
        
        HGLRC glcontext = wglCreateContext(dc);
        wglMakeCurrent(dc, glcontext);
        
        {
            HMODULE module = LoadLibraryA("opengl32.dll");
            
            wglCreateContextAttribsARB_Function *wglCreateContextAttribsARB = 0;
            wglCreateContextAttribsARB = (wglCreateContextAttribsARB_Function*)
                win32_load_gl_always("wglCreateContextAttribsARB", module);
            
            wglChoosePixelFormatARB_Function *wglChoosePixelFormatARB = 0;
            wglChoosePixelFormatARB = (wglChoosePixelFormatARB_Function*)
                win32_load_gl_always("wglChoosePixelFormatARB", module);
            
            if (wglCreateContextAttribsARB != 0 && wglChoosePixelFormatARB != 0){
                const int choosePixel_attribList[] =
                {
                    WGL_DRAW_TO_WINDOW_ARB, GL_TRUE,
                    WGL_SUPPORT_OPENGL_ARB, GL_TRUE,
                    WGL_DOUBLE_BUFFER_ARB, GL_TRUE,
                    WGL_PIXEL_TYPE_ARB, WGL_TYPE_RGBA_ARB,
                    WGL_COLOR_BITS_ARB, 32,
                    WGL_DEPTH_BITS_ARB, 24,
                    WGL_STENCIL_BITS_ARB, 8,
                    0,
                };
                
                int extended_format_id;
                UINT num_formats = 0;
                BOOL result = 0;
                
                result = wglChoosePixelFormatARB(dc,
                                                 choosePixel_attribList,
                                                 0,
                                                 1,
                                                 &extended_format_id,
                                                 &num_formats);
                
                if (result != 0 && num_formats > 0){
                    const int createContext_attribList[] = {
                        WGL_CONTEXT_MAJOR_VERSION_ARB, 3,
                        WGL_CONTEXT_MINOR_VERSION_ARB, 2,
                        WGL_CONTEXT_PROFILE_MASK_ARB, WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB,
                        0
                    };
                    
                    if (extended_format_id == format_id){
                        HGLRC extended_context =
                            wglCreateContextAttribsARB(dc,
                                                       0,
                                                       createContext_attribList);
                        if (extended_context){
                            wglMakeCurrent(dc, extended_context);
                            wglDeleteContext(glcontext);
                            glcontext = extended_context;
                            
                            MessageBoxA(0,(char*)glGetString(GL_VERSION), "OPENGL VERSION",0);
                        }
                    }
                }
            }
        }
        
        ReleaseDC(vars->hwnd, dc);
    }
    
    glEnable(GL_TEXTURE_2D);
    glEnable(GL_SCISSOR_TEST);
    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glClearColor(0,0,0,0);
}

Display_Sig(display){
    Win32_Render_Vars *vars = (Win32_Render_Vars*)render_vars;
    glFlush();
    HDC dc = GetDC(vars->hwnd);
    SwapBuffers(dc);
    ReleaseDC(vars->hwnd, dc);
}

Set_Screen_Sig(set_screen){
    if (w > 0 && h > 0){
        glViewport(0, h - out_h, out_w, out_h);
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glOrtho(0, out_w, 0, out_h, -1, 1);
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glScissor(0, h - out_h, out_w, out_h);
    }
}

// BOTTOM