#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include "memory_debug.h"

#include "gif.h"

#include	"change_endian.h"

#define SWAP_S(x) \
	if ( endian_flag == 0 )		\
	x = (((((x) & 0xff)) << 8) | ((((x) & 0xff00)) >> 8)) ;
#define SWAP_L(x) \
	if ( endian_flag == 0 )		\
		x = 		\
		(((*p)  & 0x000000ff) << 24)  |  \
		(((*p) & 0x0000ff00) << 8 )  |    \
		(((*p) & 0x00ff0000) >> 8 )  |    \
		(((*p) & 0xff000000) >> 24 ) ;


typedef struct dsc_gif{     
	char header[6];
	unsigned short scrwidth;
	unsigned short scrheight;
	char pflds;
	char bcindx;
	char pxasrat;
} DSC_GIF;

typedef struct tab_col{
	short colres;
	short sogct;
	GIF_RGB paleta[256];
} TAB_COL;

typedef struct gif_gce{
	unsigned char transpcolflag;
	unsigned char userinputflag;
	unsigned char dispmeth;
	unsigned char res;
	unsigned char delaytimehi;
	unsigned char delaytimelo;
	unsigned char transpcolindex;
} GIF_GCE;

typedef struct gif_image_desk{
	unsigned short l;
	unsigned short t;
	unsigned short w;
	unsigned short h;
	unsigned char   pf;
} GIF_IMAGE_DESK;

typedef struct gif_image_iterator{
	int Itx, Ity;				// Counters
	unsigned char * IterImage;  //  Current Image pointer
	unsigned char * _IterImage; //  Image pointer
	int width,height;
	
	int invert_y;
	
	int interlaced;
	int iypos;
	int istep;
	int iheight;
	int ipass;
} GIF_IMAGE_ITERATOR;


typedef struct gif_decode{
	short curr_size;                     /* The current code size */
	short clear;                         /* Value for a clear code */
	short ending;                        /* Value for a ending code */
	short newcodes;                      /* First available code */
	short top_slot;						/* Highest code for current size */
	short slot;							/* Last read code */
	short navail_bytes;					/* # bytes left in block */
	short nbits_left;					/* # bits left in current byte */
	unsigned char  b1;					/* Current byte */
	unsigned char  byte_buff[257];		/* Current block */
	unsigned char  *pbytes;				/* Pointer to next byte in block */
} GIF_DECODE;


#define MAX_CODES   4095
static long code_mask[13]=
{
	  0,
	  0x0001, 0x0003,
	  0x0007, 0x000F,
	  0x001F, 0x003F,
	  0x007F, 0x00FF,
	  0x01FF, 0x03FF,
	  0x07FF, 0x0FFF
};




static short gif_get_next_code(GIF_DECODE *gd, unsigned char **ppData)
{
	short i, x;
	unsigned long ret;
	
	if (gd->nbits_left == 0)
	{
		if (gd->navail_bytes <= 0)
		{
			gd->pbytes = gd->byte_buff;
			gd->navail_bytes = (short)**ppData;
			(*ppData)++;
			if (gd->navail_bytes < 0){
				return(gd->navail_bytes);
			}
			else if (gd->navail_bytes){
				for (i = 0; i < gd->navail_bytes; ++i){
					if ((x = (short)**ppData) < 0)	{
						(*ppData)++;
						return x;
					}
					(*ppData)++;
					gd->byte_buff[i] = (unsigned char)x;
				}
			}
		}
		gd->b1 = (*gd->pbytes);
		gd->pbytes++;
		gd->nbits_left = 8;
		gd->navail_bytes--;
	}
	
	ret = gd->b1 >> (8 - gd->nbits_left);
	while (gd->curr_size > gd->nbits_left)
	{
		if (gd->navail_bytes <= 0)
		{
			gd->pbytes = gd->byte_buff;
			gd->navail_bytes = **ppData;
			(*ppData)++;
			if (gd->navail_bytes < 0){
				return(gd->navail_bytes);
			}
			else if (gd->navail_bytes){
				for (i = 0; i < gd->navail_bytes; ++i){
					if ((x = **ppData) < 0){	
						(*ppData)++;
						return(x);
					}
					(*ppData)++;
					gd->byte_buff[i] = (unsigned char)x;
				}
			}
		}
		gd->b1 = *(gd->pbytes);
		gd->pbytes++;
		ret |= gd->b1 << gd->nbits_left;
		gd->nbits_left += 8;
		gd->navail_bytes--;
	}
	gd->nbits_left -= gd->curr_size;
	ret &= code_mask[gd->curr_size];
	return (short)(ret);
}

static void gif_set_row(unsigned char *buf, int n, GIF_IMAGE_ITERATOR *it)
{
	if (n<0)
		n = it->width;
	memcpy(it->IterImage,buf,n);
}

static int gif_out_line(unsigned char *pixels, int linelen, GIF_IMAGE_ITERATOR *it)
{
	int y;
	if (it->interlaced){
		//sety
		y = it->iheight-it->iypos-1;
		if((0 < y) && (y < it->height)){
			it->Ity = y;
			if(it->invert_y)
				it->IterImage = it->_IterImage + (it->width)*y;
			else
				it->IterImage = it->_IterImage + (it->width)*(it->height-y);
		}
		gif_set_row(pixels, linelen, it);
		
		if ((it->iypos += it->istep) >= it->iheight){
			do{
				if(it->ipass++ > 0)
					it->istep /= 2;
				it->iypos = it->istep / 2;
			}while (it->iypos > it->iheight);
		}
		return 0;
	}
	else if (0<=it->Itx			&& 
			it->Itx<it->width	&& 
			0<=it->Ity			&& 
			it->Ity< it->height){
		
		gif_set_row(pixels, linelen, it);
		it->Ity --;
		if (it->invert_y)
			it->IterImage -= it->width;
		else
			it->IterImage += it->width;
		return 0;
	}
	return -1;
}

static int lzw_decode_func(unsigned char **ppData, GIF_IMAGE_ITERATOR *it)
{
	unsigned char *stack_ptr, *bufptr;
	unsigned char *buf;
	
	short code, fc, oc, bufcnt;
	short c, bit_size, ret;
	unsigned char stack[MAX_CODES + 1];
	unsigned char suffix[MAX_CODES + 1];
	unsigned short prefix[MAX_CODES + 1];
	GIF_DECODE gd;
	memset(&gd, 0, sizeof(gd));
	
	if ((bit_size = (short)**ppData) < 0)
		return 0;
	(*ppData)++;
	if (bit_size < 2 || 9 < bit_size)	
		return 0;
	
	gd.curr_size = bit_size + 1;
	gd.top_slot = 1 << gd.curr_size;
	gd.clear = 1 << bit_size;
	gd.ending = gd.clear + 1;
	gd.slot = gd.ending + 1;
	gd.newcodes = gd.ending + 1;
	gd.navail_bytes = 0;
	gd.nbits_left = 0;
	
	oc = fc = 0;
	buf = (unsigned char *)d_calloc(it->width + 1,1,35);
	if (buf == NULL) 
		return 0;
	
	stack_ptr = stack;
	bufptr = buf;
	bufcnt = it->width;
	while ((c = gif_get_next_code(&gd, ppData)) != gd.ending)
    {
		if (c < 0) {
			d_f_ree(buf);
			return(0);
		}
		if (c == gd.clear){
			gd.curr_size = bit_size + 1;
			gd.slot = gd.newcodes;
			gd.top_slot = 1 << gd.curr_size;
			while ((c = gif_get_next_code(&gd, ppData)) == gd.clear);
			if (c == gd.ending) 
				break;
			if (c >= gd.slot) 
				c = 0;
			oc = fc = c;
			*bufptr++ = (unsigned char)c;
			if (--bufcnt == 0)
			{
				if ((ret = gif_out_line(buf, it->width, it)) < 0){ 
					d_f_ree(buf);
					return(ret);
				}
				bufptr = buf;
				bufcnt = it->width;
            }
		}
		else{
			code = c;
			if (code >= gd.slot){
				code = oc;
				*stack_ptr++ = (unsigned char)fc;
            }
			while (code >= gd.newcodes){
	            *stack_ptr++ = suffix[code];
				code = prefix[code];
            }
			*stack_ptr++ = (unsigned char)code;
			if (gd.slot < gd.top_slot){
				fc = code;
	            suffix[gd.slot] = (unsigned char)fc;
	            prefix[gd.slot++] = oc;
				oc = c;
            }
			if (gd.slot >= gd.top_slot){
				if (gd.curr_size < 12){
					gd.top_slot <<= 1;
					++(gd.curr_size);
				}
			}
			while (stack_ptr > stack){
				*bufptr++ = *(--stack_ptr);
			    if(--bufcnt == 0){
					if ((ret = gif_out_line(buf, it->width, it)) < 0){ 
						d_f_ree(buf);
						return ret;
					}
					bufptr = buf;
					bufcnt = it->width;
				}
			}
		}
	}
	ret = 0;
	if (bufcnt != it->width)
		ret = gif_out_line(buf, (it->width - bufcnt), it);
	d_f_ree(buf);
	return(ret);
}



unsigned int *gif_uncompress(int *ret_width, int *ret_height, 
							 GIF_ENV * ge, unsigned char *data, int length)
{
	unsigned char *index_bmp;
	unsigned char *index_bmp_ptr;
	unsigned int *return_bmp;
	unsigned int *return_bmp_ptr;
	DSC_GIF dg;
	TAB_COL tc;
	GIF_GCE gg;
	GIF_IMAGE_DESK gid;
	GIF_IMAGE_ITERATOR it;
	unsigned char *end_of_gif_data;
	char ch;
	int i;
	int x,y;

	memset(&tc,0,sizeof(TAB_COL));
	end_of_gif_data = data + length;
	memcpy(&dg, data, 13);

	SWAP_S(dg.scrheight);
	SWAP_S(dg.scrwidth);

	data += 13;
	
	if(strncmp(dg.header, "GIF8", 4)!=0)
		return 0;
	tc.sogct = 1 << ((dg.pflds & 0x07) +1 );
	tc.colres = ((int)(dg.pflds & 0x70) >> 3) +1;
	if(dg.pflds & 0x80){
		memcpy(tc.paleta, data, 3*tc.sogct);
		data += 3*tc.sogct;
	}

loop:
	 if (data == end_of_gif_data)	
		 goto done;
	 ch = ((char)*data);
	 data ++;
	 if (ch == '!')                     // extension
	 {
		unsigned char count;
		if (*data++ == 0xF9)
		{
			count = *data++;
			memcpy(&gg, data, 4);
			data +=4;
		}
		while(*data++){
			data += *((unsigned char *)(data-1));
		}
		goto loop;
	 }
	 else if (ch == ',')                 // image
	 {
		memcpy(&gid, data, 9);

		SWAP_S(gid.h);
		SWAP_S(gid.l);
		SWAP_S(gid.t);
		SWAP_S(gid.w);

		data += 9;
		if (gid.pf & 0x80) 
		{
			tc.sogct = 1 << ((gid.pf & 0x07) +1);
			memcpy(tc.paleta, data, 3*tc.sogct);
			data += 3*tc.sogct;
		}
		index_bmp = (unsigned char *)d_alloc(gid.w * gid.h,21);
		memset(index_bmp, 0, gid.w*gid.h);
		*ret_width=(int)gid.w;
		*ret_height=(int)gid.h;
		
		it._IterImage = index_bmp;
		it.width = gid.w;
		it.height = gid.h;
		ge->height = gid.h;
		ge->width = gid.w;
		
		it.Itx = 0;
		it.Ity = gid.h-1;
		it.invert_y = 0; // set invert flag for windows bmp
		if(it.invert_y)
			it.IterImage = it._IterImage + (it.width)*(it.height-1);
		else
			it.IterImage = it._IterImage;
		
		it.interlaced = gid.pf & 0x40;
		it.iheight = gid.h;
		it.istep = 8;
		it.iypos = 0;
		it.ipass = 0;
		
		lzw_decode_func(&data, &it);
		goto loop;
	}

done:
	// set cmap color table
	for(i=0;i<256;++i){
		ge->cmap.color[i].r = tc.paleta[i].r;
		ge->cmap.color[i].b = tc.paleta[i].b;
		ge->cmap.color[i].g = tc.paleta[i].g;
		if(gg.transpcolflag && gg.transpcolindex == i)
			ge->cmap.color[i].x = 255;
		else
			ge->cmap.color[i].x = 0;
	}
	// if needs index color bmp, return "index_bmp" here.
	// return index_bmp;
	
	// set RGB data
	return_bmp = (unsigned int*)d_alloc(gid.w*gid.h*4,35);
	return_bmp_ptr = return_bmp;
	index_bmp_ptr = index_bmp;
	for(y=0; y<gid.h; ++y){
		for(x=0; x<gid.w; ++x, ++index_bmp_ptr, ++return_bmp_ptr){
			*return_bmp_ptr = *((unsigned int*)&(ge->cmap.color[*index_bmp_ptr]));
			//*return_bmp_ptr = (unsigned int)GetColorRGB(&(ge->cmap.color[*index_bmp_ptr]));
		}
	}
	d_f_ree(index_bmp);
	return return_bmp;
}
