/*
    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 "defs.h"
#include "image.h"

void
image_fill(Image *image, Pixel v)
{
   int w = image->width;
   int h = image->height;
   Pixel *p = image->pixels;
   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
image_random(Image *image)
{
   int w = image->width;
   int h = image->height;
   Pixel *p = image->pixels;
   int s = image->stride;
   int i, j;
   
   for (i = 0; i < h; i++)
      for (j = 0; j < w; j++) {
	 Pixel x;
	 x.r = R; x.g = R; x.b = R;
	 x.a = 255;
	 p[i * s + j] = x;
      }
}


/* next two functions stolen from readppm.c from photo widget.
   shouldn't the dll of graphics formats have something for this,
   then we can handle jpg too... */

static char *
ppm_fgets(line, len, fp)
    char *line;
    int len;
    FILE *fp;
{
    int incomment = 0;
    int s, i = 0;
    for(;;){
        if (i == len - 1) {
          line[i] = 0;
          break;
        }
	if( -1 == (s = fgetc(fp))) {
	    return NULL;
	 }
	 if (s == '\n' || s == '\r') {
	    line[i] = 0;
	    break;
	  }
	  line[i++] = s;
	  
    }
    if ('#' == line[0])
       return ppm_fgets(line, len, fp);
    return line;
}

static int
read_hdr(fp, wp, hp)
   FILE *fp;
   int *wp, *hp;
{
    int pmax;
    char line[64];
    if (0 == fp) {
       printf("bad file\n");
       return 0;
    }
    if( ppm_fgets(line, sizeof(line), fp) == NULL
       || strcmp(line, "P6") != 0 ) {
	return 0;
       }
    if( ppm_fgets(line, sizeof(line), fp) == NULL
       || sscanf(line, "%d%d", wp, hp) != 2 ) {
	return 0;
     }
    if( ppm_fgets(line, sizeof(line), fp) == NULL
       || sscanf(line, "%d", &pmax) != 1
       || pmax != 255 ) {
	return 0;
      }
    return 1;
}

#define file_type_unknown 0
#define file_type_ppm     1
#define file_type_gif     2
#define file_type_jpeg    3
#define file_type_tiff    4

int
file_type(FILE *fp)
{
   int r = file_type_unknown;
   char buf[4];

   if (4 != fread(buf, 1, 4, fp))
      goto done;
   if (!strncmp("GIF", buf, 3))
      r = file_type_gif;
   else if (!strncmp("P6", buf, 2))
      r = file_type_ppm;
   else if (!strncmp("II", buf, 2) ||
	    !strncmp("JJ", buf, 2))
      r = file_type_tiff;
   else
      r = file_type_jpeg; /* just guessing now */
 done:
   fseek(fp, 0, SEEK_SET);
   return r;
}

#include "tiffio.h"
   
/* should support jpeg */

int
image_read(Image *image, char *file)
{
   FILE *fp;
   int width, height;
   int bytes_read;
   int total_bytes;
   int gifLoadBomb(Image *image, char *filename);

   fp = fopen(file, "r");
   if (fp == NULL)
      return TCL_ERROR;
   switch (file_type(fp)) {

    case file_type_ppm:
      if (!read_hdr(fp, &width, &height)) {
         printf("bad header %s\n", file);
	 goto fail;
	}
      /* note ppm stores 3 bytes per pixel, we store 4.
	 so after we read it in, we have to insert gaps
	 for the alpha */
      total_bytes = width * height * 3;
      image = image_allocate(image, width, height);
      if (NULL == image)
         goto fail;
      if (total_bytes != fread(image->pixels, 1, total_bytes, fp)) {
         printf("short file %s\n", file);
	 goto fail;
       }
      fclose(fp);
      
      /* insert gaps, be careful at last pixel */
      if (1) {
	 char *p = (char *) image->pixels;
	 int scan_ppm = total_bytes;
	 int scan_rgba = width * height * sizeof(Pixel);
	 while (scan_ppm) {
	    p[scan_rgba - 1] = alpha_solid;
	    p[scan_rgba - 2] = p[scan_ppm - 1];
	    p[scan_rgba - 3] = p[scan_ppm - 2];
	    p[scan_rgba - 4] = p[scan_ppm - 3];
	    scan_rgba -= 4;
	    scan_ppm -= 3;
	 }
      }
      break;
      
    case file_type_gif:
      fclose(fp);
      return gifLoadBomb(image, file);

    case file_type_tiff:
      fclose(fp);
      if (1) {
	 uint32 w, h;
	 uint32* raster;
	 size_t npixels;
	 TIFF* tif = TIFFOpen(file, "r");

	 if (NULL == tif)
	    goto fail;

	 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w);
	 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h);
	 image = image_allocate(image, w, h);
	 if (NULL == image)
	   goto fail;
	 npixels = w * h;
	 /* _TIFFmalloc(npixels * sizeof (uint32)); */
	 raster = (uint32*) image->pixels;
	 if (0 == TIFFReadRGBAImage(tif, w, h, raster, 0))
	    goto fail;
	 TIFFClose(tif);
	 if (1) {
	    /* mirror top-bottom, permute channels */
	    int i, j;
	    for (j = 0; j < h/2; j++) {
	       Pixel *p1 = image->pixels + (j * image->stride);
	       Pixel *p2 = image->pixels + (((h-1) - j) * image->stride);
	       for (i = 0; i < w; i++) {
		  Pixel s, r, t;
		  t = p1[i];
		  s.r = t.g;
		  s.g = t.b;
		  s.b = t.a;
		  s.a = t.r;
		  t = p2[i];
		  r.r = t.g;
		  r.g = t.b;
		  r.b = t.a;
		  r.a = t.r;
		  p1[i] = r;
		  p2[i] = s;
	       }
	    }
	 }
      }
      break;

    case file_type_unknown:
    case file_type_jpeg:
      goto fail;
   }
      
   return TCL_OK;
 fail:
   fclose(fp);
   fprintf(stderr, "unable to read image: %s\n", file);
   return TCL_ERROR;
}

void
image_destroy(Image* image)
{
   if (NULL != image->pixels &&
       NULL == image->parent)
      free(image->pixels);
   free(image);
}

void
image_init(Image *image)
{
   image->width =
      image->height = 0;
   image->pixels = 0;
   image->parent = 0;
}

/* recycle passed in image and its storage if possible.
   never returns a subimage */
Image *
image_allocate(Image *image, int w, int h)
{
   int total_bytes = (sizeof(Pixel)) * w * h;
   if (NULL == image) {
      image = (Image *) malloc(sizeof(Image));
      image->pixels = (Pixel *) malloc(total_bytes);
   } else if (w != image->width ||
	      h != image->height ||
	      NULL != image->parent) {
      /* ugh, complex XXX */
      if (NULL != image->pixels &&
	  NULL == image->parent)
	 free(image->pixels);
      image->pixels = (Pixel *) malloc(total_bytes);
   }
   if (NULL == image->pixels)
     return NULL;
   image->width = w;
   image->height = h;
   image->stride = w;
   image->origin_x = 0;
   image->origin_y = 0;
   return image;
}

Image *
image_subimage(Image *image, Image *of, int x, int y, int w, int h)
{
   if (NULL == image)
      image = (Image *) malloc(sizeof(Image));
   image->width = w;
   image->height = h;
   image->stride = of->stride;
   image->pixels = of->pixels + of->stride * y + x;
   /* eliminate hierarchy */
   if (NULL == of->parent) {
      image->origin_x = x;
      image->origin_y = y;
      image->parent = of;
   } else {
      image->origin_x = of->origin_x + x;
      image->origin_y = of->origin_y + y;
      image->parent = of->parent;
   }
#if 1
   if (x + w > of->width)
      fprintf(stderr, "x + w > of->width (%d + %d > %d)\n",
	      x, w, of->width);
   if (y + h > of->height)
      fprintf(stderr, "y + h > of->height (%d + %d > %d)\n",
	      y, h, of->height);
#endif
   return image;
}


/* if the requested size is larger than the image itself, then
   the tile is silently truncated to fit */

Image *
image_random_tile(Image *image, Image *of, int size)
{
   int x, y;
   if (size >= of->width) {
      size = of->width;
      x = 0;
   } else
      x = R % (of->width - size);
   if (size >= of->height) {
      size = of->height;
      y = 0;
   } else
      y = R % (of->height - size);
   if (NULL == image)
      image = (Image *) malloc(sizeof(Image));
   image->width = size;
   image->height = size;
   image->stride = of->stride;
   image->pixels = of->pixels + of->stride * y + x;
   /* eliminate hierarchy */
   if (NULL == of->parent) {
      image->origin_x = x;
      image->origin_y = y;
      image->parent = of;
   } else {
      image->origin_x = of->origin_x + x;
      image->origin_y = of->origin_y + y;
      image->parent = of->parent;
   }
   return image;
}

void
image_mean_pixel(Image *img, Pixel *pix)
{
   int r, g, b, a;
   Pixel p;
   unsigned char *bytes = (unsigned char *) &p;
   int i, j;
   int npix;

   a = r = g = b = 0;
   for (i = 0; i < img->height; i++)
      for (j = 0; j < img->width; j++) {
	 p = img->pixels[img->stride * i + j];
	 r += bytes[0];
	 g += bytes[1];
	 b += bytes[2];
	 a += bytes[3];
      }

   npix = img->width * img->height;

   bytes[0] = r / npix;
   bytes[1] = g / npix;
   bytes[2] = b / npix;
   bytes[3] = a / npix;

   *pix = p;
}
   
   

void
image_filter_down(Image *from, Image *to)
{
   int h_scale = from->height / to->height;
   int w_scale = from->width / to->width;
   int i, j;
   Pixel p;
   int npix;

   npix = h_scale * w_scale;

   for (i = 0; i < to->height; i++)
      for (j = 0; j < to->width; j++) {
	 int ii, jj;
	 int r, g, b, a;
	 a = r = g = b = 0;
	 for (ii = 0; ii < h_scale; ii++)
	    for (jj = 0; jj < w_scale; jj++) {
	       p = from->pixels[from->stride * (i * h_scale + ii) +
				(j * w_scale + jj)];
	       a += p.a;
	       r += p.r;
	       g += p.g;
	       b += p.b;
	    }
	 p.a = a / npix;
	 p.r = r / npix;
	 p.g = g / npix;
	 p.b = b / npix;
	 to->pixels[to->stride * i + j] = p;
      }
}
  

/* and more complex arithmetic versions, with alpha */
/* no overlap */
void
image_blit(Image *from, Image *to)
{
   Image f = *from;
   Image t = *to;
   int i, j;
   for (i = 0; i < f.height; i++)
      for (j = 0; j < f.width; j++)
	 t.pixels[t.stride * i + j] =
	    f.pixels[f.stride * i + j];
}
