# include "Ximage.h"

/* I almost have PNG images + plots working.  I need to do a few more things:
   1) decide on the dimensions of the output image/plot file -- like the jpeg covering the pixels? adjust the box to include the full plot box?
   2) update bDraw to either write palette values (as now) or RGB values into the buffer
   3) decision on output based on the existence of images or not
*/

bDrawBuffer *bDrawBufferCreate8bitRGB (int Nx, int Ny);
int bDrawImage (bDrawBuffer *buffer, Graphic *graphic);

int PNGit (int sock) {

  FILE *f;
  png_structp png_ptr;
  png_infop info_ptr;
  int Npalette;
  char filename[1024];
  bDrawBuffer *buffer = NULL;
  Graphic *graphic = NULL;
  png_color *palette = NULL;

  graphic = GetGraphic();

  /* expect a line telling the number of bytes and a filename */
  KiiScanMessage (sock, "%s", filename);

  f = fopen (filename, "w");
  if (f == (FILE *) NULL) {
    fprintf (stderr, "can't open output file %s\n", "Xgraph.png");
    return (TRUE);  /* true because otherwise it quits kapa! */
  }

  png_ptr = png_create_write_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, (png_voidp) NULL, (png_voidp) NULL);
  if (!png_ptr) {
    fprintf (stderr, "can't get png structure\n");
    fclose (f);
    return (TRUE);
  }

  info_ptr = png_create_info_struct (png_ptr);
  if (!info_ptr) {
    fprintf (stderr, "can't get png info structure\n");
    png_destroy_write_struct (&png_ptr, (png_infopp) NULL);
    fclose (f);
    return (TRUE);
  }

  if (setjmp (png_ptr[0].jmpbuf)) {
    fprintf (stderr, "can't get png return\n");
    png_destroy_write_struct (&png_ptr, &info_ptr);
    fclose (f);
    return (TRUE);
  }

  png_init_io (png_ptr, f);

  /* see docs for write-row-callback to provide progress info */
# define PALETTE 1
  if (PALETTE) {
    png_set_IHDR (png_ptr, info_ptr, graphic->dx, graphic->dy, 8, PNG_COLOR_TYPE_PALETTE, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); 
    palette = KapaPNGPalette (&Npalette);
    png_set_PLTE (png_ptr, info_ptr, palette, Npalette); 
  } else {
    // png_set_IHDR (png_ptr, info_ptr, graphic->dx, graphic->dy, 16, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    png_set_IHDR (png_ptr, info_ptr, graphic->dx, graphic->dy, 8, PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
    png_set_sRGB (png_ptr, info_ptr, PNG_sRGB_INTENT_ABSOLUTE);
  }

  /* other useful calls here:
     png_set_tIME(png_ptr, info_ptr, mod_time);
     png_set_text(png_ptr, info_ptr, text_ptr, num_text);
  */
 
  png_write_info (png_ptr, info_ptr);

  if (PALETTE) {
    buffer = bDrawIt ();
  } else {
    buffer = bDrawBufferCreate8bitRGB(graphic->dx, graphic->dy);
    bDrawImage (buffer, graphic);
  }

  png_write_image (png_ptr, buffer[0].pixels);
  png_write_end (png_ptr, info_ptr);
  png_destroy_write_struct (&png_ptr, &info_ptr);
  
  bDrawBufferFree (buffer);
  free (palette);
  fclose (f);
  return (TRUE);

}

/* For color images, I need to define the relationship between the drawing colors and the RGB values? */


bDrawBuffer *bDrawBufferCreate8bitRGB (int Nx, int Ny) {

  int i, j;
  bDrawBuffer *buffer;

  ALLOCATE (buffer, bDrawBuffer, 1);
  buffer[0].Nx = Nx;
  buffer[0].Ny = Ny;

  ALLOCATE (buffer[0].pixels, bDrawColor *, Ny);
  for (i = 0; i < Ny; i++) {
    ALLOCATE (buffer[0].pixels[i], bDrawColor, 3*Nx);
    for (j = 0; j < 3*Nx; j+=3) {
      buffer[0].pixels[i][j+0] = 0xff;
      buffer[0].pixels[i][j+1] = 0xff;
      buffer[0].pixels[i][j+2] = 0xff;
    }
  }
  return (buffer);
}

# define WHITE_R 255
# define WHITE_G 255
# define WHITE_B 255
# define MY_SWAP_INT(A,B) { int tmp; tmp = A; A = B; B = tmp; }

int bDrawImage (bDrawBuffer *buffer, Graphic *graphic) {
  
  Section *section;
  KapaImageWidget *image;

  int ii, i, j;
  int i_start, i_end, j_start, j_end;
  int I_start, J_start;
  int dropback;  /* this is a bit of a kludge... */
  int dx, dy, DX, DY, inDX, inDY;
  int expand_in, expand_out;
  double expand, Ix, Iy;
  unsigned char *out_pix;
  unsigned short *in_pix, *in_pix_ref;
  unsigned char *pixel1, *pixel2, *pixel3;

  bDrawColor *line_buffer;

  section = GetActiveSection();
  image   = section->image;
  if (image == NULL) return (TRUE);

  ALLOCATE (pixel1, unsigned char, graphic[0].Npixels);
  ALLOCATE (pixel2, unsigned char, graphic[0].Npixels);
  ALLOCATE (pixel3, unsigned char, graphic[0].Npixels);

  /** cmap[i].pixel must be defined even if X is not used **/
  for (i = 0; i < graphic[0].Npixels; i++) { /* set up pixel array */
    pixel1[i] = graphic[0].cmap[i].red >> 8;
    pixel2[i] = graphic[0].cmap[i].green >> 8;
    pixel3[i] = graphic[0].cmap[i].blue >> 8;
  }

  assert ((image[0].picture.expand >= 1) || (image[0].picture.expand <= -2));
  expand = expand_in = expand_out = 1.0;
  if (image[0].picture.expand > 0) {
    expand = 1 / (1.0*image[0].picture.expand);
    expand_out = image[0].picture.expand;
    expand_in  = 1;
  }
  if (image[0].picture.expand < 0) {
    expand = fabs((double)image[0].picture.expand);
    expand_out = 1;
    expand_in  = -image[0].picture.expand;
  }

  dx = image[0].picture.dx;
  dy = image[0].picture.dy;
  DX = image[0].image[0].matrix.Naxis[0];
  DY = image[0].image[0].matrix.Naxis[1];

  // i_start, j_start are the closest lit screen pixel to 0,0
  // I_start, J_start are the image pixel corresponding to i_start, j_start
  Picture_Lower (&i_start, &j_start, &I_start, &J_start, &image[0].image[0].matrix, &image[0].picture);

  // i_end, j_end are the closest lit screen pixel to dx, dy
  // I_end, J_end are the image pixel corresponding to i_end, j_end
  Picture_Upper (&i_end, &j_end, i_start, j_start, &image[0].image[0].matrix, &image[0].picture);

  assert (i_start <= i_end);
  assert (j_start <= j_end);

  Ix = image[0].picture.flipx ? I_start - 1 : I_start;
  Iy = image[0].picture.flipy ? J_start - 1 : J_start;

  inDX = image[0].picture.flipx ? -1 : +1;
  inDY = image[0].picture.flipy ? -1 : +1;

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

  ALLOCATE (line_buffer, bDrawColor, 3*dx);

  in_pix_ref  = &image[0].pixmap[DX*(int)MAX(Iy,0) + (int)MAX(Ix,0)];

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

  /**** fill in bottom area ****/
  out_pix = line_buffer;
  for (i = 0; i < dx; i++, out_pix+=3) {
    out_pix[0] = WHITE_R;
    out_pix[1] = WHITE_G;
    out_pix[2] = WHITE_B;
  }
  for (j = 0; j < j_start; j++) {
    memcpy (buffer[0].pixels[j], line_buffer, 3*dx);
  }
  
  /*** fill in the image data region ***/
  for (j = j_start; j < j_end; j+= expand_out, in_pix_ref += inDY*expand_in*DX) {
    
    /* create one output image line */
    in_pix = in_pix_ref;
    out_pix = line_buffer;

    /**** fill in area to the left of the picture ****/
    for (i = 0; i < i_start; i++, out_pix+=3) {
      out_pix[0] = WHITE_R;
      out_pix[1] = WHITE_G;
      out_pix[2] = WHITE_B;
    }
    
    /*** fill in the picture region ***/
    for (i = i_start; i < i_end; i+=expand_out, in_pix += inDX*expand_in) {
      for (ii = 0; ii < expand_out; ii++, out_pix+=3) {
	out_pix[0] = pixel1[*in_pix];
	out_pix[1] = pixel2[*in_pix];
	out_pix[2] = pixel3[*in_pix];
      }
    }
    
    /**** fill in area to the right of the picture ****/
    for (i = i_end; i < dx; i++, out_pix+=3) {
      out_pix[0] = WHITE_R;
      out_pix[1] = WHITE_G;
      out_pix[2] = WHITE_B;
    }

    /* write out the image line expand_out times */
    for (i = 0; i < expand_out; i++) {
      memcpy (buffer[0].pixels[j + i], line_buffer, 3*dx);
    }
  }

  /**** fill in top area ****/
  out_pix = line_buffer;
  for (i = 0; i < dx; i++, out_pix+=3) { 
    out_pix[0] = WHITE_R;
    out_pix[1] = WHITE_G;
    out_pix[2] = WHITE_B;
  }
  for (j = j_end; j < dy; j++) {
    memcpy (buffer[0].pixels[j], line_buffer, 3*dx);
  }

  free (pixel1);
  free (pixel2);
  free (pixel3);
  free (line_buffer);

  return (TRUE);
}

# if (0)

  /* I need to write the overlay objects on the jpeg image.
     if i can write / overwrite data in jpeg buffer, then do it here,
     otherwise i need to create a temporary image buffer, then write the 
     scanlines to that buffer */

  {
    int Npalette;
    png_color *palette;
    bDrawColor white, color;
    bDrawBuffer *buffer;

    palette = KapaPNGPalette (&Npalette);

    buffer = bDrawBufferCreate (dx, dy);
    bDrawSetBuffer (buffer);
    for (i = 0; i < NOVERLAYS; i++) {
      if (image[i].overlay[i].active) bDrawOverlay (image, i);
    }

    white = KapaColorByName ("white");
    for (j = 0; j < dy; j++) {
      for (i = 0; i < dx; i++) {
	color = buffer[0].pixels[j][i];
	if (color == white) continue;
	image_buffer[j*3*dx + 3*i + 0] = palette[color].red;
	image_buffer[j*3*dx + 3*i + 1] = palette[color].green;
	image_buffer[j*3*dx + 3*i + 2] = palette[color].blue;
      }
    }
    bDrawBufferFree (buffer);
  }

# endif
