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

#include "opd_jp2.h"
#include "main.h"

#ifdef WIN32
#include "jpeglib/jpeglib.h"
#else
#include "jpeglib.h"	/* Use the build-in JPEG-lib on Linux Systems */
#endif


/**
 * 
 * int readJPEG(FILE *infile, j2k_image_t **image)
 *
 * This function reads a old JPEG image from an open file.
 * 
 */
int readJPEG(FILE *infile, opd_image_t *image) {
	/* general image structures */
	int		iWidth			= 0;
	int		iHeight			= 0;
	int		iComponents		= 0;
	unsigned char *pucData	= NULL;

	int		bIsGray			= FALSE;
	int		iComponentSize	= 0;
	int		x,iDst;

	/* jpeglib related structures */
	struct jpeg_decompress_struct	dinfo;
	struct jpeg_error_mgr			jerr;
	JSAMPROW						pDstLine = NULL;

	dinfo.err	= jpeg_std_error(&jerr);
	jpeg_create_decompress(&dinfo);
	jpeg_stdio_src(&dinfo, infile);

	/* read the header */
	jpeg_read_header(&dinfo, TRUE);
	jpeg_start_decompress(&dinfo);

	/* Allocate the appropriate memory for decoding the JPEG image */
	iWidth			= dinfo.output_width;
	iHeight			= dinfo.output_height;

	switch(dinfo.out_color_space) {
	case JCS_GRAYSCALE:
		/* A simple grayscale image */ 
		iComponents		= 1;
		iComponentSize	= iWidth * iHeight;
		pucData		= (unsigned char *)(opd_jp2_alloc(iComponentSize));
		pDstLine	= (JSAMPROW)(opd_jp2_alloc(iWidth * sizeof(JSAMPLE)));
		if ((pucData == NULL) || (pDstLine == NULL)) {
			/* The component memory cannot be allocated. Free all 
			 * already allocated ressources */
			if (pucData != NULL) opd_jp2_free(pucData);	
			if (pDstLine != NULL) opd_jp2_free(pDstLine);
			jpeg_finish_decompress(&dinfo);
			jpeg_destroy_decompress(&dinfo);
			return(OPD_ERROR_NO_MEMORY);
		}

		/* We have the data, output line by line */
		iDst	= 0;
		while(dinfo.output_scanline < dinfo.output_height) {
			if (jpeg_read_scanlines(&dinfo, &pDstLine, 1) == 1) {
				/* A line is successfully decoded, 
				 * copy the line to the jpeg2000 structure */
				for (x = 0; x < iWidth;) {
					pucData[iDst++] = pDstLine[x++];
				}
			};
		}

		break;
	case JCS_RGB:
		/* simple RGB data */
		iComponents = 3;
		iComponentSize	= iWidth * iHeight * 3;
		pucData	= (unsigned char *)(opd_jp2_alloc(iComponentSize));
		pDstLine	= (JSAMPROW)(opd_jp2_alloc(iWidth * iComponents * sizeof(JSAMPLE)));

		if ((pDstLine == NULL) || (pucData == NULL)) {
			if (pucData		!= NULL) opd_jp2_free(pucData);
			if (pDstLine	!= NULL) opd_jp2_free(pDstLine);
			jpeg_finish_decompress(&dinfo);
			jpeg_destroy_decompress(&dinfo);
			return(OPD_ERROR_NO_MEMORY);
		}
	
		/* We have the data, output line by line */
		iDst = 0;
		while(dinfo.output_scanline < dinfo.output_height) {
			if (jpeg_read_scanlines(&dinfo, &pDstLine, 1) == 1) {
				/* A line is successfully decoded, 
				 * copy the line to the jpeg2000 structure */
				for (x = 0; x < iWidth * iComponents; iDst++,x++) {
					pucData[iDst] = pDstLine[x];
				}
			};
		}

		break;

	default:
		/* An unsupported Colorspace is found. It cannot 
		 * be decoded, return with an error */
		jpeg_finish_decompress(&dinfo);
		jpeg_destroy_decompress(&dinfo);
		return(OPD_ERROR_UNSUPPORTED);
	}

	jpeg_finish_decompress(&dinfo);
	jpeg_destroy_decompress(&dinfo);

	/* we have the image data read. fill out the return values */
	if (image != NULL) {
		/* set the components in the image structure */
		image -> uiChannels	= iComponents;
		image -> uiWidth	= iWidth;
		image -> uiHeight	= iHeight;
		image -> iPrecision	= 8;
		image -> pucData	= pucData;
		image -> iDc		= 1;
		image -> iDx		= iComponents * image -> iDc;
		image -> iDy		= iWidth * image -> iDx;		

		/* everything was ok, return success */
		return(OPD_ERROR_OK);
	} else {
		return(OPD_ERROR_NO_MEMORY);
	}
	return(OPD_ERROR_OK);
}

int	writeJPEG(  FILE *outfile, opd_image_t *image, int quality) {
	int	iWidth				= 0;
	int	iHeight				= 0;
	int iComponents			= 0;
	int iDx, iDy, iDc		= 0;
	unsigned int uiOffset	= 0;
	unsigned char *pucData	= NULL;

	/* Jpeg structures */
	struct jpeg_compress_struct	cinfo;
	struct jpeg_error_mgr	jerr;
	JSAMPROW	pSrcLine;
	int			x,y,c,iSrc;


	if ((outfile == NULL) || (image == NULL)) return(OPD_ERROR_BAD_PARAMETER);

	/* check whether all components have the same size */
	switch(iComponents = image -> uiChannels) {
	case 1:
		/* check the precision and whether the image is signed */
		if(image -> iPrecision != 8) return(OPD_ERROR_UNSUPPORTED);
		break;
	case 3:
		/* check that the precision is ok */
		if (image -> iPrecision != 8) return(OPD_ERROR_UNSUPPORTED);
		break;
	default:
		return(OPD_ERROR_UNSUPPORTED);
	}

	iWidth		= image->uiWidth;//(image -> x1) - (image -> x0);
	iHeight		= image->uiHeight;//(image -> y1) - (image -> y0);
	pucData		= image->pucData;
	uiOffset	= image->uiDataOffset;
	iDx			= image->iDx;
	iDy			= image->iDy;
	iDc			= image->iDc;


	/* Initialize the JPEG compression object with default error handling. */
	cinfo.err = jpeg_std_error(&jerr);
	jpeg_create_compress(&cinfo);

	cinfo.image_width		= iWidth; 		/* image width and height, in pixels */
	cinfo.image_height		= iHeight;
	cinfo.input_components	= iComponents;	/* # of color components per pixel */
	cinfo.data_precision	= 8;
	cinfo.in_color_space	= image -> uiChannels == 3 ? JCS_RGB : JCS_GRAYSCALE;

	jpeg_set_defaults(&cinfo);

	/* Specify data destination for compression */
	jpeg_stdio_dest(&cinfo, outfile);

	/* Set the compression quality */
	if (quality != -1) {
		/* A quality setting is given */
		jpeg_set_quality (&cinfo, quality, FALSE);
	}
#ifdef WIN32
	else {
		jpeg_simple_lossless(&cinfo,1,1);
	}
#endif
	
	/* Start compressor */
	jpeg_start_compress(&cinfo, TRUE);

	/* Allocate a single line for output */
	pSrcLine	= (JSAMPROW)(opd_jp2_alloc(iWidth * iComponents * sizeof(JSAMPLE)));

	if (pSrcLine == NULL) {
		/* Allocation is not ok, free the jpeg-objects and return */
		jpeg_finish_compress(&cinfo);
		jpeg_destroy_compress(&cinfo);
		return(OPD_ERROR_NO_MEMORY);
	}

	/* Process data */
	iSrc = 0;
	switch(iComponents) {
	case 1:
		for (y = 0; y < iHeight; y++) {
			for (x = 0; x < iWidth; x++,iSrc++) {
				pSrcLine[x] = (JSAMPLE)(pucData[uiOffset + x * iDx + y * iDy]);
			}
			jpeg_write_scanlines(&cinfo, &pSrcLine, 1);
		}
		break;
	default:
		for (y = 0; y < iHeight; y++) {
			for (x = 0; x < iWidth; x++) {
				for (c = 0; c < iComponents; c++) {
					pSrcLine[x * 3 + c] = (JSAMPLE)(pucData[uiOffset + x * iDx + y * iDy + c * iDc]);
				}
			}
			jpeg_write_scanlines(&cinfo, &pSrcLine, 1);
		}
		break;
	}


	/* Finish compression and release memory */
	if (pSrcLine != NULL) opd_jp2_free(pSrcLine);
	jpeg_finish_compress(&cinfo);
	jpeg_destroy_compress(&cinfo);

	return(OPD_ERROR_OK);
}