#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef __APPLE__
  #include <mach/error.h>
#else
  #include <error.h>
#endif
#include <errno.h>

#ifndef THRESH
#define THRESH 128
#endif

static int debug_level = 0;

static int max3(int a, int b, int c) {
  if ((a > b) && (a > c)) return a;
  if (b > c) return b;
  return c;
}

#define SP ' '
#define PX '@'
#define XS SP // '.'
#define NO SP // 'C'

typedef struct CHAR {
  char *output;
  char *fname;
  char *desc;
} CHAR;

int main(int argc, char **argv) {
  int i, j;
  const CHAR *f[128];
  for (i = 0; i < 128; i++) f[i] = NULL;

  // This is an initial phase of recognition where one glyph is manually
  // chosen by inspection from the html map, to be used for initial
  // identification.

  // We will then use that to collect multiple instances of each glyph
  // (which will almot certainly require manual filtering to remove
  //  misidentified matches).
  // Those can then be used for two purposes:
  // 1) training a neural net recogniser
  // 2) composing an 'ideal' glyph by averaging multiple (aligned)
  //    instances of a glyph.  I'm hoping that this will remove a
  //    lot of the noise and produce a cleaner master.  Manual setting
  //    of the threshold level may be needed to extract the best image.

  // one of each character manually entered from first page for demo
  // These initial approximations are replaced later by ideal characters
  // created by merging multiple instances of those characters.

  int next = 0;
  // At this point the empty cells are handled slightly differently
  // and the space character has to be listed here first.
  f[next++] = & (CHAR) { " ", "../testdata/flexowriter/tiles/AA-008/line005/col025.png", "space" };
  f[next++] = & (CHAR) { "A", "../testdata/flexowriter/tiles/AA-008/line012/col036.png", "A" };
  f[next++] = & (CHAR) { "B", "../testdata/flexowriter/tiles/AA-008/line012/col037.png", "B" };
  f[next++] = & (CHAR) { "C", "../testdata/flexowriter/tiles/AA-008/line012/col038.png", "C" };
  f[next++] = & (CHAR) { "D", "../testdata/flexowriter/tiles/AA-008/line012/col039.png", "D" };
  f[next++] = & (CHAR) { "E", "../testdata/flexowriter/tiles/AA-008/line012/col040.png", "E" };
  f[next++] = & (CHAR) { "F", "../testdata/flexowriter/tiles/AA-008/line012/col041.png", "F" };
  f[next++] = & (CHAR) { "G", "../testdata/flexowriter/tiles/AA-008/line012/col042.png", "G" };
  f[next++] = & (CHAR) { "H", "../testdata/flexowriter/tiles/AA-008/line012/col043.png", "H" };
  f[next++] = & (CHAR) { "I", "../testdata/flexowriter/tiles/AA-008/line012/col044.png", "I" };
  f[next++] = & (CHAR) { "J", "../testdata/flexowriter/tiles/AA-008/line012/col045.png", "J" };
  f[next++] = & (CHAR) { "K", "../testdata/flexowriter/tiles/AA-008/line012/col046.png", "K" };
  f[next++] = & (CHAR) { "L", "../testdata/flexowriter/tiles/AA-008/line012/col047.png", "L" };
  f[next++] = & (CHAR) { "M", "../testdata/flexowriter/tiles/AA-008/line012/col048.png", "M" };
  f[next++] = & (CHAR) { "N", "../testdata/flexowriter/tiles/AA-008/line012/col049.png", "N" };
  f[next++] = & (CHAR) { "O", "../testdata/flexowriter/tiles/AA-008/line012/col050.png", "O" };
  f[next++] = & (CHAR) { "P", "../testdata/flexowriter/tiles/AA-008/line012/col051.png", "P" };
  f[next++] = & (CHAR) { "Q", "../testdata/flexowriter/tiles/AA-008/line012/col052.png", "Q" };
  f[next++] = & (CHAR) { "R", "../testdata/flexowriter/tiles/AA-008/line012/col053.png", "R" };
  f[next++] = & (CHAR) { "S", "../testdata/flexowriter/tiles/AA-008/line012/col054.png", "S" };
  f[next++] = & (CHAR) { "T", "../testdata/flexowriter/tiles/AA-008/line012/col055.png", "T" };
  f[next++] = & (CHAR) { "U", "../testdata/flexowriter/tiles/AA-008/line012/col056.png", "U" };
  f[next++] = & (CHAR) { "V", "../testdata/flexowriter/tiles/AA-008/line012/col057.png", "V" };
  f[next++] = & (CHAR) { "W", "../testdata/flexowriter/tiles/AA-008/line012/col058.png", "W" };
  f[next++] = & (CHAR) { "X", "../testdata/flexowriter/tiles/AA-008/line012/col059.png", "X" };
  f[next++] = & (CHAR) { "Y", "../testdata/flexowriter/tiles/AA-008/line012/col060.png", "Y" };
  f[next++] = & (CHAR) { "Z", "../testdata/flexowriter/tiles/AA-008/line012/col061.png", "Z" };
  f[next++] = & (CHAR) { "a", "../testdata/flexowriter/tiles/AA-008/line013/col036.png", "a" };
  f[next++] = & (CHAR) { "b", "../testdata/flexowriter/tiles/AA-008/line013/col037.png", "b" };
  f[next++] = & (CHAR) { "c", "../testdata/flexowriter/tiles/AA-008/line013/col038.png", "c" };
  f[next++] = & (CHAR) { "d", "../testdata/flexowriter/tiles/AA-008/line013/col039.png", "d" };
  f[next++] = & (CHAR) { "e", "../testdata/flexowriter/tiles/AA-008/line013/col040.png", "e" };
  f[next++] = & (CHAR) { "f", "../testdata/flexowriter/tiles/AA-008/line013/col041.png", "f" };
  f[next++] = & (CHAR) { "g", "../testdata/flexowriter/tiles/AA-008/line013/col042.png", "g" };
  f[next++] = & (CHAR) { "h", "../testdata/flexowriter/tiles/AA-008/line013/col043.png", "h" };
  f[next++] = & (CHAR) { "i", "../testdata/flexowriter/tiles/AA-008/line013/col044.png", "i" };
  f[next++] = & (CHAR) { "j", "../testdata/flexowriter/tiles/AA-008/line013/col045.png", "j" };
  f[next++] = & (CHAR) { "k", "../testdata/flexowriter/tiles/AA-008/line013/col046.png", "k" };
  f[next++] = & (CHAR) { "l", "../testdata/flexowriter/tiles/AA-008/line013/col047.png", "l" };
  f[next++] = & (CHAR) { "m", "../testdata/flexowriter/tiles/AA-008/line013/col048.png", "m" };
  f[next++] = & (CHAR) { "n", "../testdata/flexowriter/tiles/AA-008/line013/col049.png", "n" };
  f[next++] = & (CHAR) { "o", "../testdata/flexowriter/tiles/AA-008/line013/col050.png", "o" };
  f[next++] = & (CHAR) { "p", "../testdata/flexowriter/tiles/AA-008/line013/col051.png", "p" };
  f[next++] = & (CHAR) { "q", "../testdata/flexowriter/tiles/AA-008/line013/col052.png", "q" };
  f[next++] = & (CHAR) { "r", "../testdata/flexowriter/tiles/AA-008/line013/col053.png", "r" };
  f[next++] = & (CHAR) { "s", "../testdata/flexowriter/tiles/AA-008/line013/col054.png", "s" };
  f[next++] = & (CHAR) { "t", "../testdata/flexowriter/tiles/AA-008/line013/col055.png", "t" };
  f[next++] = & (CHAR) { "u", "../testdata/flexowriter/tiles/AA-008/line013/col056.png", "u" };
  f[next++] = & (CHAR) { "v", "../testdata/flexowriter/tiles/AA-008/line013/col057.png", "v" };
  f[next++] = & (CHAR) { "w", "../testdata/flexowriter/tiles/AA-008/line013/col058.png", "w" };
  f[next++] = & (CHAR) { "x", "../testdata/flexowriter/tiles/AA-008/line013/col059.png", "x" };
  f[next++] = & (CHAR) { "y", "../testdata/flexowriter/tiles/AA-008/line013/col060.png", "y" };
  f[next++] = & (CHAR) { "z", "../testdata/flexowriter/tiles/AA-008/line013/col061.png", "z" };
  f[next++] = & (CHAR) { "0", "../testdata/flexowriter/tiles/AA-008/line015/col036.png", "0" };
  f[next++] = & (CHAR) { "1", "../testdata/flexowriter/tiles/AA-008/line015/col037.png", "1" };
  f[next++] = & (CHAR) { "2", "../testdata/flexowriter/tiles/AA-008/line015/col038.png", "2" };
  f[next++] = & (CHAR) { "3", "../testdata/flexowriter/tiles/AA-008/line015/col039.png", "3" };
  f[next++] = & (CHAR) { "4", "../testdata/flexowriter/tiles/AA-008/line015/col040.png", "4" };
  f[next++] = & (CHAR) { "5", "../testdata/flexowriter/tiles/AA-008/line015/col041.png", "5" };
  f[next++] = & (CHAR) { "6", "../testdata/flexowriter/tiles/AA-008/line015/col042.png", "6" };
  f[next++] = & (CHAR) { "7", "../testdata/flexowriter/tiles/AA-008/line015/col043.png", "7" };
  f[next++] = & (CHAR) { "8", "../testdata/flexowriter/tiles/AA-008/line015/col044.png", "8" };
  f[next++] = & (CHAR) { "9", "../testdata/flexowriter/tiles/AA-008/line015/col045.png", "9" };
  f[next++] = & (CHAR) { "&alpha;", "../testdata/flexowriter/tiles/AA-008/line014/col036.png", "&alpha;" };
  f[next++] = & (CHAR) { "&beta;", "../testdata/flexowriter/tiles/AA-008/line014/col038.png", "&beta;" };
  f[next++] = & (CHAR) { "&pi;", "../testdata/flexowriter/tiles/AA-008/line014/col040.png", "&pi;" };
  f[next++] = & (CHAR) { "=", "../testdata/flexowriter/tiles/AA-008/line016/col036.png", "=" };
  f[next++] = & (CHAR) { ">", "../testdata/flexowriter/tiles/AA-008/line016/col038.png", ">" };
  f[next++] = & (CHAR) { "<", "../testdata/flexowriter/tiles/AA-008/line016/col040.png", "<" };
  f[next++] = & (CHAR) { "|", "../testdata/flexowriter/tiles/AA-008/line016/col042.png", "|" };
  f[next++] = & (CHAR) { "*", "../testdata/flexowriter/tiles/AA-008/line016/col044.png", "*" };
  f[next++] = & (CHAR) { ":", "../testdata/flexowriter/tiles/AA-008/line016/col046.png", ":" };
  f[next++] = & (CHAR) { ",", "../testdata/flexowriter/tiles/AA-008/line016/col048.png", "," };
  f[next++] = & (CHAR) { "'", "../testdata/flexowriter/tiles/AA-008/line016/col050.png", "'" };
  f[next++] = & (CHAR) { "&", "../testdata/flexowriter/tiles/AA-008/line016/col052.png", "&" };
  f[next++] = & (CHAR) { "&squared;", "../testdata/flexowriter/tiles/AA-008/line016/col054.png", "&squared;" };
  f[next++] = & (CHAR) { "/", "../testdata/flexowriter/tiles/AA-008/line016/col056.png", "/" };
  f[next++] = & (CHAR) { ".", "../testdata/flexowriter/tiles/AA-008/line016/col058.png", "." };
  f[next++] = & (CHAR) { "+", "../testdata/flexowriter/tiles/AA-008/line016/col060.png", "+" };
  f[next++] = & (CHAR) { "-", "../testdata/flexowriter/tiles/AA-008/line016/col062.png", "-" };
  f[next++] = & (CHAR) { "_", "../testdata/flexowriter/tiles/AA-008/line016/col064.png", "_" };
  f[next++] = & (CHAR) { "&half;", "../testdata/flexowriter/tiles/AA-008/line016/col066.png", "&half;" };
  f[next++] = & (CHAR) { "(", "../testdata/flexowriter/tiles/AA-008/line016/col068.png", "(" };
  f[next++] = & (CHAR) { ")", "../testdata/flexowriter/tiles/AA-008/line016/col070.png", ")" };
  f[next++] = & (CHAR) { "[", "../testdata/flexowriter/tiles/AA-008/line016/col072.png", "[" };
  f[next++] = & (CHAR) { "]", "../testdata/flexowriter/tiles/AA-008/line016/col074.png", "]" };
  f[next++] = & (CHAR) { "?", "../testdata/flexowriter/tiles/AA-008/line016/col076.png", "?" };
  f[next++] = & (CHAR) { "&ne;", "../testdata/flexowriter/tiles/AA-008/line021/col042.png", "&ne;" };
  f[next++] = & (CHAR) { "&ge;", "../testdata/flexowriter/tiles/AA-008/line021/col044.png", "&ge;" };
  f[next++] = & (CHAR) { "&le;", "../testdata/flexowriter/tiles/AA-008/line021/col046.png", "&le;" };
  f[next++] = & (CHAR) { ";", "../testdata/flexowriter/tiles/AA-008/line021/col048.png", ";" };
  f[next++] = & (CHAR) { "^", "../testdata/flexowriter/tiles/AA-008/line021/col050.png", "^" };
#ifdef NEVER
  f[next++] = & (CHAR) { "_A", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_A" };
  f[next++] = & (CHAR) { "_B", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_B" };
  f[next++] = & (CHAR) { "_C", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_C" };
  f[next++] = & (CHAR) { "_D", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_D" };
  f[next++] = & (CHAR) { "_E", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_E" };
  f[next++] = & (CHAR) { "_F", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_F" };
  f[next++] = & (CHAR) { "_G", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_G" };
  f[next++] = & (CHAR) { "_H", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_H" };
  f[next++] = & (CHAR) { "_I", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_I" };
  f[next++] = & (CHAR) { "_J", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_J" };
  f[next++] = & (CHAR) { "_K", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_K" };
  f[next++] = & (CHAR) { "_M", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_M" };
  f[next++] = & (CHAR) { "_N", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_N" };
  f[next++] = & (CHAR) { "_O", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_O" };
  f[next++] = & (CHAR) { "_P", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_P" };
  f[next++] = & (CHAR) { "_Q", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_Q" };
  f[next++] = & (CHAR) { "_R", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_R" };
  f[next++] = & (CHAR) { "_S", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_S" };
  f[next++] = & (CHAR) { "_T", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_T" };
  f[next++] = & (CHAR) { "_U", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_U" };
  f[next++] = & (CHAR) { "_V", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_V" };
  f[next++] = & (CHAR) { "_W", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_W" };
  f[next++] = & (CHAR) { "_X", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_X" };
  f[next++] = & (CHAR) { "_Y", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_Y" };
  f[next++] = & (CHAR) { "_Z", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_Z" };
  f[next++] = & (CHAR) { "_a", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_a" };
  f[next++] = & (CHAR) { "_b", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_b" };
  f[next++] = & (CHAR) { "_c", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_c" };
  f[next++] = & (CHAR) { "_d", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_d" };
  f[next++] = & (CHAR) { "_e", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_e" };
  f[next++] = & (CHAR) { "_f", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_f" };
  f[next++] = & (CHAR) { "_g", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_g" };
  f[next++] = & (CHAR) { "_h", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_h" };
  f[next++] = & (CHAR) { "_i", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_i" };
  f[next++] = & (CHAR) { "_j", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_j" };
  f[next++] = & (CHAR) { "_k", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_k" };
  f[next++] = & (CHAR) { "_l", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_l" };
  f[next++] = & (CHAR) { "_m", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_m" };
  f[next++] = & (CHAR) { "_n", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_n" };
  f[next++] = & (CHAR) { "_o", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_o" };
  f[next++] = & (CHAR) { "_p", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_p" };
  f[next++] = & (CHAR) { "_q", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_q" };
  f[next++] = & (CHAR) { "_r", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_r" };
  f[next++] = & (CHAR) { "_s", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_s" };
  f[next++] = & (CHAR) { "_t", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_t" };
  f[next++] = & (CHAR) { "_u", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_u" };
  f[next++] = & (CHAR) { "_v", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_v" };
  f[next++] = & (CHAR) { "_w", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_w" };
  f[next++] = & (CHAR) { "_x", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_x" };
  f[next++] = & (CHAR) { "_y", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_y" };
  f[next++] = & (CHAR) { "_z", "../testdata/flexowriter/tiles/AA-008/line0/col0.png", "_z" };
#endif
  f[next] = NULL;
  
  printf("#ifndef NULL\n");
  printf("#define NULL 0\n");
  printf("#endif\n\n");

  printf("typedef struct asciimap {\n");
  printf("  const char *ch;\n"); // updated structure required for IPA example.
                                 // Backporting to initial implementation and updating ALGOL-W example.
  printf("  const char *f;\n");
  printf("  const char *desc;\n");
  printf("  const char *line[100]; // *UP TO* 45 lines.\n");
  printf("} asciimap;\n\n");

  printf("const asciimap C[128] = {\n");
  for (i = 0; i < next; i++) {
    printf("  {\n");
    printf("    \"%s\",\n", f[i]->output);
    printf("    \"%s\",\n", f[i]->fname);
    printf("    \"%s\",\n", f[i]->desc);
    {
      FILE *png;
      static char fname[1024];
      sprintf(fname, "convert %s txt:-", f[i]->fname);
      png = popen(fname, "r");
      if (png == NULL) {
        printf("      {\n");
        //for (j = 0; j < 45; j++) {
        //  printf("    \"                                                      \",\n");
        //}
        printf("        NULL,\n");
        printf("      },\n");
      } else {
        static char line[128];
        int c;
        int maxx, maxy, maxbyte, x, y, r, g, b;
        // # ImageMagick pixel enumeration: 40,67,255,gray
        // 0,0: (255,255,255)  #FFFFFF  gray(255)
        int p;
        printf("    {\n");
        for (;;) {
          p = 0;          
          for (;;) {
            c = fgetc(png);
            if (c == EOF || c == '\n' || ferror(png)) break;
            line[p++] = c;
          }
          line[p] = '\0';
          if (c != '\n') break;
          if (line[0] == '#') {
            // "# ImageMagick pixel enumeration: 40,67,255,gray"
            int rc = sscanf(line, "# ImageMagick pixel enumeration: %d,%d,%d,"/*gray*/,
                            &maxx, &maxy, &maxbyte);
            if (rc == 3) {
              if (debug_level >= 1) fprintf(stderr, "%d x %d (%d)\n", maxx, maxy, maxbyte);
              //P.width = maxx; P.height = maxy;
            } else {
              exit(1); 
            }
          } else {
            // "0,0: (255)  #FFFFFF  gray(255)"
            int rc = sscanf(line, "%d,%d: (%d,%d,%d)"/*  #FFFFFF  gray(255)*/, &x, &y, &r, &g, &b);
            if (rc == 5) {
              if (debug_level >= 2) printf("x=%d y=%d: r=%d/g=%d/b=%d\n", x, y, r, g, b);
              if ((x >= maxx || x < 0) || (y >= maxy || y < 0)) {
                // index out of range
                if (debug_level >= 2) fprintf(stderr, "png2h: pixel (%d,%d) out of range (0:%d,0:%d):  %s\n\n", x,y, maxx-1,maxy-1, line);
              } else {
                if (x == 0) printf("      \"");
                if (max3(r,g,b) > THRESH) putchar('@'); else putchar(' ');
                if (x == maxx-1)  printf("\",\n");
              }
            } else {
              rc = sscanf(line, "%d,%d: (%d)"/*  #FFFFFF  gray(255)*/, &x, &y, &g);
              if (rc == 3) {
                if (debug_level >= 2) printf("x=%d y=%d: g=%d\n", x, y, g);
                if ((x >= maxx || x < 0) || (y >= maxy || y < 0)) {
                  // index out of range
                  if (debug_level >= 2) fprintf(stderr, "png2h: pixel (%d,%d) out of range (0:%d,0:%d):  %s\n\n", x,y, maxx-1,maxy-1, line);
                } else {
                  if (x == 0) printf("      \"");
                  if (g > THRESH) putchar('@'); else putchar(' ');
                  if (x == maxx-1)  printf("\",\n");
                }
              } else {
                exit(1);
              }
            }
          }
          
        }
        fclose(png);
        printf("      NULL,\n    },\n");
      }
    }
    printf("  },\n");
  }
  printf("};\n");
}
