/*
 * Copyright © 2026  Behdad Esfahbod
 *
 *  This is part of HarfBuzz, a text shaping library.
 *
 * Permission is hereby granted, without written agreement and without
 * license or royalty fees, to use, copy, modify, and distribute this
 * software and its documentation for any purpose, provided that the
 * above copyright notice and the following two paragraphs appear in
 * all copies of this software.
 *
 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 *
 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
 *
 * Author(s): Behdad Esfahbod
 */

#include "hb.hh"
#include <hb-ot.h>
#include "hb-raster.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>


static void
write_ppm (hb_raster_image_t *img, const char *dir, unsigned gid)
{
  hb_raster_extents_t ext;
  hb_raster_image_get_extents (img, &ext);
  if (!ext.width || !ext.height) return;

  char path[512];
  snprintf (path, sizeof path, "%s/%05u.ppm", dir, gid);
  FILE *f = fopen (path, "wb");
  if (!f) { fprintf (stderr, "cannot write %s\n", path); return; }

  const uint8_t *buf = hb_raster_image_get_buffer (img);
  hb_raster_format_t fmt = hb_raster_image_get_format (img);

  fprintf (f, "P6\n%u %u\n255\n", ext.width, ext.height);
  uint8_t rgb[3];
  for (unsigned row = 0; row < ext.height; row++)
  {
    const uint8_t *src = buf + (ext.height - 1 - row) * ext.stride;
    for (unsigned x = 0; x < ext.width; x++)
    {
      if (fmt == HB_RASTER_FORMAT_A8)
      {
	rgb[0] = rgb[1] = rgb[2] = (uint8_t) (255 - src[x]);
      }
      else /* BGRA32 — composite over white */
      {
	uint32_t px;
	hb_memcpy (&px, src + x * 4, 4);
	uint8_t b = (uint8_t) (px & 0xFF);
	uint8_t g = (uint8_t) ((px >> 8) & 0xFF);
	uint8_t r = (uint8_t) ((px >> 16) & 0xFF);
	uint8_t a = (uint8_t) (px >> 24);
	unsigned inv_a = 255 - a;
	rgb[0] = (uint8_t) (r + ((255 * inv_a + 128 + ((255 * inv_a + 128) >> 8)) >> 8));
	rgb[1] = (uint8_t) (g + ((255 * inv_a + 128 + ((255 * inv_a + 128) >> 8)) >> 8));
	rgb[2] = (uint8_t) (b + ((255 * inv_a + 128 + ((255 * inv_a + 128) >> 8)) >> 8));
      }
      fwrite (rgb, 1, 3, f);
    }
  }
  fclose (f);
}

int
main (int argc, char **argv)
{
  unsigned num_iterations = 1;
  int argi = 1;
  for (; argi < argc; argi++)
  {
    const char *arg = argv[argi];
    if (strcmp (arg, "--") == 0)
    {
      argi++;
      break;
    }
    if (arg[0] != '-')
      break;
    if (strcmp (arg, "-h") == 0 || strcmp (arg, "--help") == 0)
    {
      fprintf (stderr, "Usage: %s [-n N|--num-iterations N] font-file [font-size] [output-dir]\n", argv[0]);
      return 0;
    }
    if (strcmp (arg, "-n") == 0 || strcmp (arg, "--num-iterations") == 0)
    {
      if (++argi >= argc)
      {
	fprintf (stderr, "Missing value for %s\n", arg);
	return 1;
      }
      char *end = nullptr;
      long n = strtol (argv[argi], &end, 10);
      if (!argv[argi][0] || end == argv[argi] || *end || n <= 0)
      {
	fprintf (stderr, "Invalid --num-iterations: %s\n", argv[argi]);
	return 1;
      }
      num_iterations = (unsigned) n;
      continue;
    }

    fprintf (stderr, "Unknown option: %s\n", arg);
    return 1;
  }

  if (argc - argi < 1 || argc - argi > 3)
  {
    fprintf (stderr, "Usage: %s [-n N|--num-iterations N] font-file [font-size] [output-dir]\n", argv[0]);
    return 1;
  }

  hb_face_t *face = hb_face_create_from_file_or_fail (argv[argi], 0);
  if (!face) { fprintf (stderr, "Failed to open font\n"); return 1; }

  unsigned upem = hb_face_get_upem (face);

  int font_size = (argc - argi > 1) ? atoi (argv[argi + 1]) : (int) upem;
  if (font_size <= 0) font_size = (int) upem;

  const char *outdir = (argc - argi > 2) ? argv[argi + 2] : nullptr;

  hb_font_t *font = hb_font_create (face);
  hb_font_set_scale (font, font_size, font_size);

  bool has_color = hb_ot_color_has_paint (face) ||
		   hb_ot_color_has_layers (face) ||
#ifndef HB_NO_SVG
		   hb_ot_color_has_svg (face);
#else
		   false;
#endif

  hb_raster_draw_t  *rdr = hb_raster_draw_create_or_fail ();
  hb_raster_paint_t *pnt = has_color ? hb_raster_paint_create_or_fail () : nullptr;
  unsigned glyph_count = hb_face_get_glyph_count (face);

  for (unsigned gid = 0; gid < glyph_count; gid++)
  {
    for (unsigned iter = 0; iter < num_iterations; iter++)
    {
      hb_raster_image_t *img = nullptr;

      if (pnt)
      {
	hb_glyph_extents_t gext;
	if (hb_font_get_glyph_extents (font, gid, &gext) &&
	    hb_raster_paint_set_glyph_extents (pnt, &gext))
	{
	  hb_raster_paint_set_transform (pnt, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f);
	  hb_bool_t painted = hb_raster_paint_glyph (pnt, font, gid, 0.f, 0.f,
							     0, HB_COLOR (0, 0, 0, 255));
	  if (painted)
	    img = hb_raster_paint_render (pnt);
	}

	if (img)
	{
	  if (outdir && iter + 1 == num_iterations)
	    write_ppm (img, outdir, gid);
	  hb_raster_paint_recycle_image (pnt, img);
	  continue;
	}
      }

      hb_raster_draw_set_transform (rdr, 1.f, 0.f, 0.f, 1.f, 0.f, 0.f);
      hb_raster_draw_glyph (rdr, font, gid, 0.f, 0.f);

      img = hb_raster_draw_render (rdr);
      if (img)
      {
	if (outdir && iter + 1 == num_iterations)
	  write_ppm (img, outdir, gid);
	hb_raster_draw_recycle_image (rdr, img);
      }
    }
  }

  hb_raster_paint_destroy (pnt);
  hb_raster_draw_destroy (rdr);
  hb_font_destroy (font);
  hb_face_destroy (face);
  return 0;
}
