#include <stdio.h>
#include <sys/types.h>
#include <jconfig.h>
#include <jpeglib.h>
#include <jerror.h>
#include <stdlib.h>
#include <setjmp.h>

extern int errnum;

typedef struct {
	struct jpeg_source_mgr pub;   /* public fields */
	unsigned char * source_ptr;   /* source pointer */
	size_t source_length;         /* source length */
} mem_source_mgr;

typedef mem_source_mgr * mem_src_ptr;

static int jpegInitflag = FALSE;
static struct jpeg_decompress_struct jcinfo;
static struct my_error_mgr jErr;
#define MAX_RGBA_IMAGE_SIZE ((640*480*4)+256)
static unsigned char * imagebuf = NULL;

METHODDEF(void)
init_mem_source (j_decompress_ptr cinfo)
{ 
   /* Nothing to do. */
} 

METHODDEF(void)
emit_message (j_common_ptr cinfo, int msg_level)
{           
    /* no output message */          
    if (msg_level < 0) cinfo->err->num_warnings++;
}

METHODDEF(boolean)
fill_input_mem_buffer (j_decompress_ptr cinfo)
{
	mem_src_ptr src = (mem_src_ptr) cinfo->src;

	if (src->pub.bytes_in_buffer == 0) {
		if ( src->pub.next_input_byte ) {
			/*  Error case 
			    No more data exist...
			    What should I do here?
			*/
			return FALSE;
		} else {
			src->pub.next_input_byte = src->source_ptr;
			src->pub.bytes_in_buffer = src->source_length;
		}
	}
	return TRUE;
}

METHODDEF(void)
skip_input_mem_data (j_decompress_ptr cinfo, long num_bytes)
{  
	mem_src_ptr src = (mem_src_ptr) cinfo->src;
   
	if (num_bytes > 0) {
		if (num_bytes > (long) src->pub.bytes_in_buffer) {
			/* This case is invalid.
			   Becase memory buffer could not be filled
			   from any way.
			   What should I do here?
			*/
		} else {
			src->pub.next_input_byte += (size_t) num_bytes;
			src->pub.bytes_in_buffer -= (size_t) num_bytes;
		}
	  }
}

METHODDEF(void)
term_mem_source (j_decompress_ptr cinfo)
{
   /* Nothing to do. */
}


GLOBAL(void)
jpeg_mem_src (j_decompress_ptr cinfo, const unsigned char * source_ptr, unsigned long source_length)
{
	mem_src_ptr src;
   
	if (cinfo->src == NULL) {     /* first time for this JPEG object? */
		cinfo->src = (struct jpeg_source_mgr *)
		(*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_PERMANENT,
                                  sizeof(mem_source_mgr));
		src = (mem_src_ptr) cinfo->src;
	}

	src = (mem_src_ptr) cinfo->src;
	src->pub.init_source = init_mem_source;  
	src->pub.fill_input_buffer = fill_input_mem_buffer;
	src->pub.skip_input_data = skip_input_mem_data;
	src->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
	src->pub.term_source = term_mem_source;
	src->source_ptr = source_ptr;
	src->source_length = source_length;
	src->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
	src->pub.next_input_byte = NULL; /* until buffer loaded */
}

struct my_error_mgr {
	struct jpeg_error_mgr pub;    /* "public" fields */
	jmp_buf setjmp_buffer;        /* for return to caller */
};

typedef struct my_error_mgr * my_error_ptr;

METHODDEF(void)
JpegErrorHandler (j_common_ptr cinfo)
{
	/* Error Handler... It is from djpeg.c.
	   I don't know this detail...
	*/
	my_error_ptr myerr = (my_error_ptr) cinfo->err;

	/* Always display the message. */
	/* We could postpone this until after returning, if we chose. */
	(*cinfo->err->output_message) (cinfo);

	/* Return control to the setjmp point */
	longjmp(myerr->setjmp_buffer, 1);
}

static int init_jpeg(j_decompress_ptr cinfo, unsigned char * source_ptr, size_t source_length)
{

    jErr.pub.error_exit = JpegErrorHandler;
    jErr.pub.emit_message = emit_message;

    jpeg_create_decompress(cinfo);    

    jpeg_mem_src(cinfo, source_ptr, source_length);
    imagebuf = malloc(MAX_RGBA_IMAGE_SIZE);
    if ( imagebuf == NULL ) return -1;

    jpegInitflag = TRUE;
    return 0;
}

unsigned char * jpeg_decode(unsigned char *srcptr,int srclen, int * dlen)
{
    int lineSize, x; 
    size_t bufsize;
    JSAMPROW rawptr;

    if ( jpegInitflag == FALSE ) {
        jcinfo.err = jpeg_std_error(&jErr.pub);
	if ( setjmp(jErr.setjmp_buffer) ) {
	    if (imagebuf) free(imagebuf);
	    jpeg_destroy_decompress(&jcinfo);
	    jpegInitflag = FALSE;
	    *dlen = 0;
	    errnum++;
	    return NULL;
	}
	if ( init_jpeg(&jcinfo, srcptr, srclen) < 0 ) {
	    jpeg_destroy_decompress(&jcinfo);
	    *dlen = 0;
	    return NULL;
	}
    } else {
	jpeg_mem_src(&jcinfo, srcptr, srclen);
    }	    

    jpeg_read_header(&jcinfo, TRUE);

    jcinfo.out_color_space = JCS_RGB;
    jcinfo.dct_method = JDCT_IFAST;
    jcinfo.jpeg_color_space = JCS_YCbCr;

    jpeg_start_decompress(&jcinfo);
	
    lineSize = jcinfo.output_width * jcinfo.output_components;
    bufsize = lineSize*jcinfo.image_height;
    if ( bufsize > MAX_RGBA_IMAGE_SIZE) {
	*dlen = 0;
	return NULL;
    }

    x = 0;
    jcinfo.err->num_warnings=0;
    while(jcinfo.output_scanline < jcinfo.output_height){
	rawptr = (JSAMPROW)(imagebuf + x);
	
	x += lineSize;
	jpeg_read_scanlines(&jcinfo, (JSAMPARRAY)&rawptr , 1);
    }

    jpeg_finish_decompress(&jcinfo);
    *dlen = bufsize;
    if  (jcinfo.err->num_warnings) return NULL;
    errnum = 0;
    return imagebuf;
}

void end_jpeg(void)
{
    if (jpegInitflag == FALSE) return;

    free(imagebuf);
    jpeg_destroy_decompress(&jcinfo);

    jpegInitflag = FALSE;
}
