////////////////////////////////
// Font Functions

// bake

MR4TH_SYMBOL_STATIC FONT_Baked*
font_bake(Arena *arena, FONT_Loose *font, FONT_PackParams *params){
  ProfBeginFunc();
  
  Assert(IsPow2OrZero(params->initial_atlas_side));
  Assert(params->initial_atlas_side >= params->max_glyph_side);
  
  // fill buckets
  FONT_CpBucket *buckets =
    push_array(arena, FONT_CpBucket, FONT_CP_BUCKET_COUNT);
  for (FONT_CpIndexLoose *node = font->first_cp_index;
       node != 0;
       node = node->next){
    U32 bucket_index = node->cp%FONT_CP_BUCKET_COUNT;
    FONT_CpNode *new_node = push_array(arena, FONT_CpNode, 1);
    new_node->cp = node->cp;
    new_node->index = node->index;
    SLLQueuePush(buckets[bucket_index].first,
                 buckets[bucket_index].last,
                 new_node);
  }
  
  // run atlas packing
  FONT_GlyphLayout *glyph_layout = 0;
  FONT_GlyphCoords *glyph_coords = 0;
  FONT_GlyphUVCoords *glyph_uv_coords = 0;
  U32 atlas_w = 0;
  U32 atlas_h = 0;
  RectF32 xy_off_minmax = {0};
  {
    ArenaTemp scratch = arena_get_scratch(&arena, 1);
    
    // layout nodes
    FONT_PackNode *first_pack_node = 0;
    FONT_PackNode *last_pack_node = 0;
    
    // set max glyph dimensions
    U32 max_glyph_d = params->max_glyph_side;
    
    // set max atlas growth
    U32 max_grow_i = params->max_grow_count;
    
    // layout state
    U32 w = params->initial_atlas_side;
    U32 h = params->initial_atlas_side;
    U32 space_x = 0;
    U32 layout_x = 0;
    U32 layout_y = 0;
    U32 row_max_h = 0;
    U32 grow_i = 0;
    
    // glyph loop
    for (FONT_GlyphLoose *glyph_ptr = font->first_glyph;
         glyph_ptr != 0;
         glyph_ptr = glyph_ptr->next){
      U32 gw = glyph_ptr->w + params->skirt_pixel_count*2;
      U32 gh = glyph_ptr->h + params->skirt_pixel_count*2;
      
      // check if glyph is too big
      B32 glyph_too_big = (gw > max_glyph_d ||
                           gh > max_glyph_d);
      
      // log glyphs filtered out by size
      if (glyph_too_big){
        log_emitf("glyph too big to pack [%u]\n", glyph_ptr->index);
      }
      
      // attempt to pack the glyph
      if (!glyph_too_big){
        try_again:;
        
        // check if glyph fits
        U32 max_x = layout_x + gw;
        U32 max_y = layout_y + gh;
        B32 fits = (max_x <= w && max_y <= h);
        
        // if it fits...
        if (fits){
          // place
          FONT_PackNode *new_pack_node =
            push_array(scratch.arena, FONT_PackNode, 1);
          SLLQueuePush(first_pack_node, last_pack_node, new_pack_node);
          new_pack_node->glyph = glyph_ptr;
          new_pack_node->x = layout_x + params->skirt_pixel_count;
          new_pack_node->y = layout_y + params->skirt_pixel_count;
          
          // advance cursors
          layout_x += gw;
          row_max_h = Max(row_max_h, gh);
        }
        
        if (!fits){
          // if overflowing y...
          if (max_y > h){
            // grow
            grow_i += 1;
            if (grow_i <= max_grow_i){
              if (grow_i&1){
                space_x = w;
                layout_x = w;
                layout_y = 0;
                row_max_h = 0;
                w *= 2;
              }
              else{
                space_x = 0;
                layout_x = 0;
                layout_y = h;
                row_max_h = 0;
                h *= 2;
              }
            }
          }
          
          // only overflowing x...
          else{
            // go to next row
            layout_x = space_x;
            layout_y += row_max_h;
            row_max_h = 0;
          }
        }
        
        // check if we're too big
        B32 too_big = (grow_i > max_grow_i);
        
        // try same glyph again
        if (!fits && !too_big){
          goto try_again;
        }
        
        // exit from over growing
        if (too_big){
          log_emitf("ran out of space before packing all glyphs\n");
          goto pack_done;
        }
      }
    }
    pack_done:;
    
    // fill results...
    U32 min_index = font->min_index;
    U32 glyph_count = (font->max_index - min_index) + 1;
    
    // fill glyph layouts
    //   & calculate xy off extreme values
    {
      set_inf_F32(xy_off_minmax.x0);
      set_inf_F32(xy_off_minmax.y0);
      set_neg_inf_F32(xy_off_minmax.x1);
      set_neg_inf_F32(xy_off_minmax.y1);
      
      glyph_layout = push_array(arena, FONT_GlyphLayout, glyph_count);
      for (FONT_GlyphLoose *node = font->first_glyph;
           node != 0;
           node = node->next){
        // get layout ptr
        U32 raw_index = node->index;
        U32 adj_index = raw_index - min_index;
        FONT_GlyphLayout *ptr = &glyph_layout[adj_index];
        
        // fill layout from node
        ptr->xy_off.x0 = (F32)node->left_minus_origin;
        ptr->xy_off.y0 = (F32)node->top_minus_origin;
        ptr->xy_off.x1 = (F32)node->left_minus_origin + (F32)node->w;
        ptr->xy_off.y1 = (F32)node->top_minus_origin + (F32)node->h;
        ptr->advance = node->advance;
        
        // expand extremes box
        xy_off_minmax.x0 = Min(xy_off_minmax.x0, ptr->xy_off.x0);
        xy_off_minmax.y0 = Min(xy_off_minmax.y0, ptr->xy_off.y0);
        xy_off_minmax.x1 = Max(xy_off_minmax.x1, ptr->xy_off.x1);
        xy_off_minmax.y1 = Max(xy_off_minmax.y1, ptr->xy_off.y1);
      }
    }
    
    // fill glyph coordinates & uv coordinates
    {
      F32 inv_w = 1.f/(F32)w;
      F32 inv_h = 1.f/(F32)h;
      
      glyph_coords = push_array(arena, FONT_GlyphCoords, glyph_count);
      glyph_uv_coords = push_array(arena, FONT_GlyphUVCoords, glyph_count);
      for (FONT_PackNode *node = first_pack_node;
           node != 0;
           node = node->next){
        FONT_GlyphLoose *glyph = node->glyph;
        U32 raw_index = glyph->index;
        U32 adj_index = raw_index - min_index;
        {
          FONT_GlyphCoords *ptr = &glyph_coords[adj_index];
          ptr->p[0] = node->x;
          ptr->p[1] = node->y;
          ptr->dim[0] = glyph->w;
          ptr->dim[1] = glyph->h;
        }
        {
          FONT_GlyphUVCoords *ptr = &glyph_uv_coords[adj_index];
          ptr->uv.x0 = (node->x)*inv_w;
          ptr->uv.y0 = (node->y)*inv_h;
          ptr->uv.x1 = (node->x + glyph->w)*inv_w;
          ptr->uv.y1 = (node->y + glyph->h)*inv_h;
        }
      }
    }
    
    // fill top level info
    atlas_w = w;
    atlas_h = h;
    
    arena_release_scratch(&scratch);
  }
  
  // fill metrics
  FONT_Baked *result = push_array(arena, FONT_Baked, 1);
  result->buckets = buckets;
  result->glyph_layout = glyph_layout;
  result->glyph_coords = glyph_coords;
  result->glyph_uv_coords = glyph_uv_coords;
  result->atlas_w = atlas_w;
  result->atlas_h = atlas_h;
  result->min_index = font->min_index;
  result->max_index = font->max_index;
  result->ascender  = font->ascender;
  result->descender = font->descender;
  result->line_height = font->line_height;
  result->xy_off_minmax = xy_off_minmax;
  
  ProfEndFunc();
  return(result);
}

// usage

MR4TH_SYMBOL_STATIC U32
font_glyph_index_from_codepoint(FONT_Baked *font, U32 cp){
  U32 result = 0;
  if (font != 0 && font->buckets != 0){
    U32 bucket_index = cp%FONT_CP_BUCKET_COUNT;
    for (FONT_CpNode *node = font->buckets[bucket_index].first;
         node != 0;
         node = node->next){
      if (node->cp == cp){
        result = node->index;
        break;
      }
    }
  }
  return(result);
}

MR4TH_SYMBOL_STATIC F32
font_char_advance(FONT_Baked *font, U32 codepoint){
  // TODO(allen): deduplicate
  // determine glyph index to render
  U32 index = font_glyph_index_from_codepoint(font, codepoint);
  if (index == 0){
    index = font_glyph_index_from_codepoint(font, '?');
  }
  
  FONT_GlyphLayout *layout = font->glyph_layout + index - font->min_index;
  F32 result = layout->advance;
  return(result);
}

MR4TH_SYMBOL_STATIC F32
font_text_advance(FONT_Baked *font, String8 string){
  F32 result = 0.f;
  
  if (font != 0 && font->glyph_layout != 0){
    // fixed values
    U32 fallback_index = font_glyph_index_from_codepoint(font, '?');
    U32 min_index = font->min_index;
    
    // loop
    U8 *ptr = string.str;
    U8 *opl = string.str + string.size;
    for (; ptr < opl; ){
      // determine glyph index to render/measure
      StringDecode decode = str_decode_utf8(ptr, (opl - ptr));
      ptr += decode.size;
      U32 glyph_index = font_glyph_index_from_codepoint(font, decode.codepoint);
      if (glyph_index == 0){
        glyph_index = fallback_index;
      }
      
      // measure advance of character
      FONT_GlyphLayout *layout = &font->glyph_layout[glyph_index - min_index];
      result += layout->advance;
    }
  }
  
  return(result);
}
