# include "Ximage.h"

static float slope = 1.0;
static float start = 0.0;
static int MaxValue = 255;

// XXX inline this if needed
static int PixelLookup(float value) {
  int out;
  out = MIN (MAX (slope * value - start, 0), MaxValue);
  return (out);
}

void Remap24 (Graphic *graphic, KapaImageWidget *image, Matrix *matrix) {

  int i, j, ii, jj;
  int i_start, i_end, j_start, j_end;
  int dropback, extra;  /* this is a bit of a kludge... */
  int dx, dy, DX, DY, pixelN;
  double expand, Rx, Ry, X, Y;
  int expand_in, expand_out;
  unsigned char *out_pix, *out_pix2, *data;
  float *imdata, *in_pix, *in_pix2;
  unsigned char pixel1[256], pixel2[256], pixel3[256];
  unsigned char pixvalue1, pixvalue2, pixvalue3;
  unsigned long back;
  unsigned char back1, back2, back3;

  // local arrays for pixel values
  for (i = 0; i < 256; i++) { /* set up pixel array */
    pixel1[i] = 0x0000ff &  graphic[0].cmap[i].pixel;
    pixel2[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 8);
    pixel3[i] = 0x0000ff & (graphic[0].cmap[i].pixel >> 16);
  }
  back = graphic[0].back;
  back1 = 0x0000ff & back;
  back2 = 0x0000ff & (back >> 8);
  back3 = 0x0000ff & (back >> 16);

  // define the color transform parameters
  MaxValue = graphic[0].Npixels - 1;
  if (image[0].range != 0.0) {
    slope = graphic[0].Npixels / image[0].range;
    start = graphic[0].Npixels * image[0].zero / image[0].range;
  } else {
    slope = 1.0;
    start = image[0].zero;
  }

  // set up expansions
  expand = expand_in = expand_out = 1.0;
  if (image[0].expand == 0) /* set up expansions */
    image[0].expand = 1;
  if (image[0].expand > 0) {
    expand = 1 / (1.0*image[0].expand);
    expand_out = image[0].expand;
    expand_in  = 1;
  }
  if (image[0].expand < 0) {
    expand = fabs((double)image[0].expand);
    expand_out = 1;
    expand_in  = -image[0].expand;
  }

  dx = image[0].picture.dx;
  dy = image[0].picture.dy;
  DX = matrix[0].Naxis[0];
  DY = matrix[0].Naxis[1];
  extra = 4 - (dx * 3) % 4;

  /* X,Y are the image coordinates of the first image pixel */
  X = MAX(0.5*(DX - dx*expand) - image[0].X, 0);
  if ((int)X != X) 
    X = (int) X + 1;
  else 
    X = (int) X;
  Y = MAX(0.5*(DY - dy*expand) - image[0].Y, 0);
  if ((int)Y != Y) 
    Y = (int) Y + 1;
  else 
    Y = (int) Y;

  /* Rx,Ry are the screen coordinates of the first image pixel */
  Rx = (X + image[0].X - 0.5*DX)/expand + 0.5*dx;
  Ry = (Y + image[0].Y - 0.5*DY)/expand + 0.5*dy;

  i_start = MIN (MAX (Rx, 0), dx - expand_out + 1);
  j_start = MIN (MAX (Ry, 0), dy - expand_out + 1);
  
  if (image[0].expand > 0) {
    i_end = MAX (MIN (i_start + ((int)(expand*(dx-i_start)))/expand, expand_out*(DX-X) + Rx), 0);
    j_end = MAX (MIN (j_start + ((int)(expand*(dy-j_start)))/expand, expand_out*(DY-Y) + Ry), 0);
  } else {
    i_end = MAX (MIN (dx, (DX-X)/expand + Rx), 0);
    j_end = MAX (MIN (dy, (DY-Y)/expand + Ry), 0);
  }    

  dropback = expand_out - (i_end - i_start) % expand_out;
  if ((i_end - i_start) % expand_out == 0) dropback = 0;

  data = out_pix = (unsigned char *) image[0].picture.data;
  imdata  = (float *) matrix[0].buffer;
  in_pix  = &imdata[DX*(int)MAX(Y,0) + (int)MAX(X,0)];

  /********** below we do the mapping from buffer pixels (in) to picture pixels (out) **********/

  /**** fill in bottom area ****/
  for (j = 0; j < j_start; j++) {
    for (i = 0; i < dx; i++, out_pix+=3) {
      out_pix[0] = back1;
      out_pix[1] = back2;
      out_pix[2] = back3;
    }
    out_pix += extra;
  }
  
  for (j = j_start; j < j_end; j+= expand_out, in_pix += expand_in*DX) {
    out_pix = &data[j*(3*dx+extra)];

    /**** fill in area to the left of the picture ****/
    for (jj = 0; (i_start > 0) && (jj < expand_out); jj++) { 
      out_pix2 = out_pix + jj*(3*dx + extra);
      for (i = 0; i < i_start; i++, out_pix2+=3) {
	out_pix2[0] = back1;
	out_pix2[1] = back2;
	out_pix2[2] = back3;
      }
    }
    out_pix += 3*i_start;
    
    /*** fill in the picture region ***/
    in_pix2 = in_pix;
    if (expand_out == 1) {
      for (i = i_start; i < i_end; i++, in_pix2+= expand_in, out_pix+=3) {
	pixelN = PixelLookup(*in_pix2);
	out_pix[0] = pixel1[pixelN];
	out_pix[1] = pixel2[pixelN];
	out_pix[2] = pixel3[pixelN];
      }
    } else {
      for (i = i_start; i < i_end; i+= expand_out, in_pix2++, out_pix+= 3*expand_out) { 
	pixelN = PixelLookup(*in_pix2);
	pixvalue1 = pixel1[pixelN];
	pixvalue2 = pixel2[pixelN];
	pixvalue3 = pixel3[pixelN];
	out_pix2 = out_pix;
	for (jj = 0; jj < expand_out; jj++, out_pix2+=3*(dx-expand_out)+extra) {
	  for (ii = 0; ii < expand_out; ii++, out_pix2+=3) {
	    out_pix2[0] = pixvalue1; 
	    out_pix2[1] = pixvalue2; 
	    out_pix2[2] = pixvalue3; 
	  }
	}
      }
    }
    out_pix -= 3*dropback;
    
    /**** fill in area to the right of the picture ****/
    for (jj = 0; jj < expand_out; jj++) {
      out_pix2 = out_pix + jj*(3*dx+extra);
      for (i = i_end; i < dx; i++, out_pix2+=3) {
	out_pix2[0] = back1;
	out_pix2[1] = back2;
	out_pix2[2] = back3;
      }
    }
  } 
  
  /*
  if ((j_end - j_start) % expand_out > 0)
    out_pix -= expand_out - (j_end - j_start) % expand_out;
  */
  
  out_pix = &data[j_end*(3*dx+extra)];
  /**** fill in top area ****/
  for (j = 0; j < (dy - j_end); j++) {
    for (i = 0; i < dx; i++, out_pix+=3) { 
      out_pix[0] = back1;
      out_pix[1] = back2;
      out_pix[2] = back3;
    }
    out_pix+=extra;
  }

  image[0].picture.pix = XCreateImage (graphic[0].display, graphic[0].visual, graphic[0].depth, ZPixmap, 0, 
					image[0].picture.data, image[0].picture.dx, image[0].picture.dy, 32, 0);
}

