/*
   CMSC 427 - Term Project Candidate
   ---------------------------------

   Beth Weinstein - bweinste@wam.umd.edu
   Ben Stewart - stewartb@wam.umd.edu
   Nawfel Tricha - Topaz@endorproductions.com

   Minimal OpenGL project using Microsoft Visual C++.
   This project is a premilinary candidate for the term 
   project required by CMSC 427.

   ABSTRACT:
      In this project, we will develop several fast 
      rendering and collision routines which would 
      require C/C++ speed and accuracy and then 
      implement a VisualJ++ interface.

   MODULE:
      TrueVision Targa (24-bit) loading module.
*/

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


typedef struct _TargaHeader 
   {
	unsigned char 	id_length, colormap_type, image_type;
	unsigned short	colormap_index, colormap_length;
	unsigned char	colormap_size;
	unsigned short	x_origin, y_origin, width, height;
	unsigned char	pixel_size, attributes;
   } TargaHeader;

TargaHeader		targa_header;

/******************************************************************************************\
   NT:
   Get WORD (2 bytes) from file descriptor.
\******************************************************************************************/
int fgetLittleShort (FILE *f)
{
	unsigned char	b1, b2;
	b1 = fgetc(f);
	b2 = fgetc(f);
	return (short)(b1 + b2*256);
}

/******************************************************************************************\
   NT:
   Get DWORD (4 bytes) from file descriptor.
\******************************************************************************************/
int fgetLittleLong (FILE *f)
{
	unsigned char	b1, b2, b3, b4;
	b1 = fgetc(f);
	b2 = fgetc(f);
	b3 = fgetc(f);
	b4 = fgetc(f);
	return b1 + (b2<<8) + (b3<<16) + (b4<<24);
}

/******************************************************************************************\
   NT:
   Get short (2 bytes) from file descriptor.
\******************************************************************************************/
int LoadTGA (char *pathname, unsigned char **targa_rgba, int &x_out, int &y_out)
{
	int				columns, rows, numPixels;
	unsigned char	*pixbuf;
	int				row, column;
   FILE *fin;

   fin = fopen(pathname, "rb");
   if (!fin)
      return 0;
	targa_header.id_length = fgetc(fin);
	targa_header.colormap_type = fgetc(fin);
	targa_header.image_type = fgetc(fin);
	
	targa_header.colormap_index = fgetLittleShort(fin);
	targa_header.colormap_length = fgetLittleShort(fin);
	targa_header.colormap_size = fgetc(fin);
	targa_header.x_origin = fgetLittleShort(fin);
	targa_header.y_origin = fgetLittleShort(fin);
	targa_header.width = fgetLittleShort(fin);
	targa_header.height = fgetLittleShort(fin);
	targa_header.pixel_size = fgetc(fin);
	targa_header.attributes = fgetc(fin);

	if (targa_header.image_type!=2 
		&& targa_header.image_type!=10) 
         {
         EngineError ("LoadTGA[%s]: Only type 2 and 10 targa RGB images supported\n", pathname);
         return 0;
         }

	if (targa_header.colormap_type !=0 
		|| (targa_header.pixel_size!=32 && targa_header.pixel_size!=24))
         {
		   EngineError ("Texture_LoadTGA[%s]: Only 32 or 24 bit images supported (no colormaps)\n", pathname);
         return 0;
         }

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

	*targa_rgba = new unsigned char[numPixels*4];
	
	if (targa_header.id_length != 0)
		fseek(fin, targa_header.id_length, SEEK_CUR);  // skip TARGA image comment
	
	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++) 
            {
				unsigned char red, green, blue, alphabyte;
				switch (targa_header.pixel_size) 
               {
					case 24:
							
							blue = getc(fin);
							green = getc(fin);
							red = getc(fin);
							*pixbuf++ = red;
							*pixbuf++ = green;
							*pixbuf++ = blue;
							*pixbuf++ = 255;
							break;
					case 32:
							blue = getc(fin);
							green = getc(fin);
							red = getc(fin);
							alphabyte = getc(fin);
							*pixbuf++ = red;
							*pixbuf++ = green;
							*pixbuf++ = blue;
							*pixbuf++ = alphabyte;
							break;
				   }
			   }
		   }
	   }
	else if (targa_header.image_type==10) 
      {   // Runlength encoded RGB images
		unsigned char red,green,blue,alphabyte,packetHeader,packetSize,j;
		for(row = rows - 1; row >= 0; row--) 
         {
			pixbuf = *targa_rgba + row*columns*4;
			for(column=0; column < columns;) 
            {
				packetHeader=getc(fin);
				packetSize = 1 + (packetHeader & 0x7f);
				if (packetHeader & 0x80) {        // run-length packet
					switch (targa_header.pixel_size) 
                  {
						case 24:
								blue = getc(fin);
								green = getc(fin);
								red = getc(fin);
								alphabyte = 255;
								break;
						case 32:
								blue = getc(fin);
								green = getc(fin);
								red = getc(fin);
								alphabyte = getc(fin);
								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 = getc(fin);
									green = getc(fin);
									red = getc(fin);
									*pixbuf++ = red;
									*pixbuf++ = green;
									*pixbuf++ = blue;
									*pixbuf++ = 255;
									break;
							case 32:
									blue = getc(fin);
									green = getc(fin);
									red = getc(fin);
									alphabyte = getc(fin);
									*pixbuf++ = red;
									*pixbuf++ = green;
									*pixbuf++ = blue;
									*pixbuf++ = alphabyte;
									break;
						   }
						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:;
		   }
	   }
	
	fclose(fin);
   x_out = columns;
   y_out = rows;
   return 1;
}


/******************************************************************************************\
   NT:
   Convert a TGA buffer into a texture:
\******************************************************************************************/
void TGA2GLTexture (skin_t *skin, int gamma, int alpha, int &new_x, int &new_y)
{
	GLubyte *unScaled;
   int m_iscaledWidth, m_iscaledHeight;
	int i, j;
   ULONG k;
   //float exp_alpha;

	unScaled = new GLubyte [(skin->width + 1) * (skin->height + 1) * 4];

   if (alpha >= 0)
      {
	   for (j = 0; j < skin->height; j++)
		   for (i = 0; i < skin->width; i++) 
            {
            k = (j * skin->width + i);
			   unScaled[4 * k    ] = (GLubyte) skin->tga[4 * k    ] + gamma;
			   unScaled[4 * k + 1] = (GLubyte) skin->tga[4 * k + 1] + gamma;
			   unScaled[4 * k + 2] = (GLubyte) skin->tga[4 * k + 2] + gamma;
			   unScaled[4 * k + 3] = (GLubyte) skin->tga[4 * k + 3] + gamma + alpha;
		      }
      }
   // Font mask:
   else if (alpha == TGA_PROGRESSIVE_BLEND)
      {
	   for (j = 0; j < skin->height; j++)
		   for (i = 0; i < skin->width; i++) 
            {
            k = (j * skin->width + i);
			   unScaled[4 * k    ] = skin->tga[4 * k    ] + gamma;
			   unScaled[4 * k + 1] = skin->tga[4 * k + 1] + gamma;
			   unScaled[4 * k + 2] = skin->tga[4 * k + 2] + gamma;
            unScaled[4 * k + 3] = (skin->tga[4 * k + 2] + skin->tga[4 * k + 1] + skin->tga[4 * k]) / 3;
		      }
      }
   else if (alpha == TGA_ABSOLUTE_BLEND)
      {
	   for (j = 0; j < skin->height; j++)
		   for (i = 0; i < skin->width; i++) 
            {
            k = (j * skin->width + i);
			   unScaled[4 * k    ] = skin->tga[4 * k    ] + gamma;
			   unScaled[4 * k + 1] = skin->tga[4 * k + 1] + gamma;
			   unScaled[4 * k + 2] = skin->tga[4 * k + 2] + gamma;
            if (skin->tga[4 * k + 2] || skin->tga[4 * k + 1] || skin->tga[4 * k])
               unScaled[4 * k + 3] = 255;
            else
               unScaled[4 * k + 3] = 0;
		      }
      }
	if (skin->width > 512)       m_iscaledWidth = 1024;
	else if (skin->width > 256)  m_iscaledWidth = 512;
	else if (skin->width > 128)  m_iscaledWidth = 256;
	else if (skin->width > 64)   m_iscaledWidth = 128;
	else if (skin->width > 32)   m_iscaledWidth = 64;
	else if (skin->width > 16)   m_iscaledWidth = 32;
	else if (skin->width > 8)    m_iscaledWidth = 16;
	else if (skin->width > 4)    m_iscaledWidth = 8;
	else if (skin->width > 2)    m_iscaledWidth = 4;
	else if (skin->width > 1)    m_iscaledWidth = 2;
	if (skin->height > 512)      m_iscaledHeight = 1024;
	else if (skin->height > 256) m_iscaledHeight = 512;
	else if (skin->height > 128) m_iscaledHeight = 256;
	else if (skin->height > 64)  m_iscaledHeight = 128;
	else if (skin->height > 32)  m_iscaledHeight = 64;
	else if (skin->height > 16)  m_iscaledHeight = 32;
	else if (skin->height > 8)   m_iscaledHeight = 16;
	else if (skin->height > 4)   m_iscaledHeight = 8;
	else if (skin->height > 2)   m_iscaledHeight = 4;
	else if (skin->height > 1)   m_iscaledHeight = 2;

	skin->glTexture = new GLubyte [(m_iscaledWidth + 1) * (m_iscaledHeight + 1) * 4];
	gluScaleImage (GL_RGBA, skin->width, skin->height, GL_UNSIGNED_BYTE, unScaled, m_iscaledWidth, m_iscaledHeight, GL_UNSIGNED_BYTE, skin->glTexture);
	
   //skin->width = m_iscaledWidth;
   //skin->height = m_iscaledHeight;

   new_x = m_iscaledWidth;
   new_y = m_iscaledHeight;

	delete [] unScaled;
	delete [] skin->tga;
   skin->tga = NULL;
}