#include <config.h>
#include <setjmp.h>
#include "gdk_imlib.h"
#include "gdk_imlib_private.h"

#ifdef HAVE_LIBJPEG
#include <jpeglib.h>

/** 
 * This error handling is broken beyond belief, but oh well it works
 **/

struct ImLib_JPEG_error_mgr
{
  struct jpeg_error_mgr pub;
  sigjmp_buf          setjmp_buffer;
};

typedef struct ImLib_JPEG_error_mgr *emptr;

static void
g_JPEGFatalErrorHandler(j_common_ptr cinfo)
{
  /* FIXME:
   * We should somehow signal what error occurred to the caller so the
   * caller can handle the error message */
  emptr               errmgr;

  errmgr = (emptr) cinfo->err;
  cinfo->err->output_message(cinfo);
  siglongjmp(errmgr->setjmp_buffer, 1);
  return;
}

unsigned char      *
loader_jpeg (FILE * f, int *w, int *h, int *t)
{
  struct jpeg_decompress_struct cinfo;
  struct ImLib_JPEG_error_mgr jerr;
  unsigned char      *data, *line[16], *ptr;
  int                 x, y, i;

  *t = 0;
  cinfo.err = jpeg_std_error(&(jerr.pub));
  jerr.pub.error_exit = g_JPEGFatalErrorHandler;

  /* error handler to longjmp to, we want to preserve signals */
  if (sigsetjmp(jerr.setjmp_buffer, 1))
    {
      /* Whoops there was a jpeg error */
      jpeg_destroy_decompress(&cinfo);
      return NULL;
    }

  jpeg_create_decompress(&cinfo);
  jpeg_stdio_src(&cinfo, f);
  jpeg_read_header(&cinfo, TRUE);
  cinfo.do_fancy_upsampling = FALSE;
  cinfo.do_block_smoothing = FALSE;
  jpeg_start_decompress(&cinfo);
  *w = cinfo.output_width;
  *h = cinfo.output_height;
  data = _gdk_malloc_image(*w, *h);
  if (!data)
    {
      jpeg_destroy_decompress(&cinfo);
      return NULL;
    }
  ptr = data;

  if (cinfo.rec_outbuf_height > 16)
    {
      fprintf(stderr, "gdk_imlib ERROR: JPEG uses line buffers > 16. Cannot load.\n");
      return NULL;
    }
  if (cinfo.output_components == 3)
    {
      for (y = 0; y < *h; y += cinfo.rec_outbuf_height)
	{
	  for (i = 0; i < cinfo.rec_outbuf_height; i++)
	    {
	      line[i] = ptr;
	      ptr += *w * 3;
	    }
	  jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
	}
    }
  else if (cinfo.output_components == 1)
    {
      for (i = 0; i < cinfo.rec_outbuf_height; i++)
	{
	  if ((line[i] = malloc(*w)) == NULL)
	    {
	      int                 t = 0;

	      for (t = 0; t < i; t++)
		free(line[t]);
	      jpeg_destroy_decompress(&cinfo);
	      return NULL;
	    }
	}
      for (y = 0; y < *h; y += cinfo.rec_outbuf_height)
	{
	  jpeg_read_scanlines(&cinfo, line, cinfo.rec_outbuf_height);
	  for (i = 0; i < cinfo.rec_outbuf_height; i++)
	    {
	      for (x = 0; x < *w; x++)
		{
		  *ptr++ = line[i][x];
		  *ptr++ = line[i][x];
		  *ptr++ = line[i][x];
		}
	    }
	}
      for (i = 0; i < cinfo.rec_outbuf_height; i++)
	free(line[i]);
    }
  jpeg_finish_decompress(&cinfo);
  jpeg_destroy_decompress(&cinfo);

  return data;
}

gint
saver_jpeg (GdkImlibImage *im, char *file, GdkImlibSaveInfo *info)
{
      struct jpeg_compress_struct cinfo;
      struct jpeg_error_mgr jerr;
      JSAMPROW            row_pointer[1];
      int                 row_stride;
      FILE *f;
	     
      f = fopen(file, "wb");
      if (f)
	{
	  cinfo.err = jpeg_std_error(&jerr);
	  jpeg_create_compress(&cinfo);
	  jpeg_stdio_dest(&cinfo, f);
	  cinfo.image_width = im->rgb_width;
	  cinfo.image_height = im->rgb_height;
	  cinfo.input_components = 3;
	  cinfo.in_color_space = JCS_RGB;
	  jpeg_set_defaults(&cinfo);
	  jpeg_set_quality(&cinfo, (100 * info->quality) >> 8, TRUE);
	  jpeg_start_compress(&cinfo, TRUE);
	  row_stride = cinfo.image_width * 3;
	  while (cinfo.next_scanline < cinfo.image_height)
	    {
	      row_pointer[0] = im->rgb_data + (cinfo.next_scanline * row_stride);
	      jpeg_write_scanlines(&cinfo, row_pointer, 1);
	    }
	  jpeg_finish_compress(&cinfo);
	  fclose(f);
	  return 1;
	}
      return 0;
}

#else
unsigned char      *
loader_jpeg (FILE * f, int *w, int *h, int *t)
{
	return NULL;
}

gint
saver_jpeg (GdkImlibImage *im, char *file, GdkImlibSaveInfo *info)
{
	return 0;
}
#endif
