#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/pop2/tiles/LA-000/line001/col001.png", "space" };
  f[next++] = & (CHAR) { "A", "../testdata/pop2/tiles/LA-000/line055/col010.png", "A" };
  f[next++] = & (CHAR) { "B", "../testdata/pop2/tiles/LA-000/line053/col007.png", "B" };
  f[next++] = & (CHAR) { "C", "../testdata/pop2/tiles/LA-000/line054/col012.png", "C" };
  f[next++] = & (CHAR) { "D", "../testdata/pop2/tiles/LA-000/line050/col016.png", "D" };
  f[next++] = & (CHAR) { "E", "../testdata/pop2/tiles/LA-000/line048/col022.png", "E" };
  f[next++] = & (CHAR) { "F", "../testdata/pop2/tiles/LA-000/line046/col010.png", "F" };
  f[next++] = & (CHAR) { "G", "../testdata/pop2/tiles/LA-000/line043/col025.png", "G" };
  f[next++] = & (CHAR) { "H", "../testdata/pop2/tiles/LA-000/line042/col017.png", "H" };
  f[next++] = & (CHAR) { "I", "../testdata/pop2/tiles/LA-000/line038/col015.png", "I" };
  //  f[next++] = & (CHAR) { "J", "../testdata/pop2/tiles/LA-000/line0/col0.png", "J" };
  f[next++] = & (CHAR) { "K", "../testdata/pop2/tiles/LA-000/line034/col017.png", "K" };
  f[next++] = & (CHAR) { "L", "../testdata/pop2/tiles/LA-000/line022/col014.png", "L" };
  f[next++] = & (CHAR) { "M", "../testdata/pop2/tiles/LA-000/line030/col026.png", "M" };
  f[next++] = & (CHAR) { "N", "../testdata/pop2/tiles/LA-000/line004/col066.png", "N" };
  f[next++] = & (CHAR) { "O", "../testdata/pop2/tiles/LA-000/line053/col026.png", "O" };
  f[next++] = & (CHAR) { "P", "../testdata/pop2/tiles/LA-000/line037/col016.png", "P" };
  //  f[next++] = & (CHAR) { "Q", "../testdata/pop2/tiles/LA-000/line0/col0.png", "Q" };
  f[next++] = & (CHAR) { "R", "../testdata/pop2/tiles/LA-000/line030/col013.png", "R" };
  f[next++] = & (CHAR) { "S", "../testdata/pop2/tiles/LA-000/line054/col023.png", "S" };
  f[next++] = & (CHAR) { "T", "../testdata/pop2/tiles/LA-000/line050/col033.png", "T" };
  f[next++] = & (CHAR) { "U", "../testdata/pop2/tiles/LA-000/line038/col013.png", "U" }; // NEED BETTER
  f[next++] = & (CHAR) { "V", "../testdata/pop2/tiles/LA-000/line013/col005.png", "V" };
  f[next++] = & (CHAR) { "W", "../testdata/pop2/tiles/LA-000/line042/col025.png", "W" }; // NEED BETTER
  f[next++] = & (CHAR) { "X", "../testdata/pop2/tiles/LA-000/line049/col062.png", "X" };
  f[next++] = & (CHAR) { "Y", "../testdata/pop2/tiles/LA-000/line036/col013.png", "Y" };
  //  f[next++] = & (CHAR) { "Z", "../testdata/pop2/tiles/LA-000/line0/col0.png", "Z" };
  f[next++] = & (CHAR) { "0", "../testdata/pop2/tiles/LA-000/line032/col012.png", "0" }; // NEED BETTER
  f[next++] = & (CHAR) { "1", "../testdata/pop2/tiles/LA-000/line040/col036.png", "1" };
  f[next++] = & (CHAR) { "2", "../testdata/pop2/tiles/LA-000/line037/col037.png", "2" };
  f[next++] = & (CHAR) { "3", "../testdata/pop2/tiles/LA-000/line040/col023.png", "3" };
  f[next++] = & (CHAR) { "4", "../testdata/pop2/tiles/LA-000/line015/col021.png", "4" };
  f[next++] = & (CHAR) { "5", "../testdata/pop2/tiles/LA-000/line046/col025.png", "5" };
  f[next++] = & (CHAR) { "6", "../testdata/pop2/tiles/LA-000/line021/col021.png", "6" };
  f[next++] = & (CHAR) { "7", "../testdata/pop2/tiles/LA-000/line021/col012.png", "7" };
  f[next++] = & (CHAR) { "8", "../testdata/pop2/tiles/LA-000/line004/col062.png", "8" };
  f[next++] = & (CHAR) { "9", "../testdata/pop2/tiles/LA-000/line046/col026.png", "9" };
  f[next++] = & (CHAR) { "[", "../testdata/pop2/tiles/LA-000/line004/col056.png", "[" };
  f[next++] = & (CHAR) { "]", "../testdata/pop2/tiles/LA-000/line004/col074.png", "]" };
  f[next++] = & (CHAR) { "(", "../testdata/pop2/tiles/LA-000/line055/col013.png", "(" };
  f[next++] = & (CHAR) { ")", "../testdata/pop2/tiles/LA-000/line055/col022.png", ")" };
  f[next++] = & (CHAR) { ".", "../testdata/pop2/tiles/LA-000/line004/col060.png", "." };
  f[next++] = & (CHAR) { ",", "../testdata/pop2/tiles/LA-000/line055/col018.png", "," };
  f[next++] = & (CHAR) { ":", "../testdata/pop2/tiles/LA-000/line053/col011.png", ":" }; // NEED BETTER
  f[next++] = & (CHAR) { ";", "../testdata/pop2/tiles/LA-000/line055/col023.png", ";" };
  f[next++] = & (CHAR) { "'", "../testdata/pop2/tiles/LA-000/line015/col005.png", "'" };
  f[next++] = & (CHAR) { "+", "../testdata/pop2/tiles/LA-000/line037/col061.png", "+" };
  f[next++] = & (CHAR) { "-", "../testdata/pop2/tiles/LA-000/line020/col006.png", "-" };
  f[next++] = & (CHAR) { "<", "../testdata/pop2/tiles/LA-000/line042/col013.png", "<" };
  f[next++] = & (CHAR) { ">", "../testdata/pop2/tiles/LA-000/line047/col027.png", ">" };
  f[next++] = & (CHAR) { "=", "../testdata/pop2/tiles/LA-000/line039/col020.png", "=" };
  f[next++] = & (CHAR) { "/", "../testdata/pop2/tiles/LA-005/line023/col013.png", "/" };
  f[next++] = & (CHAR) { "\"", "../testdata/pop2/tiles/LA-005/line023/col015.png", "\"" };
  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");
}
