#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>
#include <string.h>

#include <jpeglib.h>
#include "pic_private.h"
#include "pic.h"

// ----------------------------------------------------------------------------------------------------------------------------------

struct my_error_mgr 
{
	struct jpeg_error_mgr pub;
	jmp_buf setjmp_buffer;
};
typedef struct my_error_mgr * my_error_ptr;

// ----------------------------------------------------------------------------------------------------------------------------------

static unsigned char	error;

// ----------------------------------------------------------------------------------------------------------------------------------

void my_error_exit(j_common_ptr cinfo)
{
	my_error_ptr myerr = (my_error_ptr) cinfo->err;
	error=1;
	longjmp(myerr->setjmp_buffer, 1);
}

// ----------------------------------------------------------------------------------------------------------------------------------

unsigned long Pic_JPG_Read(const char *FileName, char **ppPal, char **ppDatas, unsigned long *w, unsigned long *h)
{
	struct jpeg_decompress_struct	cinfo;
	struct my_error_mgr				jerr;
	FILE							*file;
	JSAMPARRAY						buffer;		
	int								row_stride,i;		
	char							*pDatas,*pPal;
	unsigned long					ptr;
	
	error=0;
	ptr=0;
	file=fopen(FileName, "rb");
	if (!file)
	{
		Pic_SetError("JPG_Read, unable to open %s",FileName);
		return(0);
	}
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	setjmp(jerr.setjmp_buffer);
	if (error)
	{
		Pic_SetError("JPG_Read, internal decompression error");
		jpeg_destroy_decompress(&cinfo);
		return(0);
	}
	jpeg_create_decompress(&cinfo);
	jpeg_stdio_src(&cinfo, file);
	(void) jpeg_read_header(&cinfo, TRUE);
	*w=cinfo.image_width;
	*h=cinfo.image_height;
	if (!ppPal)
	{
		pDatas=(char*)Pic_calloc(1,(*w)*(*h)*3);
	}
	else
	{
		pDatas=(char*)Pic_calloc(1,(*w)*(*h));
		pPal=(char*)Pic_calloc(1,256*3);
		if (!pPal)
		{
			Pic_SetError("JPG_Read, not enough memory for palette");
			return(0);
		}
		cinfo.desired_number_of_colors = 256;
		cinfo.quantize_colors = TRUE;
		cinfo.dither_mode = JDITHER_ORDERED;
	}
	if (!pDatas)
	{
		Pic_SetError("JPG_Read, not enough memory for pic");
		return(0);
	}
	(void) jpeg_start_decompress(&cinfo);
	row_stride = cinfo.output_width * cinfo.output_components;
	buffer = (*cinfo.mem->alloc_sarray)
	((j_common_ptr) &cinfo, JPOOL_IMAGE, row_stride, 1);
	while (cinfo.output_scanline < cinfo.output_height) 
	{
		(void) jpeg_read_scanlines(&cinfo, buffer, 1);
		memcpy(&pDatas[ptr],buffer[0],row_stride);
		ptr+=row_stride;
	}
	*ppDatas=pDatas;
	if (ppPal)
	{
		for(i=0 ; i<256 ; i++)
		{
			pPal[i*3+0]=cinfo.colormap[2][i];
			pPal[i*3+1]=cinfo.colormap[1][i];
			pPal[i*3+2]=cinfo.colormap[0][i];
		}
		*ppPal=pPal;
	}	
	(void) jpeg_finish_decompress(&cinfo);
	jpeg_destroy_decompress(&cinfo);
	fclose(file);
	return(1);
}

// ----------------------------------------------------------------------------------------------------------------------------------

unsigned long Pic_JPG_Write(const char *FileName, unsigned long Qual, char *pDatas, unsigned long w, unsigned long h)
{
	struct jpeg_compress_struct	cinfo;
	struct my_error_mgr			jerr;
	FILE						*file;		
	JSAMPROW					row_pointer[1];
	int							row_stride;		

	error=0;
	file=fopen(FileName,"wb");
	if (!file)
	{
		Pic_SetError("JPG_Write, unable to open %s",FileName);
		return(0);
	}
	jpeg_create_compress(&cinfo);
	cinfo.err = jpeg_std_error(&jerr.pub);
	jerr.pub.error_exit = my_error_exit;
	setjmp(jerr.setjmp_buffer);
	if (error)
	{
		Pic_SetError("JPG_Write, internal compression error");
		jpeg_destroy_compress(&cinfo);
		return(0);
	}
	jpeg_stdio_dest(&cinfo, file);
	cinfo.image_width = w; 	
	cinfo.image_height = h;
	cinfo.input_components = 3;	
	cinfo.in_color_space = JCS_RGB;
	jpeg_set_defaults(&cinfo);
	jpeg_set_quality(&cinfo, Qual, TRUE);
	jpeg_start_compress(&cinfo, TRUE);
	row_stride = w * 3;
	while(cinfo.next_scanline<cinfo.image_height) 
	{
		row_pointer[0] = (JSAMPROW)& pDatas[cinfo.next_scanline * row_stride];
		(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
	}
	jpeg_finish_compress(&cinfo);
	fclose(file);
	jpeg_destroy_compress(&cinfo);
	return(1);
}