#ifndef CHARSET
// pre-built font
#define CHARSET "nchar.h"
#endif

// 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 <omp.h>

#define THRESH 128

#include CHARSET   // Pick up appropriate font

static int debug_level = 0;

typedef unsigned char pixel;

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

#define MASTERS 128
image *master[MASTERS];

int SPACE = -1; // Index of SPACE glyph
int NOISE_THRESHOLD = 1;
static long int smallest_glyph_size = 0x7FFFFFFF; // How many pixels in the smallest non-space glyph?

image *convertasciimap(int c) {
  image *m;
  int x, i, width, height;
  long int glyph_size;
  
  if (C[c].line[0] == NULL) return NULL;
  m = malloc(sizeof(image));
  m->name = C[c].ch;
  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 *));
  glyph_size = 0L;
  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;
        glyph_size += 1L;
      }
    }
  }
  if (glyph_size < smallest_glyph_size) {
    if (glyph_size == 0L) {
      if (SPACE < 0) SPACE = c; // take the first one.
    } else {
      smallest_glyph_size = glyph_size;
    }
  }
  m->width = width; m->height = height;
  return m;
}

void initimage(void) {
  int i;
  for (i = 0; i < MASTERS; i++) {
    master[i] = convertasciimap(i);
  }
  if (SPACE < 0) SPACE = 0; // default.
  NOISE_THRESHOLD = smallest_glyph_size/4;
  if (debug_level >= 1) fprintf(stderr, "Glyphs with fewer than %d pixels set will be treated as empty.\n", NOISE_THRESHOLD);
}

// ignore. quick hack.
int RANDOM(int percentage) {
  long int R = random();
  if (R < 0) R = -R;
  if ((R%100) <= percentage) {
    //fprintf(stderr, "ADDED\n");
    return 1;
  } else {
    //fprintf(stderr, "IGNORED\n");
    return 0;
  }
}

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)++;            // THIS WAS SUPPRESSED BUT NOW REENABLED. STILL NEEDS A NON-100% WEIGHTING HOWEVER
          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
      if (RANDOM(85)) (*excess_white)++;               // RESTORED. Ignore the hacky part for now.
      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.

// WE NEED EXTRA PARAMETERS FOR MISSING PIXELS THAT ARE ONLY MISSING BECAUSE THE
// CONTENDER GLYPH IS SLIGHTLY OUTSIDE THE GRID SQUARE OF THE EXEMPLAR.  If these
// pixels are weighed less heavily they won't count against the character as much
// as they are doing and will fix the C vs O problem.

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);
  // return 1.0/(float)excess_black; // failed experiment - improved some, ruined more.
  /*
       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 999999999.9; // 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);
  }
  //           1         2         3         4         5         6         7         8         9         10        11        12        13        14
  //  12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
  //   //XALCCL JOB  (T193***,914,1,2,200),' ED SATTERTHWAITE ',MSGLEVEI=1      1OB  18
  float score = (3000.0 - (float)excess_black)*2.1 + (3000.0 - (float)excess_white) * 0.4;
  /*
    1.5 vs 0.5 initially
    
  O: B+ 11  B- 341  B= 1839  W= 227  Score=5829.500000
  U: B+ 18  B- 285  B= 1895  W= 220  Score=5843.850098

  after increasing black and decreasing white
  O: B+ 11  B- 341  B= 1839  W= 227  Score=5846.000000  +17
  U: B+ 18  B- 285  B= 1895  W= 220  Score=5857.200195  +14  +11

  after decreasing black and increasing white
  O: B+ 11  B- 341  B= 1839  W= 227  Score=5747.000000 -82
  U: B+ 18  B- 285  B= 1895  W= 220  Score=5777.100098 -66  +30

  */
  
  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.

  //#pragma parallel for num_threads(4)
  
  *excess_black = *excess_white = *matching_black = *matching_white = 0UL; // init to 0 before adding all pixels
  for (y = (yoff < 0 ? yoff : 0); y < master->height + (yoff > 0 ? yoff : 0); y++) {    // WHY MASTER??? SURELY CONTENDER?
    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);
}

// 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;
}


int chocr(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, "chocr: cannot allocate 'command' at line %d\n",
            __LINE__-3);
    exit(EXIT_FAILURE);
  }
  sprintf(command, cmd, filename);
  
  im = popen(command, "r");
  if (im == NULL) {
    fprintf(stderr, "chocr: cannot load %s - %s\n", filename, strerror(errno));
    exit(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, "chocr: cannot allocate 'y' at line %d\n", __LINE__-3);
          exit(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,
                    "chocr: cannot allocate 'x' at line %d\n", __LINE__-3);
            exit(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, "chocr: not a pgm format file: %s\n", line);
        exit(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,
                    "chocr: 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) > THRESH ? 255 : 0 );
        }
      } 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,
                      "chocr: pixel (%d,%d) out of range (0:%d,0:%d):  %s\n\n",
                      x,y, P.width-1,P.height-1, line);
            fprintf(stderr, "chocr: not a pgm format file: %s\n", line);
            exit(EXIT_FAILURE);
          } else {
            // ASSIGN GREY VALUE
            P.y[y][x] = (g > THRESH ? 255 : 0);
          }
        } else {
          fprintf(stderr, "chocr: not a pgm format file: %s\n", line);
          exit(EXIT_FAILURE);
        }
      }
    }
  }
  if (pclose(im) != 0) {
    fprintf(stderr, "chocr: could not load %s\n", filename);
    exit(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, best_xoff, best_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;
  
  {
    int any_set_pixels = 0; // fast test for empty cell
    int x, y;
    for (y = 0; y < P.height; y++) {
      for (x = 0; x < P.width; x++) {
        if (!P.y[y][x]) {
          any_set_pixels += 1;
        }
      }
    }
    if (any_set_pixels < NOISE_THRESHOLD) { // Can include some noise.
      if (debug_level >= 1) fprintf(stderr, "Contender consists of %d pixels - treating as a blank cell.\n", any_set_pixels);
        best_score = 999999999.9; // early quit flag
        best_xoff = 0; best_yoff = 0;
        best_excess_black = 0;
        best_excess_white = 0;
        best_matching_black = any_set_pixels;
        best_matching_white = P.height*P.width - any_set_pixels;
        character = master[SPACE];
    } else {
      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;
            character = master[m];
          }
        }
      }
    }
  }

  if (best_score > 0.0) {
    debugoff(character, &P, best_xoff, best_yoff,
              best_excess_black, best_excess_white,
              best_matching_black, best_matching_white,
             best_score);
    fprintf(stderr, "Recognised as '%s' (score %f)\n", character->name, best_score);
    printf("%s\n", character->name); fflush(stdout);
  }

  exit(EXIT_SUCCESS);
  return EXIT_FAILURE;
}

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

  if (argc != 2) {
    fprintf(stderr, "syntax: chocr [-d] <image>\n\n");
    exit(EXIT_FAILURE);
  }
  
  initimage();

  return chocr(argv[1]);
}
