// cc -Ofast -o exemplar exemplar.c

// Use grep output like the following to merge multiple instances of a letter and produce an ideal form of it.
// Manually replace the glyph in nchar.c once we have one.

// Will require code from genfont.c to output result and code from merge.c to align bitmaps.
// Probably should factor the common code out as a shared subroutine eventually.

/*

(v/tiles/  f1/.txt:/ re0 i/png/m)0

./exemplar tiles/AW-000/line010/col011.png tiles/AW-000/line010/col019.png tiles/AW-000/line011/col014.png tiles/AW-000/line011/col019.png tiles/AW-000/line005/col021.png tiles/AW-000/line006/col026.png tiles/AW-000/line009/col018.png tiles/AW-000/line009/col025.png tiles/AW-000/line003/col023.png tiles/AW-000/line005/col020.png tiles/AW-000/line001/col076.png tiles/AW-000/line002/col005.png tiles/AW-000/line002/col046.png


./exemplar tiles/AW-000/line001/col018.png tiles/AW-000/line001/col026.png tiles/AW-000/line001/col029.png tiles/AW-000/line001/col068.png tiles/AW-000/line001/col080.png tiles/AW-000/line002/col025.png tiles/AW-000/line002/col060.png


./exemplar tiles/AW-000/line011/col008.png tiles/AW-000/line012/col002.png tiles/AW-000/line012/col008.png tiles/AW-000/line012/col013.png tiles/AW-000/line010/col002.png tiles/AW-000/line010/col008.png tiles/AW-000/line010/col014.png tiles/AW-000/line011/col002.png tiles/AW-000/line007/col051.png tiles/AW-000/line008/col007.png tiles/AW-000/line009/col002.png tiles/AW-000/line007/col018.png  tiles/AW-000/line003/col018.png tiles/AW-000/line005/col009.png tiles/AW-000/line005/col034.png tiles/AW-000/line006/col035.png tiles/AW-000/line002/col032.png tiles/AW-000/line002/col037.png tiles/AW-000/line001/col053.png tiles/AW-000/line002/col008.png

./exemplar tiles/AW-000/line001/col016.png tiles/AW-000/line003/col022.png tiles/AW-000/line006/col049.png tiles/AW-000/line006/col054.png tiles/AW-000/line007/col022.png

./exemplar tiles/AW-000/line001/col036.png tiles/AW-000/line003/col031.png tiles/AW-000/line006/col058.png

tiles/AW-000/line001/col013.txt:B
tiles/AW-000/line001/col077.txt:B
tiles/AW-000/line002/col006.txt:B
tiles/AW-000/line002/col009.txt:B
tiles/AW-000/line002/col033.txt:B
tiles/AW-000/line005/col028.txt:B
tiles/AW-000/line005/col030.txt:B
tiles/AW-000/line007/col035.txt:B
tiles/AW-000/line007/col067.txt:B
tiles/AW-000/line010/col012.txt:B
tiles/AW-000/line010/col015.txt:B

tiles/AW-000/line001/col081.txt:8
tiles/AW-000/line007/col044.txt:8
tiles/AW-000/line008/col077.txt:8
*/
// Identify a single-char .png file using sample images.

// TO DO: PENALTY for black pixels in examined glyph that are off-screen
// when comparing against masters
// (DONE)

// TO DO: calculations using grey scale rather than 0 or 255.
// (not doing so at the moment because of hand-editable images in nchar.h)

// TO DO: after exclusive-or-ing the master and the unknown, do a
// raster thinning of the result by 1 to 3 pixels, leaving primary
// blobs to be counted rather than mostly noise

// TO DO: allow multiple master glyphs decoding to the same character
// to allow for some pages where the scanning was lighter or darker
// than the original exemplar

// TO DO: tweak weighting by how far from 0,0 match is found.

// There's an off-by-1 error compounded with misidentified overlaps
// which shows up when comparing anything against the master for ' '
// which always shows a B+ = 39 despite no +'s in the intersection display
// (FIXED)

// TO DO: add omp primitives for parallelising loops. (TRIED, FAILED)

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

//#include <omp.h>

#include "nchar.h"

static int debug_level = 0;

typedef unsigned char pixel;

typedef struct image {
  char *name;
  int width, height;
  pixel **y;
} image;

typedef struct cumulative_image {
  int count;
  char *name;
  int width, height;
  long int **y;
} cumulative_image;

#define MASTERS 128
image *master[MASTERS];

image *convertasciimap(int c) {
  image *m;
  int x, i, width, height;
  if (C[c].line[0] == NULL) return NULL;
  m = malloc(sizeof(image));
  m->name = malloc(2); m->name[0] = c; m->name[1] = '\0';
  height = 0;
  for (;;) {
    if (C[c].line[height] == NULL) break;
    height += 1;
  }
  width = strlen(C[c].line[0]);
  m->y = (pixel **)malloc(height * sizeof(pixel *));
  for (i = 0; i < height; i++) {
    m->y[i] = (pixel *)malloc(width * sizeof(pixel));
    for (x = 0; x < width; x++) {
      if (C[c].line[i][x] == '@') m->y[i][x] = 255; /* or 1. Shouldn't matter. */ else m->y[i][x] = 0;
    }
  }
  m->width = width; m->height = height;
  return m;
}

void initimage(void) {
  int i;
  for (i = 0; i < MASTERS; i++) {
    master[i] = convertasciimap(i);
  }
}

int compare_pixel(int x, int y,
             image *old, int oldx, int oldy,
             image *new, int newx, int newy,
             unsigned long *excess_black, unsigned long *excess_white,
             unsigned long *matching_black, unsigned long *matching_white) {
  int oldpixel, newpixel;
  // compare individual pixels


  // NEEDS TO BE UPDATED TO MATCH THE CODE IN debugoff
  // For now I'll hack it by adjusting the variables in debugoff
  
  // +       (*excess_black)++;
  // -       (*excess_white)++;
  // @       (*matching_white)++;
  
  if (oldx < 0 || oldx >= old->width) {
    if ((x > 0) && (x < old->width) && (y > 0) && (y < old->height) && (old->y[y][x] != 0)) (*excess_black) += 1;
    if (newx < 0 || newx >= new->width) {
      return '(';
    } else {
      if (newy < 0 || newy >= new->height) {
        return '.';
      } else {
        if (new->y[newy][newx] != 0) {
          return '.';
        } else {
          //#pragma omp critical
          (*excess_black)++;
          return '+';
        }
      }
    }
  } else if (oldy < 0 || oldy >= old->height) {
    return '.';
  } else {
    oldpixel = old->y[oldy][oldx];
  }

  // Need to also penalise pixels in 'new' that are outside the bounding
  // rectangle, after translation. TO DO.  Increment excess_black.
  // Unfortunately although we know 'newx' and 'newy' here, we don't
  // know the offsets that were applied so can't determine what the
  // off-grid pixel would have been.
  if (newx < 0 || newx >= new->width) {
    newpixel = 0;
    if (oldpixel) {
      return '.';
    } else {
      //#pragma omp critical
      (*excess_white)++;
      return '=';
    }
  } else if (newy < 0 || newy >= new->height) {
    newpixel = 0;
    if (oldpixel) {
      return '.';
    } else {
      //#pragma omp critical
      (*excess_white)++;
      return '=';
    }
  } else {
    newpixel = new->y[newy][newx];
  }
  if (debug_level >= 3) {
    fprintf(stderr, "%s[%d,%d] vs %s[%d,%d] -> %d vs %d -> ",
         old->name, oldx, oldy,
         new->name, newx, newy,
         oldpixel, newpixel
         );
  }
  if (oldpixel) { // oldpixel = 1
    if (newpixel) {  // newpixel = 1
      //#pragma omp critical
      (*matching_black)++;
      if (debug_level >= 3) fprintf(stderr, "*matching_black++\n");
      return ' ';
    } else {         // newpixel = 0
      if (debug_level >= 3) fprintf(stderr, "*excess_black++\n");
      //#pragma omp critical
      (*excess_black)++;
      return '+';
    }
  } else {        // oldpixel = 0
    if (newpixel) {  // newpixel = 1
      if (debug_level >= 3) fprintf(stderr, "*excess_white++\n");
      //#pragma omp critical
      (*excess_white)++;
      return '-';
    } else {         // newpixel = 0
      if (debug_level >= 3) fprintf(stderr, "*matching_white++\n");
      //#pragma omp critical
      (*matching_white)++;
      return '@';
    }
  }
}

// I know, the black/white names are incredibly badly chosen, especially since a pixel
// on paper is black and on screen is white :-(  I should go over this and rename
// everything more understandably, such as 'pixel_present_in_exemplar' for example.
// (also master/contender turned out to be confusing too.  It should be 'exemplar'
// vs 'unknown'...

// nomenclature: B+ excessive_black in this master versus in unknown glyph
//                  i.e. pixels which are set in the unknown glyph which don't exist in this master.
//
//               B- excessive_white aka missing_black
//                  pixels which are set in this master which don't exist in the unknown glyph
//
//               B= matching black
//                  unset pixels in both
//
//               W= matching white
//                  pixels which are present in both
//
// a set pixel in this master which is off-screen should count as an excessive_white
//
// a set pixel in the unknown glyph which is off-screen should count as a excessive_black
//
// Unfortunately with the current structure I don't think we can tell whether an off-screen
// pixel was set or clear.
//

// Having compared the exemplar the unknown, work out a single score value based
// on the sum of exemplar vs unknown pixels at each location.

float eval(unsigned long excess_black, unsigned long excess_white,
           unsigned long matching_black, unsigned long matching_white) {
  //fprintf(stderr, "eval: B+ %ld  B- %ld  B= %ld  W= %ld", excess_black, excess_white, matching_black, matching_white);

  /*
       We should penalise excess pixels in our contender more than missing pixels;
       We should rate a master higher for more matching pixels (higher natching_white)
        but not simply because the master has more pixels set (eg a solid blob would
        match 100% of the pixels in our contender, which means we must rate that
        lower due to failure to match a pixel that is present in the master (ie excess_white)

       It's a delicate balancing act and one I haven't completey worked out yet.
       Be careful that a tweak for some specific example will affect other characters
       unexpectedly - this code absolutely requires a good regression test suite.
   */

  // special case for blank contenders and blank master.
  // Later will optimise matching blanks to speed up throughput.
  if (matching_white == 0) {
    if ((excess_black == 0) && (excess_white == 0)) {
      return (float)matching_black;   // glyph matches space perfectly
    } else if (excess_white == 0) {
      return (float)matching_black/(100.0 * (float)excess_black);
    } else {
      return 1.0/(100.0 * (float)excess_white);
    }
  }
  
  float percentage_of_excess_pixels, percentage_of_missing_pixels, more_pixels_is_better;

  if (matching_white == 0) {
    percentage_of_excess_pixels = 100;
  } else {
    if (excess_white == 0) {
      percentage_of_excess_pixels = 0.01;
    } else {
      percentage_of_excess_pixels = (excess_white*100.0) / (float)matching_white;
    }
  }
  if (matching_black == 0) {
    percentage_of_missing_pixels = 100;
  } else {
    if (excess_black == 0) {
      percentage_of_missing_pixels = 0.01;
    } else {
      percentage_of_missing_pixels = (excess_black*100.0) / (float)matching_black;
    }
  }

  if (matching_white+excess_black == 0) {
    more_pixels_is_better = 0.0;
  } else {
    more_pixels_is_better = (float)(matching_white+matching_black) / (float)(matching_white+matching_black+excess_black+excess_white);
  }

  // Main score calculation.  Could be a *lot* better.
  float score = ((1.0/percentage_of_excess_pixels) + (1.0/percentage_of_missing_pixels)) * more_pixels_is_better;

  if (debug_level >= 4) fprintf(stderr, "  return ((1.0/percentage_of_excess_pixels(%f)) + (1.0/percentage_of_missing_pixels(%f))) * more_pixels_is_better(%f) = %f;\n",
          percentage_of_excess_pixels, percentage_of_missing_pixels,  more_pixels_is_better, score);

  return score;
}

float evaluate(image *master, image *contender,
              int xoff, int yoff,
              unsigned long *excess_black, unsigned long *excess_white,
              unsigned long *matching_black, unsigned long *matching_white) {
  int x, y, master_cornerx, master_cornery;

  // The master glyph is positioned in a rectangle that is centered in old.
  // This rectangle may be smaller than the extent of old.  This code was
  // written in the 'future hopeful' tense.  I haven't actually taken advantage
  // of this ability yet.  For now the exemplar and the unknown are around the
  // same size.  The idea behind this was that I might extract a few extra
  // pixels surrounding the grid rectangle when extracting the individual
  // glyphs.  But it turned out that even glyphs that are trimmed off a
  // little at the edges are still recognised pretty well!
  master_cornerx = (master->width  - contender->width )/2;
  master_cornery = (master->height - contender->height)/2;
  
  // compare pixels as if both bitmaps were infinite...
  // Move contender around, keep master fixed.

  // paralleliaable loop. I expect it to show up in profiling.

  *excess_black = *excess_white = *matching_black = *matching_white = 0UL; // init to 0 before adding all pixels
  //#pragma parallel for num_threads(4)
  for (y = (yoff < 0 ? yoff : 0); y < master->height + (yoff > 0 ? yoff : 0); y++) {
    for (x = (xoff < 0 ? xoff : 0); x < contender->width+(xoff > 0 ? xoff : 0); x++) {
      (void) compare_pixel(x,y,
              master, x+master_cornerx, y+master_cornery,
              contender, x-xoff, y-yoff,
              excess_black, excess_white, matching_black, matching_white);
    }
  }

  float score = eval(*excess_black, *excess_white, *matching_black, *matching_white);
  
  if (debug_level >= 2) {
    fprintf(stderr, "'%s' vs \"%s\" at (%d,%d): ", master->name, contender->name, xoff, yoff);
    fprintf(stderr, "excess_black = %lu, excess_white = %lu, "
           "matching_black = %lu, matching_white = %lu, score = %f\n",
           *excess_black,        *excess_white,
           *matching_black,      *matching_white,
            score);
  }
  
  return score;
}

void debugoff(image *master, image *contender, int xoff, int yoff,
              unsigned long excess_black, unsigned long excess_white,
              unsigned long matching_black, unsigned long matching_white,
              float score) {
  int x, y, master_pixel, contender_pixel;
  int cornerx = (master->width  - contender->width )/2;
  int cornery = (master->height - contender->height)/2;

  for (y = (yoff < 0 ? yoff : 0); y < master->height + (yoff > 0 ? yoff : 0); y++) {
    // MASTER
    for (x = 0; x < master->width; x++) {
      // master position is fixed
      if (y < 0) {
        fputc(' ', stderr);
      } else if (y >= master->height) {
        fputc(' ', stderr);
      } else {
        if (x < 0) {
          fputc('.', stderr);
        } else if (x >= contender->width) {
          fputc('.', stderr);
        } else {
          master_pixel = master->y[y][x];
          fputc(master_pixel ? '@' : ' ', stderr);
        }
      }
    }
    fprintf(stderr, " | ");
    // CONTENDER
    for (x = (xoff < 0 ? xoff : 0); x < contender->width+(xoff > 0 ? xoff : 0); x++) {
      if (y-yoff < 0) {
        fputc(',', stderr);
      } else if (y-yoff >= contender->height) {
        fputc('.', stderr);
      } else {
        if (x-xoff < 0) {
          fputc('.', stderr);
        } else if (x-xoff >= contender->width) {
          fputc('.', stderr);
        } else {
          contender_pixel = contender->y[y-yoff][x-xoff];
          fputc(contender_pixel ? '@' : ' ', stderr);
        }
      }
    }
    fprintf(stderr, " | ");
    // INTERSECTION
    for (x = (xoff < 0 ? xoff : 0); x < contender->width+(xoff > 0 ? xoff : 0); x++) {
      unsigned long int local_excess_black = 0UL, local_excess_white = 0UL, local_matching_black = 0UL, local_matching_white = 0UL;
      int ch = compare_pixel(x,y,
                             master, x+cornerx, y+cornery,
                             contender, x-xoff, y-yoff,
                             &local_excess_black, &local_excess_white, &local_matching_black, &local_matching_white);
      fputc(ch, stderr);
    }
    fprintf(stderr, " |\n");
  }
  fprintf(stderr, "master=\"%s\"                                 "
         "contender=%s                 intersection\n",
         master->name, contender->name);
  fprintf(stderr, "%s: B+ %lu  B- %lu  B= %lu  W= %lu  Score=%f\n\n",
         master->name,
         excess_black, excess_white,
         matching_black, matching_white,
         score);
}

void addch(cumulative_image *c_image, image *master, image *contender, int xoff, int yoff,
              float score) {
  int x, y, master_pixel, contender_pixel;
  int cornerx = (master->width  - contender->width )/2;
  int cornery = (master->height - contender->height)/2;

  int vx, vy;
  for (vy = 0, y = (yoff < 0 ? yoff : 0); y < master->height + (yoff > 0 ? yoff : 0); y++, vy++) {
    // CONTENDER
    if (vy == contender->height) break;
    for (vx = 0, x = (xoff < 0 ? xoff : 0); x < contender->width+(xoff > 0 ? xoff : 0); x++, vx++) {
      if (vx == contender->width) break;
      if (y-yoff < 0) {
        if (y < master->height && x < master->width) {
          fputc('@', stderr);
          if (vy < master->height && vx < master->width) c_image->y[vy][vx] += 255;
        }
      } else if (y-yoff >= contender->height) {
        if (y < master->height && x < master->width) {
          fputc('?', stderr);
          if (vy < master->height && vx < master->width) c_image->y[vy][vx] += 255;
        }
      } else {
        if (x-xoff < 0) {
          if (y < master->height && x < master->width) {
            fputc('@', stderr);
            if (vy < master->height && vx < master->width) c_image->y[vy][vx] += 255;
          }
        } else if (x-xoff >= contender->width) {
          if (y < master->height && x < master->width) {
            fputc('?', stderr);
            if (vy < master->height && vx < master->width) c_image->y[vy][vx] += 255;
          }
        } else {
          contender_pixel = contender->y[y-yoff][x-xoff];
          if (y < master->height && x < master->width) {
            assert(vy < master->height && vx < master->width);
            fputc(contender_pixel ? '@' : ' ', stderr);
            c_image->y[vy][vx] += contender->y[y-yoff][x-xoff];
          }
        }
      }
    }
    fprintf(stderr, " %02d\n", vy);
  }
}

// Main workhorse:  if there is any chance of speeding this up, here's where to start...
float locate_bitmap(image *master, image *contender, 
                   int *xoffp, int *yoffp,
                   unsigned long *excess_black, unsigned long *excess_white,
                   unsigned long *matching_black, unsigned long *matching_white) {
  int dist, xoff, yoff, best_xoff=0, best_yoff=0;
  unsigned long best_excess_black = 0L, best_excess_white = 0L,
                best_matching_black = 0L, best_matching_white = 0L;
  float score = 0.0, best_score = 0.0; // AHA!!!!

  dist = 0;
  // move 'contender' around in a spiral until we find
  // the best match between contender and master

  // I haven't profiled the code yet but I expect this to be
  // the heavy loop.  Other loops eg in debug code are not important.

  // paralleliaable loop as long as score update access uses a lock 
  for (dist = 0; dist < contender->width/3; dist++) {
    for (yoff = -dist; yoff <= dist; yoff++) {
      for (xoff = -dist; xoff <= dist; xoff++) {
        score = evaluate(master, contender,
                 xoff, yoff,
                 excess_black, excess_white, matching_black, matching_white);
        if (score > best_score) {
          best_excess_black   = *excess_black;
          best_excess_white   = *excess_white;
          best_matching_black = *matching_black;
          best_matching_white = *matching_white;
          best_score = score;
          best_xoff = xoff;
          best_yoff = yoff;
        }
      }
    }
  }
  *xoffp = best_xoff; *yoffp = best_yoff;
  // identify best x,y offset and whether contender
  // is more-or-less a subset of master.
  // If building master glyph, OR in the contender to the master.
  // (not using actual bitmaps, so
  // total = (total*N + contender)/(N+1), N++ starting at total=0,N=0 )
  if (debug_level >= 1) {
    debugoff(master, contender, best_xoff, best_yoff,
             best_excess_black, best_excess_white,
             best_matching_black, best_matching_white,
             best_score);
    if (debug_level >= 2) fprintf(stderr, "Best overlap at %d,%d\n", best_xoff, best_yoff);  
  }
  *excess_black = best_excess_black;
  *excess_white =  best_excess_white;
  *matching_black = best_matching_black;
  *matching_white = best_matching_white;
  *xoffp = best_xoff;
  *yoffp = best_yoff;
  return best_score;
}

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


// for the result:
static cumulative_image c_image = { /* count: */ 0};
static int best_xoff, best_yoff, best_master_index;

int merge(char *filename) {
  static const char *cmd = "convert %s txt:-";
  static char *command;
  static FILE *im;
  static image P;
  int maxx, maxy, maxbyte;
  
  command = malloc(strlen(cmd)-2+1+strlen(filename));
  if (!command) {
    fprintf(stderr, "merge: cannot allocate 'command' at line %d\n",
            __LINE__-3);
    return(EXIT_FAILURE);
  }
  sprintf(command, cmd, filename);
  
  im = popen(command, "r");
  if (im == NULL) {
    fprintf(stderr, "merge: cannot load %s - %s\n", filename, strerror(errno));
    return(EXIT_FAILURE);
  }

  P.width = P.height = 0;
  P.name = filename;
  
  for (;;) {
    static char line[128];
    int c;
    int x, y, r, g, b;
    // # ImageMagick pixel enumeration: 40,67,255,gray
    // 0,0: (255,255,255)  #FFFFFF  gray(255)
    int p=0;
    for (;;) {
      c = fgetc(im);
      if (c == EOF || c == '\n' || ferror(im)) break;
      //fputc(c, stdout);
      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;
        if (debug_level >= 2)
          fprintf(stderr, "malloc(%d * %u)\n", P.height, sizeof(pixel *));
        P.y = malloc(P.height * sizeof(pixel *));
        if (!P.y) {
          fprintf(stderr, "merge: cannot allocate 'y' at line %d\n", __LINE__-3);
          return(EXIT_FAILURE);
        }
        for (y = 0; y < P.height; y++) {
          if (debug_level >= 2)
            fprintf(stderr, "malloc(%d * %u)\n", P.width, sizeof(pixel));
          P.y[y] = malloc(P.width * sizeof(pixel));
          if (!P.y[y]) {
            fprintf(stderr,
                    "merge: cannot allocate 'x' at line %d\n", __LINE__-3);
            return(EXIT_FAILURE);
          }
          P.y[y] = malloc(P.width * sizeof(pixel));
          for (x = 0; x < P.width; x++) {
            P.y[y][x] = 0;
          }
        }
      } else {
        fprintf(stderr, "merge: not a pgm format file: %s\n", line);
        return(EXIT_FAILURE);
      }
      continue;
    } else {
      int rc = sscanf(line, "%d,%d: (%d,%d,%d)"/*  #FFFFFF  gray(255)*/,
             &x, &y, &r, &g, &b);
      if (rc == 5) {
        if (debug_level >= 2)
          fprintf(stderr, "x=%d y=%d: r=%d/g=%d/b=%d\n", x, y, r, g, b); 
        if ((x >= P.width || x < 0) || (y >= P.height || y < 0)) {
          // index out of range
          if (debug_level >= 2)
            fprintf(stderr,
                    "merge: pixel (%d,%d) out of range (0:%d,0:%d):  %s\n\n",
                    x,y, P.width-1,P.height-1, line);
        } else {
          // ASSIGN GREY VALUE
          P.y[y][x] = max3(r,g,b);
        }
      } else {
        int rc = sscanf(line, "%d,%d: (%d)"/*  #FFFFFF  gray(255)*/,
               &x, &y, &g);
        if (rc == 3) {
          if ((x >= P.width || x < 0) || (y >= P.height || y < 0)) {
            // index out of range
            if (debug_level >= 2)
              fprintf(stderr,
                      "merge: pixel (%d,%d) out of range (0:%d,0:%d):  %s\n\n",
                      x,y, P.width-1,P.height-1, line);
            fprintf(stderr, "merge: not a pgm format file: %s\n", line);
            return(EXIT_FAILURE);
          } else {
            // ASSIGN GREY VALUE
            P.y[y][x] = g;
          }
        } else {
          fprintf(stderr, "merge: not a pgm format file: %s\n", line);
          return(EXIT_FAILURE);
        }
      }
    }
  }
  if (pclose(im) != 0) {
    fprintf(stderr, "merge: could not load %s\n", filename);
    return(EXIT_FAILURE);
  }


  // We've read the character.  Now we compare it to the representative samples.

  image *character;
  float score, best_score = 0.0;
  int m, xoff, yoff;
  long unsigned int excess_black, excess_white, matching_black, matching_white;
  long unsigned int best_excess_black, best_excess_white, best_matching_black, best_matching_white;

  for (m = 0; m < MASTERS; m++) {
    if (master[m]) {
      score = locate_bitmap(master[m],
                    &P,
                    &xoff, &yoff,
                    &excess_black, &excess_white,
                    &matching_black, &matching_white);
      if (score > best_score) {
        best_score = score; best_xoff = xoff; best_yoff = yoff;
        best_excess_black = excess_black;
        best_excess_white = excess_white;
        best_matching_black = matching_black;
        best_matching_white = matching_white;
        best_master_index = m;
        character = master[m];
      }
    }
  }

  if (c_image.count == 0) {
    int i, x;
    // now we know the dimensions of where the result will be stored...
    c_image.width = character->width;
    c_image.height = character->height;
    c_image.name = character->name;
    c_image.y = (long int **)malloc(c_image.height * sizeof(long int *));
    for (i = 0; i < c_image.height; i++) {
      c_image.y[i] = (long int *)malloc(c_image.width * sizeof(long int));
      for (x = 0; x < c_image.width; x++) {
         c_image.y[i][x] = 0;
      }
    }
  }
  
  if (best_score > 0.0) {
    int x, y;
    debugoff(character, &P, best_xoff, best_yoff,
              best_excess_black, best_excess_white,
              best_matching_black, best_matching_white,
             best_score);
    fprintf(stderr, "Merging '%s' (score %f)\n", character->name, best_score);
    //printf("%s\n", character->name); fflush(stdout);

    c_image.count += 1;

    // need to check within *both* characters bounds
    fprintf(stderr, "xoff = %d  yof = %d\n", xoff, yoff);

    addch(&c_image, master[best_master_index], &P, best_xoff, best_yoff, best_score);
  }

  return(EXIT_SUCCESS);
}


int main(int argc, char **argv) {
  int i, rc;
  
  while ((argc > 2) && (strcmp(argv[1], "-d") == 0)) {
    debug_level += 1; argc -= 1; argv += 1;
  }

  if (argc < 2) {
    fprintf(stderr, "syntax: merge [-d] <image1> [<image2> <image3> <image4> ...]\n\n");
    exit(EXIT_FAILURE);
  }
  
  initimage();

  i = 1;
  for (;;) {
    rc = merge(argv[i]);
    i += 1;
    if (rc != EXIT_SUCCESS) break;
    if (argv[i] == NULL) {
      int x, y;
      long int g;
      // OUTPUT RESULT HERE
#ifndef THRESH
#define THRESH 0
#endif
      /*  An entry looks like this:
      {
      '$',  "AW-025/line017/col089.png",
        {
          "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
          "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
          ...     
          "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
          "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
          "@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@",
          NULL,
        },
      },
      */

      printf("  {\n");
      printf("    '%s%c', ", best_master_index=='\'' || best_master_index=='\\' ? "\\" : "", best_master_index);
      printf(" \"%s\",\n", c_image.name);
      printf("    {\n");

      #ifdef NEVER
      for (y = 0; y < c_image.height; y++) {
        for (x = 0; x < c_image.width; x++) {
          #ifndef EDITABLE
          if (x == 0) printf("      ");
          int g = c_image.y[y][x] / c_image.count;
          fprintf(stdout, "%3d,", g);
          if (x == c_image.width-1)  printf("\n");
          #else
          if (x == 0) printf("      \"");
          int g = c_image.y[y][x] / c_image.count;
          if (g > THRESH) putchar('@'); else putchar(' ');
          if (x == c_image.width-1)  printf("\",\n");
          #endif
        }
      }
      #endif

      #ifndef EDITABLE
      for (y = 0; y < c_image.height; y++) {
        for (x = 0; x < c_image.width; x++) {
          if (x == 0) printf("      \"");
          int g = c_image.y[y][x] / c_image.count;
          if (g > THRESH) putchar('@'); else putchar(' ');
          if (x == c_image.width-1)  printf("\",\n");
        }
      }
      #endif

      printf("      NULL,\n    },\n");
      printf("  },\n");

      break;
    }
  }
  
  exit (rc);
  return rc;
}

