/* Generates fonts.h from font files using FreeType 2 library
 * https://www.freetype.org/ */
#include <ctype.h>
#include <libgen.h>
#include <math.h>
#include <stdio.h>
#include <string.h>

#include <freetype2/ft2build.h>
#include FT_FREETYPE_H

#include "common.h"

#define HEIGHT 52
#define PIXEL_SIZE HEIGHT
#define MAX_WIDTH 100
#define DESCENT 11 /* Found empirically. TODO: how to calculate? */
#define ALPHABET                                                               \
  "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"

typedef struct Context {
  char name[MAX_NAME + 1];
  char *alphabet;
  int num_fonts;
  int num_chars;
  int height;
  int baseline;
  int font_idx;
  int glyph_idx;
  char glyph_char;
} Context;

#define BEGIN(_ctx)                                                            \
  (printf("/* autogenerated by %s */\n#include <stdio.h>\n#include "           \
          "\"common.h\"\n",                                                    \
          __FILE__))
#define FONT_START(_ctx) (printf("\n/* %s */\n", (_ctx)->name))
#define GLYPH_START(_ctx)                                                      \
  (printf("static int8_t font%i_glyph%i[] = {// %c\n ", _ctx->font_idx,        \
          _ctx->glyph_idx, _ctx->glyph_char))
#define BYTE_OUT(_ctx, _v) (printf("%i,", (_v)))
#define ROW_END(_ctx) (printf("%i,\n ", BC_NEWLINE))
#define GLYPH_END(_ctx) (printf("%i};\n", BC_EOG))
#define FONT_END(_ctx) (font_end(_ctx))
#define END(_ctx) (end(_ctx))

void font_end(Context *ctx) {
  printf("static int8_t const *font%i_glyphs[] = {\n ", ctx->font_idx);
  for (int i = 0; i < ctx->num_chars; i++) {
    printf("font%i_glyph%i,", ctx->font_idx, i);
    if (i % 10 == 9)
      printf("\n ");
  }
  printf("NULL};\nstatic Font font%i = {\"%s\", \"%s\", %i, %i, "
         "font%i_glyphs};\n",
         ctx->font_idx, ctx->name, ctx->alphabet, HEIGHT, ctx->baseline,
         ctx->font_idx);
}

void end(Context *ctx) {
  printf("\nstatic Font *fonts[] = {\n ");
  for (int i = 0; i < ctx->num_fonts; i++) {
    /* TODO: we can use struct Font fonts[] and inline
     * {name, alphabet, height, baseline, fontN_glyphs} here, but would need
     * some extra storage or 2-pass iteration */
    printf("&font%i,", i);
  }
  printf("NULL\n};\n");
}

void draw_bitmap(Context *ctx, FT_Bitmap *bitmap, FT_Int bitmap_top) {
  /* Eg, letters 'o' and 'j'
   * _____________________________________
   *     |                | y_shift     |
   *     | y_shift     ___|___          |
   *   __|__           O   |            |
   * // \\             ||  | bitmap_top |
   * || || bitmap_top  ||  |            | HEIGHT
   *_\\_//_____________||__|_ baseline  |
   *                   ||  |            |
   *                \\_//  | DESCENT    |
   *  ________________"____|____________|__
   *
   * For "o" bitmap->rows == bitmap_top
   * For "j" bitmap->rows == bitmap_top + descent
   *
   **/
  int y_shift = HEIGHT - (bitmap_top + DESCENT);
  GLYPH_START(ctx);
  for (int i = 0; i < y_shift; i++)
    ROW_END(ctx);
  for (size_t row = 0; row < bitmap->rows; row++) {
    int num_spaces = 0;
    for (size_t col = 0; col < bitmap->width; col++) {
      if (row < 0 || col < 0 || col >= MAX_WIDTH || row >= HEIGHT) {
        fprintf(stderr,
                "WARNING: %s canvas overflow: character '%c' exceeds canvas "
                "size (%i, %i) at (%zu, %zu)\n",
                ctx->name, ctx->glyph_char, HEIGHT, MAX_WIDTH, row, col);
        continue;
      }
      unsigned char pixel_brightness =
          bitmap->buffer[row * bitmap->pitch + col];
      if (pixel_brightness > 1) {
        if (num_spaces) {
          BYTE_OUT(ctx, BC_NUM_SPACES(num_spaces));
          num_spaces = 0;
        }
        BYTE_OUT(ctx, BC_GREY_PIXEL(pixel_brightness));
      } else {
        num_spaces++;
      }
    }
    ROW_END(ctx);
  }
  GLYPH_END(ctx);
}

int main(int argc, char **argv) {
  FT_Library library;
  FT_Face face;

  FT_GlyphSlot slot;
  FT_Error error;

  Context ctx;

  char *filepath, *filename;
  char *text;

  int num_chars;

  if (argc < 2) {
    fprintf(stderr, "usage: %s font1 font2 ...fontN\n", argv[0]);
    exit(1);
  }

  ctx.alphabet = ALPHABET;
  ctx.num_chars = strlen(ctx.alphabet);
  ctx.num_fonts = argc - 1;
  ctx.height = HEIGHT;
  ctx.baseline = HEIGHT - DESCENT;

  error = FT_Init_FreeType(&library); /* initialize library */
  /* error handling omitted */
  BEGIN(ctx);
  for (int i = 1; i < argc; i++) {
    filepath = argv[i];
    /* converting filepath to lowercase file name without extension */
    filename = basename(filepath);
    strncpy(ctx.name, filename, MAX_NAME);
    ctx.name[MAX_NAME + 1] = '\0';
    char *ext_pos = strrchr(ctx.name, '.');
    if (ext_pos != NULL) {
      *ext_pos = '\0';
    }
    for (char *p = ctx.name; *p; ++p)
      *p = tolower(*p);
    ctx.font_idx = i - 1;

    error = FT_New_Face(library, filepath, 0, &face); /* create face object */
    if (error) {
      fprintf(stderr, "error opening font %s\n", filepath);
      exit(1);
    }

    error = FT_Set_Pixel_Sizes(face, 0, PIXEL_SIZE);
    if (error) {
      fprintf(stderr, "error setting pixel size %s\n", filepath);
      exit(1);
    }

    slot = face->glyph;
    FONT_START(&ctx);
    for (int n = 0; n < ctx.num_chars; n++) {
      /* load glyph image into the slot (erase previous one) */
      error = FT_Load_Char(face, ctx.alphabet[n], FT_LOAD_RENDER);
      if (error) {
        fprintf(stderr, "error rendering char '%c' %s\n", ctx.alphabet[n],
                filepath);
        continue;
      }
      ctx.glyph_char = ctx.alphabet[n];
      ctx.glyph_idx = n;
      draw_bitmap(&ctx, &slot->bitmap, slot->bitmap_top);
    }
    FONT_END(&ctx);
    FT_Done_Face(face);
  }
  END(&ctx);
  FT_Done_FreeType(library);

  return 0;
}
