////////////////////////////////
// NOTE(allen): Include Freetype

#include "freetype/freetype.h"

////////////////////////////////
// NOTE(allen): Font Parse Function

function FONT_Loose*
font_parse_by_freetype(Arena *arena, String8 data){
  B32 error = 0;
  
  // setup ft library
  FT_Library ft = {0};
  FT_Error ft_init_result = FT_Init_FreeType(&ft);
  if (ft_init_result != 0){
    er_emit(str8_cstring((U8*)FT_Error_String(ft_init_result)));
    error = 1;
  }
  
  // create a face object
  FT_Face face = {0};
  if (!error){
    FT_Open_Args args = {0};
    args.flags = FT_OPEN_MEMORY;
    args.memory_base = data.str;
    args.memory_size = data.size;
    FT_Error result = FT_Open_Face(ft, &args, 0, &face);
    if (result != 0){
      er_emit(str8_cstring((U8*)FT_Error_String(result)));
      error = 1;
    }
  }
  
  // comprehend all the characters
  FONT_CpIndexLoose *first_cp_index = 0;
  FONT_CpIndexLoose *last_cp_index = 0;
  U32 min_index = max_U32;
  U32 max_index = 0;
  if (!error){
    FT_ULong char_code = 0;
    for (;;){
      FT_UInt glyph_index = 0;
      char_code = FT_Get_Next_Char(face, char_code, &glyph_index);
      if (char_code == 0){
        break;
      }
      FONT_CpIndexLoose *new_cp_index = push_array(arena, FONT_CpIndexLoose, 1);
      SLLQueuePush(first_cp_index, last_cp_index, new_cp_index);
      new_cp_index->cp = char_code;
      new_cp_index->index = glyph_index;
      min_index = Min(min_index, glyph_index);
      max_index = Max(max_index, glyph_index);
    }
  }
  
  // set size
  if (!error){
    FT_F26Dot6 char_size = (20 << 6);
    U32 dpi = 72;
    FT_Error result = FT_Set_Char_Size(face, char_size, char_size, dpi, dpi);
    if (result != 0){
      er_emit(str8_cstring((U8*)FT_Error_String(result)));
      error = 1;
    }
  }
  
  // get line advance
  S32 ascender = 0;
  S32 descender = 0;
  U32 line_height = 0;
  if (!error){
    FT_SizeRec *size = face->size;
    ascender = size->metrics.ascender >> 6;;
    descender = size->metrics.descender >> 6;;
    U32 specified_height = size->metrics.height >> 6;
    line_height = Max(ascender - descender, specified_height);
  }
  
  // render all the glyphs
  FONT_GlyphLoose *first_glyph = 0;
  FONT_GlyphLoose *last_glyph = 0;
  if (!error){
    for (U64 i = min_index; i <= max_index; i += 1){
      U32 glyph_index = (U32)i;
      
      // render with FreeType
      FT_Int32 flags = FT_LOAD_DEFAULT;
      FT_Error render_result = FT_Load_Glyph(face, glyph_index, flags);
      if (render_result == 0){
        render_result = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
      }
      
      // failure to render
      if (render_result != 0){
        log_emitf("failed to render glyph [%u]\n", glyph_index);
      }
      
      // convert to 8-bit mono chrome bitmap
      FT_GlyphSlotRec *glyph = 0;
      U32 width = 0;
      U32 height = 0;
      U8 *buf = 0;
      if (render_result == 0){
        glyph = face->glyph;
        FT_Bitmap *bitmap = &glyph->bitmap;
        width = bitmap->width;
        height = bitmap->rows;
        
        if (width > 0 && height > 0){
          // convert to 8-bit monochrome bitmap
          switch (bitmap->pixel_mode){
            case FT_PIXEL_MODE_GRAY:
            {
              if (bitmap->num_grays == 256){
                buf = push_array(arena, U8, width*height);
                S32 pitch = bitmap->pitch;
                U8 *in_bottom_line = bitmap->buffer;
                if (pitch > 0){
                  in_bottom_line = bitmap->buffer + pitch*(height - 1);
                }
                U8 *in_line = in_bottom_line;
                U8 *out_line = buf;
                for (U32 y = 0; y < height; y += 1){
                  MemoryCopy(out_line, in_line, width);
                  in_line -= pitch;
                  out_line += width;
                }
              }
            }break;
          }
          
          // unhandled pixel mode
          if (buf == 0){
            log_emitf("unhandled pixel mode [%u]\n", glyph_index);
          }
        }
      }
      
      // use zero width/height when render fails
      U32 effective_width = width;
      U32 effective_height = height;
      if (buf == 0){
        effective_width = 0;
        effective_height = 0;
      }
      
      // save glyph
      FONT_GlyphLoose *new_glyph = push_array(arena, FONT_GlyphLoose, 1);
      SLLQueuePush(first_glyph, last_glyph, new_glyph);
      new_glyph->bitmap = buf;
      new_glyph->w = effective_width;
      new_glyph->h = effective_height;
      new_glyph->index = glyph_index;
      new_glyph->left_minus_origin = glyph->bitmap_left;
      new_glyph->bottom_minus_origin = glyph->bitmap_top - effective_height;
      new_glyph->advance = (glyph->advance.x >> 6);
    }
  }
  
  // finish using the library
  if (ft_init_result == 0){
    FT_Error result = FT_Done_FreeType(ft);
    if (result != 0){
      er_emit(str8_cstring((U8*)FT_Error_String(result)));
      error = 1;
    }
  }
  
  // fill result
  FONT_Loose *result = 0;
  if (!error){
    result = push_array(arena, FONT_Loose, 1);
    result->first_cp_index = first_cp_index;
    result->last_cp_index = last_cp_index;
    result->first_glyph = first_glyph;
    result->last_glyph = last_glyph;
    result->min_index = min_index;
    result->max_index = max_index;
    result->ascender = ascender;
    result->descender = descender;
    result->line_height = line_height;
  }
  
  return(result);
}
