#include "toollib.h"
#include "piclib.h"

byte_t*	g_tgabuffer;
byte_t*	g_tgabuffptr;

/*****************************************************************************
	TL_LoadPCX

*****************************************************************************/
void TL_LoadPCX(char* filename, byte_t** pic, byte_t** palette, int* width, int* height)
{
	byte_t*	raw;
	pcx_t*	pcx;
	int		x;
	int		y;
	int		len;
	int		databyte;
	int		runlength;
	byte_t*	out;
	byte_t*	pix;

	// load the file
	len = TL_LoadFile(filename,(void **)&raw);

	// parse the PCX file
	pcx = (pcx_t*)raw;
	raw = &pcx->data;

	pcx->xmin           = TL_LittleShort(pcx->xmin);
	pcx->ymin           = TL_LittleShort(pcx->ymin);
	pcx->xmax           = TL_LittleShort(pcx->xmax);
	pcx->ymax           = TL_LittleShort(pcx->ymax);
	pcx->hres           = TL_LittleShort(pcx->hres);
	pcx->vres           = TL_LittleShort(pcx->vres);
	pcx->bytes_per_line = TL_LittleShort(pcx->bytes_per_line);
	pcx->palette_type   = TL_LittleShort(pcx->palette_type);

	if (pcx->manufacturer != 0x0a || 
		pcx->version != 5 || 
		pcx->encoding != 1 || 
		pcx->bits_per_pixel != 8 || 
		pcx->xmax >= 640 || 
		pcx->ymax >= 480)
		TL_Error("Bad pcx file %s",filename);
	
	if (palette)
	{
		*palette = (byte_t*)TL_Malloc(768);
		memcpy(*palette,(byte_t*)pcx + len - 768,768);
	}

	if (width)
		*width = pcx->xmax+1;

	if (height)
		*height = pcx->ymax+1;

	if (!pic)
		return;

	out  = (byte_t*)TL_Malloc((pcx->ymax+1)*(pcx->xmax+1));
	*pic = out;
	pix  = out;

	for (y=0; y<=pcx->ymax; y++, pix += pcx->xmax+1)
	{
		for (x=0; x<=pcx->xmax; )
		{
			databyte = *raw++;

			if((databyte & 0xC0) == 0xC0)
			{
				runlength  = databyte & 0x3F;
				databyte = *raw++;
			}
			else
				runlength = 1;

			while (runlength-- > 0)
				pix[x++] = databyte;
		}
	}

	if (raw - (byte_t *)pcx > len)
		TL_Error("PCX file %s was malformed",filename);

	TL_Free(pcx);
}

/*****************************************************************************
	TL_SavePCX

*****************************************************************************/
void TL_SavePCX(char* filename, byte_t* data, int width, int height, byte_t* palette)
{
	int		i;
	int		j;
	int		length;
	pcx_t*	pcx;
	byte_t*	pack;
	  
	pcx = (pcx_t*)TL_Malloc(width*height*2+1000);

	pcx->manufacturer   = 0x0A;		// PCX id
	pcx->version        = 5;		// 256 color
 	pcx->encoding       = 1;		// uncompressed
	pcx->bits_per_pixel = 8;		// 256 color
	pcx->xmin           = 0;
	pcx->ymin           = 0;
	pcx->xmax           = TL_LittleShort((short)(width-1));
	pcx->ymax           = TL_LittleShort((short)(height-1));
	pcx->hres           = TL_LittleShort((short)width);
	pcx->vres           = TL_LittleShort((short)height);
	pcx->color_planes   = 1;		// chunky image
	pcx->bytes_per_line = TL_LittleShort((short)width);
	pcx->palette_type   = TL_LittleShort(2);	// not a grey scale

	// pack the image
	pack = &pcx->data;
	
	for (i=0; i<height; i++)
	{
		for (j=0; j<width; j++)
		{
			if ((*data & 0xc0) != 0xC0)
				*pack++ = *data++;
			else
			{
				*pack++ = 0xC1;
				*pack++ = *data++;
			}
		}
	}
			
	// write the palette
	*pack++ = 0x0C;	
	for (i=0; i<768; i++)
		*pack++ = *palette++;
		
	// write output file 
	length = pack - (byte_t*)pcx;
	TL_SaveFile(filename,pcx,length);

	TL_Free(pcx);
} 

/*****************************************************************************
	TGA_GetByte

*****************************************************************************/
byte_t TGA_GetByte(void)
{
	return (*g_tgabuffptr++);
}

/*****************************************************************************
	TGA_GetShort

*****************************************************************************/
short TGA_GetShort(void)
{
	byte_t	msb;
	byte_t	lsb;

	lsb = g_tgabuffptr[0];
	msb = g_tgabuffptr[1];

	g_tgabuffptr += 2;

	return ((msb<<8)|lsb);
}

/*****************************************************************************
	TL_LoadTGA

*****************************************************************************/
void TL_LoadTGA(char* name, byte_t** pixels, int* width, int* height)
{
	int				columns;
	int				rows;
	int				numPixels;
	byte_t*			pixbuf;
	int				row;
	int				column;
	byte_t*			targa_rgba;
	tga_t			targa_header;
	byte_t			red;
	byte_t			green;
	byte_t			blue;
	byte_t			alphabyte;
	byte_t			packetHeader;
	byte_t			packetSize;
	byte_t			j;

	TL_LoadFile(name,(void**)&g_tgabuffer);
	g_tgabuffptr = g_tgabuffer;

	/* load unaligned tga data */
	targa_header.id_length       = TGA_GetByte();
	targa_header.colormap_type   = TGA_GetByte();
	targa_header.image_type      = TGA_GetByte();
	targa_header.colormap_index  = TGA_GetShort();
	targa_header.colormap_length = TGA_GetShort();
	targa_header.colormap_size   = TGA_GetByte();
	targa_header.x_origin        = TGA_GetShort();
	targa_header.y_origin        = TGA_GetShort();
	targa_header.width           = TGA_GetShort();
	targa_header.height          = TGA_GetShort();
	targa_header.pixel_size      = TGA_GetByte();
	targa_header.attributes      = TGA_GetByte();

	if (targa_header.image_type != 2 && targa_header.image_type != 10) 
		TL_Error("TL_LoadTGA: %s - Only type 2 and 10 targa RGB images supported",name);

	if ((targa_header.colormap_type != 0) || (targa_header.pixel_size != 32 && targa_header.pixel_size != 24))
		TL_Error("TL_LoadTGA: %s - Only 32 or 24 bit images supported (no colormaps)",name);

	columns   = targa_header.width;
	rows      = targa_header.height;
	numPixels = columns * rows;

	if (width)
		*width = columns;
	if (height)
		*height = rows;

	targa_rgba = (byte_t*)TL_Malloc(numPixels*4);
	*pixels    = targa_rgba;

	if (targa_header.id_length != 0)
	{
		// skip TARGA image comment
		g_tgabuffptr += targa_header.id_length;
	}

	if (targa_header.image_type==2) 
	{  
		// Uncompressed, RGB images
		for (row=rows-1; row>=0; row--) 
		{
			pixbuf = targa_rgba + row*columns*4;
			for(column=0; column<columns; column++) 
			{
				switch (targa_header.pixel_size) 
				{
					case 24:
							blue      = TGA_GetByte();
							green     = TGA_GetByte();
							red       = TGA_GetByte();
							alphabyte = 255; 
							break;

					case 32:
							blue      = TGA_GetByte();
							green     = TGA_GetByte();
							red       = TGA_GetByte();
							alphabyte = TGA_GetByte();
							break;
				}

				*pixbuf++ = red;
				*pixbuf++ = green;
				*pixbuf++ = blue;
				*pixbuf++ = alphabyte;

			}
		}
	}
	else if (targa_header.image_type==10) 
	{   
		// Runlength encoded RGB images
		for (row=rows-1; row>=0; row--)
		{
			pixbuf = targa_rgba + row*columns*4;
			for(column=0; column<columns; ) 
			{
				packetHeader = TGA_GetByte();
				packetSize   = 1 + (packetHeader & 0x7f);
				if (packetHeader & 0x80) 
				{        
					// run-length packet
					switch (targa_header.pixel_size)
					{
						case 24:
							blue      = TGA_GetByte();
							green     = TGA_GetByte();
							red       = TGA_GetByte();
							alphabyte = 255;
							break;

						case 32:
							blue      = TGA_GetByte();
							green     = TGA_GetByte();
							red       = TGA_GetByte();
							alphabyte = TGA_GetByte();
							break;
					}
	
					for(j=0; j<packetSize; j++) 
					{
						*pixbuf++ = red;
						*pixbuf++ = green;
						*pixbuf++ = blue;
						*pixbuf++ = alphabyte;
						column++;

						if (column==columns) 
						{ 
							// run spans across rows
							column=0;
							if (row>0)
								row--;
							else
								goto breakOut;
							pixbuf = targa_rgba + row*columns*4;
						}
					}
				}
				else 
				{
					// non run-length packet
					for(j=0; j<packetSize; j++)
					{
						switch (targa_header.pixel_size)
						{
							case 24:
								blue      = TGA_GetByte();
								green     = TGA_GetByte();
								red       = TGA_GetByte();
								alphabyte = 255;
								break;

							case 32:
								blue      = TGA_GetByte();
								green     = TGA_GetByte();
								red       = TGA_GetByte();
								alphabyte = TGA_GetByte();
								break;
						}

						*pixbuf++ = red;
						*pixbuf++ = green;
						*pixbuf++ = blue;
						*pixbuf++ = alphabyte;
						column++;

						if (column == columns) 
						{
							// pixel packet run spans across rows
							column=0;
							if (row>0)
								row--;
							else
								goto breakOut;
							pixbuf = targa_rgba + row*columns*4;
						}						
					}
				}
			}
breakOut:;
		}
	}
	
	TL_Free(g_tgabuffer);
}

/*****************************************************************************
	TL_SaveTGA

	Saves TGA.  Supports r/w 16/24/32 bpp.
*****************************************************************************/
void TL_SaveTGA(char* filename, byte_t* pixels, int width, int height, int sbpp, int tbpp)
{
	int				handle;
	tga_t			tga;
	unsigned short	rgba5551;
	unsigned long	rgba8888;
	int				r;
	int				g;
	int				b;
	int				x;
	int				y;
	int				a;
	byte_t*			tgabuffer;
	byte_t*			tgabufferptr;
	byte_t*			rawbufferptr;
	byte_t*			tempbuffer;
	byte_t*			tempbufferptr;
	int				bytesperpixel;

	// all source is upsampled into easy 32 bit rgba8888 
	// and downsampled into tga buffer
	tempbuffer = (byte_t*)TL_Malloc(width*height*4);

	if (sbpp == 16)
	{
		/* source is 16 bit rgba */
		rawbufferptr = pixels;
		for (y=0; y<height; y++)
		{
			tempbufferptr = tempbuffer + y*width*4;
			for (x=0; x<width; x++)
			{
				rgba5551 = *(unsigned short*)rawbufferptr;
				r        = (rgba5551 & 0xF800)>>11;
				g        = (rgba5551 & 0x07C0)>>6;
				b        = (rgba5551 & 0x003E)>>1;
				a        = (rgba5551 & 0x01);
				tempbufferptr[0] = (byte_t)(b * (255.0/31.0));
				tempbufferptr[1] = (byte_t)(g * (255.0/31.0));
				tempbufferptr[2] = (byte_t)(r * (255.0/31.0));
				tempbufferptr[3] = (byte_t)(a * 255.0);

				rawbufferptr  += sizeof(unsigned short);
				tempbufferptr += 4;
			}
		}
	}
	else if (sbpp == 24)
	{
		/* source is 24 bit rgba */
		rawbufferptr = pixels;
		for (y=0; y<height; y++)
		{
			tempbufferptr = tempbuffer + y*width*4;
			for (x=0; x<width; x++)
			{
				tempbufferptr[0] = rawbufferptr[0];
				tempbufferptr[1] = rawbufferptr[1];
				tempbufferptr[2] = rawbufferptr[2];
				tempbufferptr[3] = 255;

				rawbufferptr  += 3;
				tempbufferptr += 4;
			}
		}
	}
	else if (sbpp == 32)
	{
		/* source is 32 bit rgba */
		memcpy(tempbuffer,pixels,width*height*4);
	}
	else
		TL_Error("TL_SaveTGA: cannot handle source %d bits per pixel",sbpp);

	if (tbpp == 16)
		bytesperpixel = 2;
	else if (tbpp == 24)
		bytesperpixel = 3;
	else if (tbpp == 32)
		bytesperpixel = 4;
	else
		TL_Error("TL_SaveTGA: cannot handle target %d bits per pixel",tbpp);

	handle = TL_SafeOpenWrite(filename);

	/* write the targa header */
	tga.id_length       = 0;
	tga.colormap_type   = 0;
	tga.image_type      = 2;
	tga.colormap_index  = 0;
	tga.colormap_length = 0;
	tga.colormap_size   = 0;
	tga.x_origin        = 0;
	tga.y_origin        = 0;
	tga.width           = width;
	tga.height          = height;
	tga.pixel_size      = tbpp;
	tga.attributes      = 0;

	TL_SafeWrite(handle,&tga.id_length,sizeof(tga.id_length));
	TL_SafeWrite(handle,&tga.colormap_type,sizeof(tga.colormap_type));
	TL_SafeWrite(handle,&tga.image_type,sizeof(tga.image_type));
	TL_SafeWrite(handle,&tga.colormap_index,sizeof(tga.colormap_index));
	TL_SafeWrite(handle,&tga.colormap_length,sizeof(tga.colormap_length));
	TL_SafeWrite(handle,&tga.colormap_size,sizeof(tga.colormap_size));
	TL_SafeWrite(handle,&tga.x_origin,sizeof(tga.x_origin));
	TL_SafeWrite(handle,&tga.y_origin,sizeof(tga.y_origin));
	TL_SafeWrite(handle,&tga.width,sizeof(tga.width));
	TL_SafeWrite(handle,&tga.height,sizeof(tga.height));
	TL_SafeWrite(handle,&tga.pixel_size,sizeof(tga.pixel_size));
	TL_SafeWrite(handle,&tga.attributes,sizeof(tga.attributes));

	/* tga images are upside down left to right - !@#$% */
	tgabuffer = (byte_t*)TL_Malloc(width*height*bytesperpixel);

	/* source is 32 bit rgba */
	rawbufferptr = tempbuffer;
	for (y=height-1; y>=0; y--)
	{
		tgabufferptr = tgabuffer + y*width*bytesperpixel;
		for (x=0; x<width; x++)
		{
			switch (bytesperpixel)
			{
				case 2:
					break;

				case 3:
					rgba8888 = TL_BigLong(*(unsigned long*)rawbufferptr);
					r        = (rgba8888 & 0xFF000000)>>24;
					g        = (rgba8888 & 0x00FF0000)>>16;
					b        = (rgba8888 & 0x0000FF00)>>8;

					tgabufferptr[0] = b;
					tgabufferptr[1]	= g;
					tgabufferptr[2]	= r;
					break;

				case 4:
					rgba8888 = TL_BigLong(*(unsigned long*)rawbufferptr);
					r        = (rgba8888 & 0xFF000000)>>24;
					g        = (rgba8888 & 0x00FF0000)>>16;
					b        = (rgba8888 & 0x0000FF00)>>8;
					a        = rgba8888 & 0xFF;

					tgabufferptr[0] = b;
					tgabufferptr[1]	= g;
					tgabufferptr[2]	= r;
					tgabufferptr[3]	= a;
					break;
			}

			rawbufferptr += 4;
			tgabufferptr += bytesperpixel;
		}
	}

	TL_SafeWrite(handle,tgabuffer,width*height*bytesperpixel);
	close(handle);

	TL_Free(tempbuffer);
	TL_Free(tgabuffer);
}

/*****************************************************************************
	TL_LoadImage

	Loads an image based on extension.
*****************************************************************************/
void TL_LoadImage(char* name, byte_t** pixels, byte_t** palette, int* width, int* height)
{
	char	ext[16];

	TL_GetExtension(name,ext);
	if (!stricmp(ext,"pcx"))
		TL_LoadPCX(name,pixels,palette,width,height);
	else if (!stricmp(ext,"tga"))
	{
		TL_LoadTGA(name,pixels,width,height);
		*palette = NULL;
	}
	else
		TL_Error("TL_LoadImage: unknown image extension %s",ext);
}