#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <jpeglib.h>
#include <jerror.h>

#include <amscimglib4/amscimglib4.h>
#include <amscimglib4/amscimglib4_intl.h>

#ifdef __cplusplus
extern "C" {  
#endif


AMSCIMGLIB4_API void amscimglib4_readimage_jpeg(const char *fname, amscimglib4_image *img)
{

    int rc, i, j;

    FILE *fp = NULL; //the file pointer

    //variables for the decompressor
    struct jpeg_decompress_struct cinfo;
    struct jpeg_error_mgr jerr;

    int row_stride, X, Y, pixsz;
    unsigned char *jpg_buffer;
    unsigned char *rowptr[1];

    amscimglib4_pixel px;

    int I,J;

    //load the jpeg data from a file into a memory buffer
    fp = fopen(fname,"rb");
    if(fp!=NULL)
    {


      //begin processing the jpeg

      //allocate a decompress struct with the default error handler
      cinfo.err = jpeg_std_error(&jerr);
      jpeg_create_decompress(&cinfo);

      //configure this decompressor to read data from the file stream
      jpeg_stdio_src(&cinfo,fp);

      rc = jpeg_read_header(&cinfo, TRUE);
      if(rc!=1)
      {
        printf("amscimglib4_readimg_jpg: Error! %s not a normal JPEG.\n", fname);
        fclose(fp);
        return;
      }

      jpeg_start_decompress(&cinfo);

      X = cinfo.output_width;
      Y = cinfo.output_height;
      pixsz = cinfo.output_components;
      row_stride = X*pixsz;



      amscimglib4_image_resize(img,X,Y);
      //amscimglib4_delete_image(img);
      //amscimglib4_new_image(img,X,Y);

      //allocate buffer
      jpg_buffer = (unsigned char *) malloc(sizeof(unsigned char)*X*Y*pixsz);

      //read the jpeg
      while(cinfo.output_scanline < cinfo.output_height)
      {
        rowptr[0] = jpg_buffer + cinfo.output_scanline*row_stride; //pointer to pointer to row
        jpeg_read_scanlines(&cinfo, rowptr, 1);
      }

      //translate into my image format
      for(I=0;I<X;I++)
      {
        for(J=0;J<Y;J++)
        {
          if(pixsz == 1)
          {
            //greyscale
            px.R = jpg_buffer[(I+X*J)*pixsz+0];
            px.G = jpg_buffer[(I+X*J)*pixsz+0];
            px.B = jpg_buffer[(I+X*J)*pixsz+0];
            px.A = 255;
            amscimglib4_set_pixel(img,I,J,px);
          }
          else if(pixsz == 3)
          {
            //RGB - what I imagine most jpegs actually are

            px.R = jpg_buffer[(I+X*J)*pixsz+0];
            px.G = jpg_buffer[(I+X*J)*pixsz+1];
            px.B = jpg_buffer[(I+X*J)*pixsz+2];
            px.A = 255;
            amscimglib4_set_pixel(img,I,J,px);
          }
          else if(pixsz == 4)
          {
            //RGBA? careful - might be CYMK?
            px.R = jpg_buffer[(I+X*J)*pixsz+0];
            px.G = jpg_buffer[(I+X*J)*pixsz+1];
            px.B = jpg_buffer[(I+X*J)*pixsz+2];
            px.A = jpg_buffer[(I+X*J)*pixsz+3];
            amscimglib4_set_pixel(img,I,J,px);
          }
          else
          {
            printf("amscimglib4_read_jpeg: Doesn't know how to handle pixel size of %d\n.",pixsz);
          }
        }
      }

      //free the buffer
      free(jpg_buffer);

      jpeg_finish_decompress(&cinfo); //cleans things up. lets you reuse the cinfo object
      jpeg_destroy_decompress(&cinfo); //free everything

      fclose(fp);
    }
    else
    {
      printf("amscimglib4_readimage_jpeg: Error! %s could not be opened!\n",fname);
    }

    return;
}


AMSCIMGLIB4_API void amscimglib4_writeimage_jpeg(const char *fname, amscimglib4_image *img, int quality)
{
  int I,J;
  unsigned char *jpg_buffer; //points to a large array of RGBRGBRGB data
  int height = img->sizey;
  int width = img->sizex;
  FILE *fp = NULL;

  unsigned char *row_pointer[1];
  int row_stride;

  struct jpeg_compress_struct cinfo;
  struct jpeg_error_mgr jerr;

  amscimglib4_pixel px;

  //step 1 - allocate and initialize a JPEG compression object

  cinfo.err = jpeg_std_error(&jerr);
  //initialize the JPEG compression object
  jpeg_create_compress(&cinfo);
  //specify data destination (a file)
  fp = fopen(fname,"wb");
  if(fp==NULL)
  {
    printf("amscimglib4_writeimage_jpeg: Error! %s could not be opened!\n",fname);
  }
  else
  {
    jpeg_stdio_dest(&cinfo,fp);

    //set up the input image description
    cinfo.image_width = width; //image width and height
    cinfo.image_height = height;
    cinfo.input_components = 3; //color components per pixel
    cinfo.in_color_space = JCS_RGB;

    jpeg_set_defaults(&cinfo);
    jpeg_set_quality(&cinfo,quality,TRUE); //limit to baseline-JPEG values
    //quality ranges from 1 to 100
    jpeg_start_compress(&cinfo,TRUE); //true ensures we will write a complete JPEG file.

    row_stride = width*3;

    //allocate and translate the image buffer to an RGB bytebuffer
    jpg_buffer = (unsigned char *)malloc(sizeof(unsigned char)*width*height*3);
    for(I=0;I<img->sizex;I++)
    {
      for(J=0;J<img->sizey;J++)
      {
        amscimglib4_get_pixel(img,I,J,&px);

        jpg_buffer[(I+J*img->sizex)*3+0] = px.R;
        jpg_buffer[(I+J*img->sizex)*3+1] = px.G;
        jpg_buffer[(I+J*img->sizex)*3+2] = px.B;
      }
    }

    while(cinfo.next_scanline <cinfo.image_height)
    {
      //jpeg_write_scanlines expects an array of pointers to scanlines.
      row_pointer[0] = & jpg_buffer[cinfo.next_scanline*row_stride];
      //how can any of these jpeglib authors miss the fact they are passing a pointer? Why allocate one element when passing this pointer?
      jpeg_write_scanlines(&cinfo, row_pointer, 1);
    }

    //finish compression
    jpeg_finish_compress(&cinfo);
    //I think this concludes writing the file

    free(jpg_buffer);
    fclose(fp);

    //cleanup steps - frees memory
    jpeg_destroy_compress(&cinfo);

  }

  return;
}



#ifdef __cplusplus
}; //end extern "C"
#endif