/*
    bomb - automatic interactive visual stimulation
    Copyright (C) 1994  Scott Draves <spot@cs.cmu.edu>

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <math.h>

#include "defs.h"
#include "image.h"
#include "image_db.h"
#include "match.h"

/* dot product of two sub-rectangles.  the image8_t is 8 bit per pixel and
   the Image is 32, so we compare only to SIG_CHAN */

#define SIG_CHAN 1

int
image8_compare(Image *image1, image8_t *image2)
{
  int t, i, j;
  int diff = 0;
  Image im1 = *image1;
  image8_t im2 = *image2;

  for (i = 0; i < im1.height; i++)
     for (j = 0; j < im1.width; j++) {
	u_char *p1 = (u_char *)(im1.pixels + (i * im1.stride + j));
	u_char *p2 = (u_char *)(im2.p + (i * im2.stride + j));
	u_char  g = p1[SIG_CHAN];
	t = g - *p2;
	diff += t * t;

     }
  return diff;
}

int
image8_climb(image8_t *base, Image *tile)
{
   Image try;
   int t, current = image8_compare(tile, base);
   if (0 == current)
      return 1;
      
   try = *tile;
   if (try.origin_y + try.height < try.parent->height) {
      try.pixels += try.stride;
      try.origin_y++;
      t = image8_compare(&try, base);
      if (t < current) {
	 *tile = try;
	 current = t;
      }
      try.pixels -= try.stride;
      try.origin_y--;
   }
   if (try.origin_y > 0) {
      try.pixels -= try.stride;
      try.origin_y--;
      t = image8_compare(&try, base);
      if (t < current) {
	 *tile = try;
	 current = t;
      }
      try.pixels += try.stride;
      try.origin_y++;
   }
   if (try.origin_x + try.width < try.parent->width) {
      try.origin_x++;
      try.pixels += 1;
      t = image8_compare(&try, base);
      if (t < current) {
	 *tile = try;
	 current = t;
      }
      try.origin_x--;
      try.pixels -= 1;
   }
   if (try.origin_x > 0) {
      try.pixels -= 1;
      try.origin_x--;
      t = image8_compare(&try, base);
      if (t < current) {
	 *tile = try;
	 current = t;
      }
      try.pixels += 1;
      try.origin_x++;
   }
   return 0;
}

void
image8_match_init(image8_t *base,
		  Image *images, int nimages,
		  int nbest, Image *best, int *best_diffs, int *origins)
{
   int i;
   int w = base->width;
   int h = base->height;
   for (i = 0; i < nbest; i++) {
      int ii = R % nimages;
      Image *img = &images[ii];
      origins[i] = ii;
      (void) image_subimage(best + i, img,
			    R % (img->width - w),
			    R % (img->height - h),
			    w, h);
      best_diffs[i] = image8_compare(best + i, base);
   }
}

/* BIG_IMAGES and SMALL_IMAGES are NIMAGES length arrays of images.  the
   small ones are filtered down (by SMALL_FACTOR) versions of the big ones.
   search for PATTERN and return the best match found in MATCH.  it tries
   NSMALL matches in each of the small images, and the NBIG best of those
   are matched against the big images. */

#define MAX_NBIG 300

void
image8_match2(image8_t *pattern, Image *match,
	      Image *big_images, Image *small_images, int nimages,
	      int nbig, int nsmall)
{
   int i;
   Image big_match;
   static Image small_matches[MAX_NBIG];
   static int diffs[MAX_NBIG];
   static int origins[MAX_NBIG];
   int best, best_i;

   if (nbig > MAX_NBIG) {
      nbig = MAX_NBIG;
      fprintf(stderr, "nbig truncated from %d to %d\n",
	      nbig, MAX_NBIG);
   }

   image8_filter_down(pattern, &small_pattern);

   image8_match_init(&small_pattern, small_images, nimages,
		     nbig, small_matches, diffs, origins);

   for (i = 0; i < nimages; i++)
      image8_match(&small_pattern, &small_images[i],
		   nsmall, nbig, small_matches, diffs, origins, i);
	 
   for (i = 0; i < nbig; i++) {
      Image *small_match = &small_matches[i];
      int match_i, diff;
      match_i = origins[i];
      image_subimage(&big_match, &big_images[match_i],
		     small_match->origin_x * SMALL_FACTOR,
		     small_match->origin_y * SMALL_FACTOR,
		     pattern->width, pattern->height);
      diff = image8_compare(&big_match, pattern);
      if (0 == i || diff < best) {
	 best = diff;
	 *match = big_match;
      }
   }
}



int
image8_find_largest(int n, int *diffs)
{
   int i;
   int j = 0;
   int v = diffs[j];

   for (i = 1; i < n; i++)
      if (diffs[i] > v) {
	 v = diffs[i];
	 j = i;
      }
   return j;
}
   

/* match NTRIES rectangles in IMAGE against BASE.  return the NBEST matches
   in BEST, and how good each match is in BEST_DIFFS.  the BEST and
   BEST_DIFFS arrays must be initialized on entry.  should use a heap
   instead of an array */

void
image8_match(image8_t *base,
	     Image *image, int ntries,
	     int nbest, Image *best, int *best_diffs,
	     int *origins, int index)
{
   int w = base->width;
   int h = base->height;
   int y, i, j;
   int n_per_row, d_row;
   int npix;
   int worst_of_best;
   int worst_index;
   Image try;

   if (ntries > (image->height - h)) {
      n_per_row = (int) ceil(ntries / (double)(image->height - h));
      d_row = 1;
   } else {
      n_per_row = 1;
      d_row = (int) ceil((image->height - h) / (double)ntries);
   }

   worst_index = image8_find_largest(nbest, best_diffs);
   worst_of_best = best_diffs[worst_index];

   image_subimage(&try, image, 0, 0, w, h);
			    
   for (y = 0; y < image->height - h; y += d_row) {
      Pixel *row_base = image->pixels + y * image->stride;
      try.origin_y = y;
      for (j = 0; j < n_per_row; j++) {
	 int diff;
	 try.origin_x = R % (image->width - w);
	 try.pixels = row_base + try.origin_x;
	 diff = image8_compare(&try, base);
	 if (diff < worst_of_best) {
	    origins[worst_index] = index;
	    best_diffs[worst_index] = diff;
	    best[worst_index] = try;
	    worst_index = image8_find_largest(nbest, best_diffs);
	    worst_of_best = best_diffs[worst_index];
	 }
      }
   }
}

void
image8_fill(image8_t *image, int v)
{
   int w = image->width;
   int h = image->height;
   u_char *p = image->p;
   int s = image->stride;
   int i, j;
   
   for (i = 0; i < h; i++)
      for (j = 0; j < w; j++)
	 p[i * s + j] = v;
}


void
image8_filter_down(image8_t *from, image8_t *to)
{
   image8_t f = *from;
   image8_t t = *to;
   int h_scale = f.height / t.height;
   int w_scale = f.width / t.width;
   int i, j;
   int npix;
   npix = h_scale * w_scale;

   for (i = 0; i < t.height; i++)
      for (j = 0; j < t.width; j++) {
	 int ii, jj;
	 int tot = 0;
	 for (ii = 0; ii < h_scale; ii++)
	    for (jj = 0; jj < w_scale; jj++) {
	       tot += f.p[f.stride * (i * h_scale + ii) +
			  (j * w_scale + jj)];
	    }
	 t.p[t.stride * i + j] = tot / npix;
      }
}


void
image8_blit(Image *from, image8_t *to)
{
   Image f = *from;
   int stride = to->stride;
   u_char *tp = to->p;
   int i, j;

#if 1
   if (f.height > to->height)
      fprintf(stderr, "f.height > to->height (%d > %d)\n",
	      f.height, to->height);
   if (f.width > to->width)
      fprintf(stderr, "f.width > to->width (%d > %d)\n",
	      f.width, to->width);
#endif

   for (i = 0; i < f.height; i++)
      for (j = 0; j < f.width; j++)
	 tp[stride * i + j] =
	    f.pixels[f.stride * i + j];
}

void
image8_blit8(image8_t *from, image8_t *to)
{
   int w = from->width;
   int h = from->height;
   u_char *fp = from->p;
   int fstride = from->stride;
   int tstride = to->stride;
   u_char *tp = to->p;
   int i, j;

   for (i = 0; i < h; i++)
      for (j = 0; j < w; j++)
	 tp[tstride * i + j] = fp[fstride * i + j];
}
