/*
 *  Overreact - Mr. 4th Dimention
 *      Allen Webster
 *  03.21.2015 (mm.dd.yyyy)
 *
 * Graphics Layer.
 */

// TOP

internal Blit_Rect
rect_from_target(Game_Render_Target *target){
    Blit_Rect result;
    result.x_start = 0;
    result.y_start = 0;
    result.x_end = target->width;
    result.y_end = target->height;
    
    return result;
}

internal void
bitmap_open_file(char *filename, Bitmap_File *bmp_file){
	File file = system_load_file(filename);
	if (file.data && file.size > sizeof(Bitmap_Header)){
		*bmp_file = {};
		bmp_file->file = file;
		bmp_file->header = *(Bitmap_Header*)file.data;
		bmp_file->byte_pitch =
			bmp_file->header.image_size / bmp_file->header.h;
		
		Assert(bmp_file->header.type == 0x4D42);
		Assert(bmp_file->header.compression == 0);
	}
}

internal i32
bitmap_data_requirement(Bitmap_File *bmp_file){
    i32 w, h;
    w = round_up_POT(bmp_file->header.w);
    h = round_up_POT(bmp_file->header.h);
    
	return 4*(w * h);
}

internal void
bitmap_fill_image(Bitmap_File *bmp_file, Image *image){
	image->width = round_up_POT(bmp_file->header.w);
    image->height = round_up_POT(bmp_file->header.h);
	image->pitch = 4*image->width;
    
    i32 true_width, true_height;
    true_width = bmp_file->header.w;
    true_height = bmp_file->header.h;
    
    image->img_width = true_width;
    image->img_height = true_height;
    
	i32 byte_per_pixel = bmp_file->header.bits_per_pixel >> 3;
	u8 *pixel_line = (u8*)bmp_file->file.data + bmp_file->header.offset;
	u8 *dest_line = (u8*)image->data;
	if (byte_per_pixel == 3){
		for (i32 y = 0; y < true_height; ++y){
			u8 *pixel = pixel_line;
			u32 *dest = (u32*)dest_line;
			for (i32 x = 0; x < true_width; ++x){
#if 0
				*dest = (0xFF << 24) | (pixel[2] << 16) | (pixel[1] << 8) | (pixel[0]);
#else
				*dest = (0xFF << 24) | (pixel[0] << 16) | (pixel[1] << 8) | (pixel[2]);
#endif
				pixel += 3;
				++dest;
			}
			pixel_line += bmp_file->byte_pitch;
			dest_line += image->pitch;
		}
	}
	else{
		for (i32 y = 0; y < true_height; ++y){
			u32 *pixel = (u32*)pixel_line;
			u32 *dest = (u32*)dest_line;
			for (i32 x = 0; x < true_width; ++x){
#if 0
				*dest =
					(*pixel & 0xFF00FF00) |
					((*pixel & 0x00FF0000) >> 16) |
					((*pixel & 0x000000FF) << 16);
#else
				*dest = *pixel;
#endif
				++pixel;
				++dest;
			}
			pixel_line += bmp_file->byte_pitch;
			dest_line += image->pitch;
		}
	}
}

internal void
bitmap_free_file(Bitmap_File *bmp_file){
	system_free_file(bmp_file->file);
}

// TODO(allen): eliminate this?
internal i32
font_init(){
    return 0;
}

inline internal i32
font_predict_size(i32 pt_size){
	return pt_size*pt_size*128;
}

// TODO(allen): switch to 0 = FAIL, 1 = SUCCESS
internal i32
font_load(char *filename, Font *font_out, i32 pt_size,
          void *font_block, i32 font_block_size){
    
    i32 result = 0;
    
    File file;
    file = system_load_file(filename);
    
    if (!file.data){
        result = 1;
    }
    
    else{
        stbtt_fontinfo font;
        if (!stbtt_InitFont(&font, (u8*)file.data, 0)){
            result = 1;
        }
        else{
            i32 ascent, descent, line_gap;
            real32 scale;
            
            stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap);
            scale = stbtt_ScaleForPixelHeight(&font, (real32)pt_size);
            
            real32 scaled_ascent, scaled_descent, scaled_line_gap;
            
            scaled_ascent = scale*ascent;
            scaled_descent = scale*descent;
            scaled_line_gap = scale*line_gap;
            
            font_out->height = (i32)(scaled_ascent - scaled_descent + scaled_line_gap);
            font_out->ascent = (i32)(scaled_ascent);
            font_out->descent = (i32)(scaled_descent);
            font_out->line_skip = (i32)(scaled_line_gap);
            
            u8 *memory_cursor = (u8*)font_block;
            Assert(pt_size*pt_size*128 <= font_block_size);
            
            i32 tex_width, tex_height;
            tex_width = pt_size*128;
            tex_height = pt_size*2;
            
            font_out->tex_width = tex_width;
            font_out->tex_height = tex_height;
            
            if (stbtt_BakeFontBitmap((u8*)file.data, 0, (real32)pt_size,
                                     memory_cursor, tex_width, tex_height, 0, 128, font_out->chardata) <= 0){
                result = 0;
            }
            
            else{
                GLuint font_tex;
                glGenTextures(1, &font_tex);
                glBindTexture(GL_TEXTURE_2D, font_tex);
                
                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, tex_width, tex_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, memory_cursor);
                
                font_out->tex = font_tex;
            }
            
            i32 max_advance = 0;
            
            for (u16 code_point = 0; code_point < 128; ++code_point){
                if (stbtt_FindGlyphIndex(&font, code_point) != 0){
                    font_out->glyphs[code_point].exists = 1;
                    font_out->glyphs[code_point].advance = font_out->chardata[code_point].xadvance;
                    i32 advance = Ceil(font_out->chardata[code_point].xadvance);
                    if (max_advance < advance){
                        max_advance = advance;
                    }
                }
            }
            font_out->advance = max_advance;
            
        }
        system_free_file(file);
    }
    
    return result;
}

internal void
font_draw_glyph_clipped(Game_Render_Target *target,
                        Font *font, u16 character,
                        real32 x, real32 y, u32 color,
                        Blit_Rect clip_box){
    
    if (clip_box.x_start < clip_box.x_end && clip_box.y_start < clip_box.y_end){
        real32 x_shift, y_shift;
        x_shift = font->chardata[character].xoff;
        y_shift = (real32)font->ascent;// + font->chardata[character].yoff;
        
        x += x_shift;
        y += y_shift;
        
        glScissor(clip_box.x_start,
                  target->height - clip_box.y_end,
                  clip_box.x_end - clip_box.x_start,
                  clip_box.y_end - clip_box.y_start);
        
        stbtt_aligned_quad q;
        stbtt_GetBakedQuadUnrounded(font->chardata, font->tex_width, font->tex_height, character, &x, &y, &q, 1);
        
        Vec3 c = unpack_color3(color);
        glColor3f(c.r, c.g, c.b);
        glBindTexture(GL_TEXTURE_2D, font->tex);
        glBegin(GL_QUADS);
        {
            glTexCoord2f(q.s0, q.t1); glVertex2f(q.x0, q.y1);
            glTexCoord2f(q.s1, q.t1); glVertex2f(q.x1, q.y1);
            glTexCoord2f(q.s1, q.t0); glVertex2f(q.x1, q.y0);
            glTexCoord2f(q.s0, q.t0); glVertex2f(q.x0, q.y0);
        }
        glEnd();
        
        glScissor(0, 0, target->width, target->height);
    }
}

internal void
font_draw_glyph(Game_Render_Target *target,
                Font *font, u16 character,
                real32 x, real32 y, u32 color){
    Blit_Rect screen = rect_from_target(target);
    font_draw_glyph_clipped(target, font, character, x, y, color, screen);
}