/*
 * Copyright 2026 Behdad Esfahbod. All Rights Reserved.
 */

#include "demo-font.h"
#include "demo-atlas.h"
#include "../helper-extents-overlay.hh"

#include <hb-ot.h>

#include <map>

typedef std::map<unsigned int, glyph_info_t> glyph_cache_t;

struct demo_font_t {
  hb_face_t       *face;
  hb_font_t       *font;
  glyph_cache_t   *glyph_cache;
  demo_atlas_t    *atlas;
  hb_gpu_paint_t  *p;
  hb_gpu_draw_t   *d;
  bool             draw_only;
  bool             show_extents;

  unsigned int num_glyphs;
  unsigned int sum_bytes;
};

demo_font_t *
demo_font_create (hb_font_t    *hb_font,
		  demo_atlas_t *atlas,
		  hb_bool_t     draw_only)
{
  demo_font_t *font = (demo_font_t *) calloc (1, sizeof (demo_font_t));

  font->face = hb_face_reference (hb_font_get_face (hb_font));
  font->font = hb_font_reference (hb_font);
  font->glyph_cache = new glyph_cache_t ();
  font->atlas = demo_atlas_reference (atlas);
  font->draw_only = draw_only;
  if (draw_only)
    font->d = hb_gpu_draw_create_or_fail ();
  else
    font->p = hb_gpu_paint_create_or_fail ();

  return font;
}

void
demo_font_destroy (demo_font_t *font)
{
  if (!font)
    return;

  hb_gpu_paint_destroy (font->p);
  hb_gpu_draw_destroy (font->d);
  demo_atlas_destroy (font->atlas);
  delete font->glyph_cache;
  hb_font_destroy (font->font);
  hb_face_destroy (font->face);
  free (font);
}


hb_face_t *
demo_font_get_face (demo_font_t *font)
{
  return font->face;
}

hb_font_t *
demo_font_get_font (demo_font_t *font)
{
  return font->font;
}

void
demo_font_set_show_extents (demo_font_t *font, hb_bool_t show)
{
  bool b = !!show;
  if (font->show_extents == b) return;
  font->show_extents = b;
  demo_font_clear_cache (font);
}

void
demo_font_set_palette (demo_font_t *font, unsigned palette_index)
{
  if (font->draw_only)
    return;
  hb_gpu_paint_set_palette (font->p, palette_index);
  demo_font_clear_cache (font);
}

void
demo_font_clear_custom_palette_colors (demo_font_t *font)
{
  if (font->draw_only)
    return;
  hb_gpu_paint_clear_custom_palette_colors (font->p);
  demo_font_clear_cache (font);
}

hb_bool_t
demo_font_set_custom_palette_color (demo_font_t *font,
				    unsigned int color_index,
				    hb_color_t   color)
{
  if (font->draw_only)
    return false;
  hb_bool_t ok = hb_gpu_paint_set_custom_palette_color (font->p, color_index, color);
  demo_font_clear_cache (font);
  return ok;
}


static void
_demo_font_upload_glyph (demo_font_t  *font,
			 unsigned int  glyph_index,
			 glyph_info_t *glyph_info)
{
  hb_glyph_extents_t hb_ext = {};
  hb_blob_t *blob;
  int x_scale, y_scale;
  hb_font_get_scale (font->font, &x_scale, &y_scale);
  float sw = (float) (x_scale * 0.01);
  if (font->draw_only)
  {
    hb_gpu_draw_clear (font->d);
    hb_gpu_draw_glyph (font->d, font->font, glyph_index);
    if (font->show_extents)
    {
      hb_glyph_extents_t g;
      if (hb_font_get_glyph_extents (font->font, glyph_index, &g))
	util_emit_extents_overlay_into_draw (
	  hb_gpu_draw_get_funcs (font->d), font->d,
	  &g, /*pen_x*/ 0.f, /*pen_y*/ 0.f, sw);
    }
    blob = hb_gpu_draw_encode (font->d, &hb_ext);
  }
  else
  {
    hb_gpu_paint_clear (font->p);
    hb_gpu_paint_glyph (font->p, font->font, glyph_index);
    if (font->show_extents)
    {
      hb_glyph_extents_t g;
      if (hb_font_get_glyph_extents (font->font, glyph_index, &g))
	util_emit_extents_overlay_into_paint (
	  hb_gpu_paint_get_funcs (font->p), font->p,
	  &g, /*pen_x*/ 0.f, /*pen_y*/ 0.f, sw);
    }
    blob = hb_gpu_paint_encode (font->p, &hb_ext);
  }
  unsigned int len = blob ? hb_blob_get_length (blob) : 0;

  glyph_info->extents.min_x = hb_ext.x_bearing;
  glyph_info->extents.max_x = hb_ext.x_bearing + hb_ext.width;
  glyph_info->extents.max_y = hb_ext.y_bearing;
  glyph_info->extents.min_y = hb_ext.y_bearing + hb_ext.height;
  glyph_info->advance = hb_font_get_glyph_h_advance (font->font, glyph_index);
  glyph_info->upem = y_scale;
  glyph_info->is_empty = (len == 0);

  if (!glyph_info->is_empty)
    glyph_info->atlas_offset = demo_atlas_alloc (font->atlas,
						 hb_blob_get_data (blob, NULL),
						 len);

  font->num_glyphs++;
  font->sum_bytes += len;

  if (blob)
  {
    if (font->draw_only)
      hb_gpu_draw_recycle_blob (font->d, blob);
    else
      hb_gpu_paint_recycle_blob (font->p, blob);
  }
}

void
demo_font_lookup_glyph (demo_font_t  *font,
			unsigned int  glyph_index,
			glyph_info_t *glyph_info)
{
  if (font->glyph_cache->find (glyph_index) == font->glyph_cache->end ()) {
    _demo_font_upload_glyph (font, glyph_index, glyph_info);
    (*font->glyph_cache)[glyph_index] = *glyph_info;
  } else
    *glyph_info = (*font->glyph_cache)[glyph_index];
}

void
demo_font_clear_cache (demo_font_t *font)
{
  font->glyph_cache->clear ();
  font->num_glyphs = 0;
  font->sum_bytes = 0;
}

void
demo_font_print_stats (demo_font_t *font)
{
  double atlas_used_kb = demo_atlas_get_used (font->atlas) * 8 / 1024.;

  if (!font->num_glyphs)
    return;

  LOGI ("%3u glyphs; avg %5.2fkb per glyph; atlas used %5.2fkb\n",
	font->num_glyphs,
	font->sum_bytes / 1024. / font->num_glyphs,
	atlas_used_kb);
}
