/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
│ vi: set et ft=c ts=2 sts=2 sw=2 fenc=utf-8                               :vi │
╞══════════════════════════════════════════════════════════════════════════════╡
│ Copyright 2021 Justine Alexandra Roberts Tunney                              │
│                                                                              │
│ Permission to use, copy, modify, and/or distribute this software for         │
│ any purpose with or without fee is hereby granted, provided that the         │
│ above copyright notice and this permission notice appear in all copies.      │
│                                                                              │
│ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL                │
│ WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED                │
│ WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE             │
│ AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL         │
│ DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR        │
│ PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER               │
│ TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR             │
│ PERFORMANCE OF THIS SOFTWARE.                                                │
╚─────────────────────────────────────────────────────────────────────────────*/
#include "libc/calls/struct/sigaction.h"
#include "libc/fmt/conv.h"
#include "libc/limits.h"
#include "libc/log/log.h"
#include "libc/macros.h"
#include "libc/mem/gc.h"
#include "libc/mem/mem.h"
#include "libc/stdio/append.h"
#include "libc/stdio/stdio.h"
#include "libc/str/str.h"
#include "libc/sysv/consts/sig.h"
#include "libc/x/x.h"
#include "third_party/getopt/getopt.internal.h"
#include "third_party/stb/stb_truetype.h"

#define SQR(x) ((x) * (x))

int end;
int start;
int verbose;
int difference;

struct Block {
  char16_t c;
  unsigned char b[4][2];
} kBlocks[] = {
    {u' ', {{0000, 0000}, {0000, 0000}, {0000, 0000}, {0000, 0000}}},  //
    {u'░', {{0060, 0060}, {0060, 0060}, {0060, 0060}, {0060, 0060}}},  //
    {u'▒', {{0140, 0140}, {0140, 0140}, {0140, 0140}, {0140, 0140}}},  //
    {u'▓', {{0300, 0300}, {0300, 0300}, {0300, 0300}, {0300, 0300}}},  //
    {u'█', {{0377, 0377}, {0377, 0377}, {0377, 0377}, {0377, 0377}}},  //
    {u'▄', {{0000, 0000}, {0000, 0000}, {0377, 0377}, {0377, 0377}}},  //
    {u'▌', {{0377, 0000}, {0377, 0000}, {0377, 0000}, {0377, 0000}}},  //
    {u'▐', {{0000, 0377}, {0000, 0377}, {0000, 0377}, {0000, 0377}}},  //
    {u'▀', {{0377, 0377}, {0377, 0377}, {0000, 0000}, {0000, 0000}}},  //
};

static char *Raster(int yn, int xn, unsigned char Y[yn][xn], int *dw) {
  char *r = 0;
  unsigned char B[4][4];
  int y, x, i, j, k, s, w, bi, bs;
  *dw = 0;
  for (y = 0; y < yn; y += 4) {
    if (y)
      appendw(&r, '\n');
    for (w = x = 0; x < xn; x += 4) {
      for (i = 0; i < 4; ++i) {
        for (j = 0; j < 4; ++j) {
          if (y + i < yn && x + j < xn) {
            B[i][j] = Y[y + i][x + j];
          } else {
            B[i][j] = 0;
          }
        }
      }
      bi = 0;
      bs = INT_MAX;
      for (k = 0; k < ARRAYLEN(kBlocks); ++k) {
        s = 0;
        for (i = 0; i < 4; ++i) {
          for (j = 0; j < 4; ++j) {
            s += SQR(B[i][j] - kBlocks[k].b[i][j / 2]);
          }
        }
        if (s < bs) {
          bi = k;
          bs = s;
        }
      }
      appendw(&r, tpenc(kBlocks[bi].c));
      ++w;
    }
    if (w > *dw)
      *dw = w;
  }
  return r;
}

void OnSig(int sig) {
  exit(128 + sig);
}

int main(int argc, char *argv[]) {
  char *p;
  void *bmap;
  float scale;
  bool gotsome;
  char **rasters;
  char **fasters;
  size_t ttfsize;
  bool isdifferent;
  unsigned char **ttf;
  stbtt_fontinfo *font;
  int c, i, j, m, o, dw, maxw, *w, *h, s = 40 * 4;
  ShowCrashReports();
  signal(SIGPIPE, OnSig);
  start = 0;
  end = 0x10FFFD;
  while ((o = getopt(argc, argv, "vdc:s:e:S:")) != -1) {
    switch (o) {
      case 'v':
        ++verbose;
        break;
      case 'd':
        difference = 1;
        break;
      case 'c':
        start = end = strtol(optarg, 0, 16);
        break;
      case 's':
        start = strtol(optarg, 0, 16);
        break;
      case 'e':
        end = strtol(optarg, 0, 16);
        break;
      case 'S':
        s = strtol(optarg, 0, 0);
        break;
      default:
        return 1;
    }
  }
  m = argc - optind;
  w = gc(calloc(m, sizeof(*w)));
  h = gc(calloc(m, sizeof(*h)));
  ttf = gc(calloc(m, sizeof(*ttf)));
  font = gc(calloc(m, sizeof(*font)));
  rasters = gc(calloc(m, sizeof(*rasters)));
  fasters = gc(calloc(m, sizeof(*fasters)));
  for (j = 0; j < m; ++j) {
    ttf[j] = gc(xslurp(argv[optind + j], &ttfsize));
    if (!ttf[j]) {
      fprintf(stderr, "%s: not found\n", argv[optind + j]);
      exit(1);
    }
    stbtt_InitFont(font + j, ttf[j], stbtt_GetFontOffsetForIndex(ttf[j], 0));
    printf("%s\n", argv[optind + j]);
  }
  for (c = start; c <= end; ++c) {
    maxw = 0;
    gotsome = false;
    isdifferent = false;
    for (j = 0; j < m; ++j) {
      rasters[j] = 0;
      if ((i = stbtt_FindGlyphIndex(font + j, c)) > 0) {
        w[j] = 0;
        h[j] = 0;
        scale = stbtt_ScaleForPixelHeight(font + j, s);
        bmap = stbtt_GetGlyphBitmap(font + j, 0, scale, i, w + j, h + j, 0, 0);
        if (w[j] && h[j]) {
          gotsome = true;
          if (verbose) {
            rasters[j] = Raster(h[j], w[j], bmap, &dw);
            if (!isdifferent && j && rasters[j - 1] &&
                strcmp(rasters[j], rasters[j - 1])) {
              isdifferent = true;
            }
            if (dw > maxw)
              maxw = dw;
          }
        }
        free(bmap);
      }
    }
    if (gotsome) {
      memcpy(fasters, rasters, m * sizeof(*rasters));
      printf("%04X\n", c);
      if (verbose && (!difference || isdifferent)) {
        do {
          gotsome = false;
          for (j = 0; j < m; ++j) {
            if (!rasters[j]) {
              printf("%-*s    ", maxw, "");
              continue;
            }
            p = strchrnul(rasters[j], '\n');
            if (p - rasters[j])
              gotsome = true;
            printf("%-*.*s    ", maxw, p - rasters[j], rasters[j]);
            rasters[j] = *p ? p + 1 : p;
          }
          printf("\n");
        } while (gotsome);
        printf("\n");
      }
      for (j = 0; j < m; ++j) {
        free(fasters[j]);
      }
    }
  }
  return 0;
}
