/*
 * Mr. 4th Dimention - Allen Webster
 *
 * 12.12.2014
 *
 * Win32 font rendering for nicer fonts
 *
 */

// TOP

struct GMetrics{
    f32 advance;
    f32 xoff;
    f32 xoff2;
};

internal b32
win32_glyph_metrics(HDC dc, int code, GMetrics *gmetrics){
    b32 result = false;
    ABCFLOAT abc = {0};
    INT width;
    
    if (GetCharWidth32W(dc, code, code, &width)){
        gmetrics->advance = (f32)width;
        if (GetCharABCWidthsFloat(dc, code, code, &abc)){
            gmetrics->xoff = abc.abcfA;
            gmetrics->xoff2 = (abc.abcfA + abc.abcfB);
            
            result = true;
        }
    }
    
    return(result);
}

internal i32
win32_draw_font_load(Partition *part,
                     Render_Font *font_out,
                     char *filename_untranslated,
                     char *fontname,
                     i32 pt_size,
                     i32 tab_width,
                     i32 oversample,
                     b32 store_texture){
    
    char space_[1024];
    String filename = make_fixed_width_string(space_);
    b32 translate_success = sysshared_to_binary_path(&filename, filename_untranslated);
    
    i32 result = 0;
    
    if (translate_success){
        HDC dc = GetDC(win32vars.window_handle);
        
        AddFontResourceEx(filename.str, FR_PRIVATE, 0);
        
        HFONT font_handle = CreateFont(
            pt_size,
            0,
            0,
            0,
            FW_NORMAL,
            FALSE,
            FALSE,
            FALSE,
            ANSI_CHARSET,
            OUT_DEFAULT_PRECIS,
            CLIP_DEFAULT_PRECIS,
            DEFAULT_QUALITY,
            FF_DONTCARE | DEFAULT_PITCH,
            fontname
            );
        HBITMAP bmp_handle = CreateBitmap(
            pt_size*2, pt_size*2,
            4, 32,
            0
            );
        
        
        if (font_handle != 0 && bmp_handle != 0){
            SelectObject(dc, font_handle);
            SelectObject(dc, bmp_handle);
            
            memset(font_out, 0, sizeof(*font_out));
            
            TEXTMETRIC metric;
            if (GetTextMetrics(dc, &metric)){
                
                font_out->height = (i32)(metric.tmHeight + metric.tmExternalLeading);
                font_out->ascent = (i32)(metric.tmAscent);
                font_out->descent = (i32)(metric.tmDescent);
                font_out->line_skip = (i32)(metric.tmExternalLeading);
                
                if (!store_texture){
                    result = 1;
                }
                else{
                    Temp_Memory temp = begin_temp_memory(part);
                    
                    i32 tex_width = pt_size*16;
                    i32 tex_height = pt_size*16;
                    void *block = sysshared_push_block(part, tex_width * tex_height);
                    
                    font_out->tex_width = tex_width;
                    font_out->tex_height = tex_height;
                    
                    result = 1;
                    /////////////////////////////////////////////////////////////////
                    stbtt_pack_context spc_;
                    
                    int pack_result;
                    {
                        stbtt_pack_context *spc = &spc_;
                        unsigned char  *pixels = (u8*)block;
                        int pw = tex_width;
                        int ph = tex_height;
                        int stride_in_bytes = tex_width;
                        int padding = 1;
                        void *alloc_context = part;
                        
                        {
                            stbrp_context *context = (stbrp_context *) STBTT_malloc(sizeof(*context)            ,alloc_context);
                            int            num_nodes = pw - padding;
                            stbrp_node    *nodes   = (stbrp_node    *) STBTT_malloc(sizeof(*nodes  ) * num_nodes,alloc_context);
                            
                            if (context == NULL || nodes == NULL) {
                                if (context != NULL) STBTT_free(context, alloc_context);
                                if (nodes   != NULL) STBTT_free(nodes  , alloc_context);
                                pack_result = 0;
                                goto packbegin_end;
                            }
                            
                            spc->user_allocator_context = alloc_context;
                            spc->width = pw;
                            spc->height = ph;
                            spc->pixels = pixels;
                            spc->pack_info = context;
                            spc->nodes = nodes;
                            spc->padding = padding;
                            spc->stride_in_bytes = stride_in_bytes != 0 ? stride_in_bytes : pw;
                            spc->h_oversample = 1;
                            spc->v_oversample = 1;
                            
                            stbrp_init_target(context, pw-padding, ph-padding, nodes, num_nodes);
                            
                            STBTT_memset(pixels, 0, pw*ph); // background of 0 around pixels
                            
                            pack_result = 1;
                            goto packbegin_end;
                        }
                    }
                    packbegin_end:;
                    
                    if (pack_result){
                        
                        int pack_font_range_result;
                        
                        {
                            stbtt_pack_range range;
                            range.first_unicode_char_in_range = 0;
                            range.num_chars_in_range          = 128;
                            range.chardata_for_range          = font_out->chardata;
                            range.font_size                   = STBTT_POINT_SIZE((f32)pt_size);
                            
                            stbtt_pack_context *spc = &spc_;
                            stbtt_pack_range *ranges = ⦥
                            int num_ranges = 1;
                            
                            {
                                int i,j,k,n, return_value = 1;
                                stbrp_context *context = (stbrp_context *) spc->pack_info;
                                stbrp_rect    *rects;
                                
                                // flag all characters as NOT packed
                                for (i=0; i < num_ranges; ++i)
                                    for (j=0; j < ranges[i].num_chars_in_range; ++j)
                                    ranges[i].chardata_for_range[j].x0 =
                                    ranges[i].chardata_for_range[j].y0 =
                                    ranges[i].chardata_for_range[j].x1 =
                                    ranges[i].chardata_for_range[j].y1 = 0;
                                
                                n = 0;
                                for (i=0; i < num_ranges; ++i)
                                    n += ranges[i].num_chars_in_range;
                                
                                rects = (stbrp_rect *) STBTT_malloc(sizeof(*rects) * n, spc->user_allocator_context);
                                if (rects == NULL){
                                    pack_font_range_result = 0;
                                    goto pack_font_range_end;
                                }
                                
                                //info.userdata = spc->user_allocator_context;
                                //stbtt_InitFont(&info, fontdata, stbtt_GetFontOffsetForIndex(fontdata,font_index));
                                k=0;
                                for (i=0; i < num_ranges; ++i) {
                                    //float fh = ranges[i].font_size;
                                    //float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh);
                                    for (j=0; j < ranges[i].num_chars_in_range; ++j) {
                                        int w,h;
                                        
                                        GMetrics gmetrics;
                                        
                                        if (!win32_glyph_metrics(dc, ranges[i].first_unicode_char_in_range + j, &gmetrics)){
                                            result = 0;
                                            break;
                                        }
                                        
                                        w = CEIL32(gmetrics.xoff2 - gmetrics.xoff);
                                        h = font_out->ascent + font_out->descent;
                                        
                                        rects[k].w = (stbrp_coord) (w + spc->padding);
                                        rects[k].h = (stbrp_coord) (h + spc->padding);
                                        ++k;
                                    }
                                }
                                
                                stbrp_pack_rects(context, rects, k);
                                
                                k = 0;
                                for (i=0; i < num_ranges; ++i) {
                                    //float fh = ranges[i].font_size;
                                    //float scale = fh > 0 ? stbtt_ScaleForPixelHeight(&info, fh) : stbtt_ScaleForMappingEmToPixels(&info, -fh);
                                    for (j=0; j < ranges[i].num_chars_in_range; ++j) {
                                        stbrp_rect *r = &rects[k];
                                        if (r->was_packed) {
                                            stbtt_packedchar *bc = &ranges[i].chardata_for_range[j];
                                            //int glyph = stbtt_FindGlyphIndex(&info, ranges[i].first_unicode_char_in_range + j);
                                            int code = ranges[i].first_unicode_char_in_range + j;
                                            stbrp_coord pad = (stbrp_coord) spc->padding;
                                            
                                            // pad on left and top
                                            r->x += pad;
                                            r->y += pad;
                                            r->w -= pad;
                                            r->h -= pad;
                                            
#if 0
                                            int advance, lsb, x0,y0,x1,y1;
                                            
                                            stbtt_GetGlyphHMetrics(&info, glyph, &advance, &lsb);
                                            stbtt_GetGlyphBitmapBox(&info, glyph,
                                                                    scale * spc->h_oversample,
                                                                    scale * spc->v_oversample,
                                                                    &x0,&y0,&x1,&y1);
                                            stbtt_MakeGlyphBitmapSubpixel(&info,
                                                                          spc->pixels + r->x + r->y*spc->stride_in_bytes,
                                                                          r->w - spc->h_oversample+1,
                                                                          r->h - spc->v_oversample+1,
                                                                          spc->stride_in_bytes,
                                                                          scale * spc->h_oversample,
                                                                          scale * spc->v_oversample,
                                                                          0,0,
                                                                          glyph);
#else
                                            float advance, x0, y0, x1, y1;
                                            
                                            GMetrics gmetrics;
                                            if (!win32_glyph_metrics(dc, code, &gmetrics)){
                                                result = false;
                                                break;
                                            }
                                            
                                            advance = gmetrics.advance;
                                            x0 = gmetrics.xoff;
                                            y0 = -font_out->ascent;
                                            x1 = gmetrics.xoff2;
                                            y1 = font_out->descent;
                                            
#endif
                                            
                                            bc->x0       = (stbtt_int16)  r->x;
                                            bc->y0       = (stbtt_int16)  r->y;
                                            bc->x1       = (stbtt_int16) (r->x + r->w);
                                            bc->y1       = (stbtt_int16) (r->y + r->h);
                                            bc->xadvance =       (float)  advance;
                                            bc->xoff     =       (float)  x0;
                                            bc->yoff     =       (float)  y0;
                                            bc->xoff2    =       (float)  (x0 + r->w);
                                            bc->yoff2    =       (float)  (y0 + r->h);
                                        } else {
                                            return_value = 0; // if any fail, report failure
                                        }
                                        
                                        ++k;
                                    }
                                }
                                
                                STBTT_free(rects, spc->user_allocator_context);
                                pack_font_range_result =  return_value;
                                goto pack_font_range_end;
                            }
                        }
                        pack_font_range_end:;
                        
                        
                        if (!pack_font_range_result){
                            result = 0;
                        }
                        
                        {
                            stbtt_pack_context *spc = &spc_;
                            {
                                STBTT_free(spc->nodes    , spc->user_allocator_context);
                                STBTT_free(spc->pack_info, spc->user_allocator_context);
                            }
                        }
                    }
                    else{
                        result = 0;
                    }
                    /////////////////////////////////////////////////////////////////
                    
                    if (result){
                        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);
                        
                        memset(block, 0xFF, tex_width*tex_height);
                        
                        glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, tex_width, tex_height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, block);
                        
                        font_out->tex = font_tex;
                        glBindTexture(GL_TEXTURE_2D, 0);
                        
                        font_out->chardata['\r'] = font_out->chardata[' '];
                        font_out->chardata['\n'] = font_out->chardata[' '];
                        font_out->chardata['\t'] = font_out->chardata[' '];
                        font_out->chardata['\t'].xadvance *= tab_width;
                        
                        for (u8 code_point = 0; code_point < 128; ++code_point){
                            font_out->advance_data[code_point] = font_out->chardata[code_point].xadvance;
                        }
                        
                        i32 max_advance = metric.tmMaxCharWidth;
                        font_out->advance = max_advance - 1;
                    }
                    
                    end_temp_memory(temp);
                }
            }
            
            DeleteObject(font_handle);
            DeleteObject(bmp_handle);
        }
        
        ReleaseDC(win32vars.window_handle, dc);
    }
    
    return(result);
}

// BOTTOM