#include "FSTexture.h"


// LoadPCXFile()
// desc: loads a PCX file into memory
unsigned char *FSTexture::LoadPCXFile(char *filename, PCXHEADER *pcxHeader)
{
     int idx = 0;                  // counter index
     int c;                             // used to retrieve a char from the file
     int i;                             // counter index
     int numRepeat;      
     FILE *filePtr;                // file handle
     int width;                         // pcx width
     int height;                        // pcx height
     unsigned char *pixelData;     // pcx image data
     unsigned char *paletteData;   // pcx palette data

     // open PCX file
     filePtr = fopen(filename, "rb");
     if (filePtr == NULL)
          return NULL;

     // retrieve first character; should be equal to 10
     c = getc(filePtr);
     if (c != 10)
     {
          fclose(filePtr);
          return NULL;
     }

     // retrieve next character; should be equal to 5
     c = getc(filePtr);
     if (c != 5)
     {
          fclose(filePtr);
          return NULL;
     }

     // reposition file pointer to beginning of file
     rewind(filePtr);

     // read 4 characters of data to skip
     fgetc(filePtr);
     fgetc(filePtr);
     fgetc(filePtr);
     fgetc(filePtr);

     // retrieve leftmost x value of PCX
     pcxHeader->xMin = fgetc(filePtr);       // loword
     pcxHeader->xMin |= fgetc(filePtr) << 8; // hiword

     // retrieve bottom-most y value of PCX
     pcxHeader->yMin = fgetc(filePtr);       // loword
     pcxHeader->yMin |= fgetc(filePtr) << 8; // hiword

     // retrieve rightmost x value of PCX
     pcxHeader->xMax = fgetc(filePtr);       // loword
     pcxHeader->xMax |= fgetc(filePtr) << 8; // hiword

     // retrieve topmost y value of PCX
     pcxHeader->yMax = fgetc(filePtr);       // loword
     pcxHeader->yMax |= fgetc(filePtr) << 8; // hiword

     // calculate the width and height of the PCX
     width = pcxHeader->xMax - pcxHeader->xMin + 1;
     height = pcxHeader->yMax - pcxHeader->yMin + 1;

     // allocate memory for PCX image data
     pixelData = (unsigned char*)malloc(width*height);

     // set file pointer to 128th byte of file, where the PCX image data starts
     fseek(filePtr, 128, SEEK_SET);
     
     // decode the pixel data and store
     while (idx < (width*height))
     {
          c = getc(filePtr);
          if (c > 0xbf)
          {
               numRepeat = 0x3f & c;
               c = getc(filePtr);

               for (i = 0; i < numRepeat; i++)
               {
                    pixelData[idx++] = c;
               }
          }
          else
               pixelData[idx++] = c;

          fflush(stdout);
     }

     // allocate memory for the PCX image palette
     paletteData = (unsigned char*)malloc(768);

     // palette is the last 769 bytes of the PCX file
     fseek(filePtr, -769, SEEK_END);

     // verify palette; first character should be 12
     c = getc(filePtr);
     if (c != 12)
     {
          fclose(filePtr);
          return NULL;
     }

     // read and store all of palette
     for (i = 0; i < 768; i++)
     {
          c = getc(filePtr);
          paletteData[i] = c;
     }

     // close file and store palette in header
     fclose(filePtr);
     pcxHeader->palette = paletteData;

     // return the pixel image data
     return pixelData;
}

// LoadPCXTexture()
// desc: loads a PCX image file as a texture
void FSTexture::LoadPCXTexture(char *filename)
{
     PCXHEADER texInfo;            // header of texture
//     texture_t *thisTexture;       // the texture
     unsigned char *unscaledData;// used to calculate pcx
     int i;                             // index counter
     int j;                             // index counter
     int width;                         // width of texture
     int height;                        // height of texture

     // load the PCX file into the texture struct
     data = LoadPCXFile(filename, &texInfo);
     if (data == NULL)
     {
          free(data);
     //     return NULL;
     }

     // store the texture information
     palette = texInfo.palette;
     width = texInfo.xMax - texInfo.xMin + 1;
     height = texInfo.yMax - texInfo.yMin + 1;
     textureType = PCX;

     // allocate memory for the unscaled data
     unscaledData = (unsigned char*)malloc(width*height*4);

     // store the unscaled data via the palette
     for (j = 0; j < height; j++) 
     {
          for (i = 0; i < width; i++) 
          {
               unscaledData[4*(j*width+i)+0] = (unsigned char)palette[3*data[j*width+i]+0];
               unscaledData[4*(j*width+i)+1] = (unsigned char)palette[3*data[j*width+i]+1];
               unscaledData[4*(j*width+i)+2] = (unsigned char)palette[3*data[j*width+i]+2];
               unscaledData[4*(j*width+i)+3] = (unsigned char)255;
          }
     }

     // find width and height's nearest greater power of 2
     width = this->width;
     height = this->height;

     // find width's
     i = 0;
     while (width)
     {
          width /= 2;
          i++;
     }
     scaledHeight = (long)pow(2, i-1);

     // find height's
     i = 0;
     while (height)
     {
          height /= 2;
          i++;
     }
     scaledWidth = (long)pow(2, i-1);

     // clear the texture data
     if (data != NULL)
     {
          free(data);
          data = NULL;
     }

     // reallocate memory for the texture data
     data = (unsigned char*)malloc(scaledWidth*scaledHeight*4);
     
     // use the GL utility library to scale the texture to the unscaled dimensions
     gluScaleImage(GL_RGBA, width, height, GL_UNSIGNED_BYTE, unscaledData, scaledWidth, scaledHeight, GL_UNSIGNED_BYTE, data);

//     return thisTexture;

}


bool FSTexture::LoadTGA_CMP(FILE *in_file, GLubyte *imageData)
{
	GLuint   bytesPerPixel = bpp / 8;
	GLuint   pixelcount    = height * width;
	GLuint   currentpixel  = 0;
	GLuint   currentbyte   = 0;
	GLubyte *colorbuffer   = (GLubyte*)malloc(bytesPerPixel);
	
	if (colorbuffer == NULL)
	{
		printf("error, unable to allocate memory!\n");
		return false;
	}
	
	do
	{
		GLubyte chunkheader = 0;
		if (fread(&chunkheader, sizeof(GLubyte), 1, in_file) == 0)
		{
			printf("error, could not read RLE header.\n");
			free(colorbuffer);
			return false;
		}
		
		if (chunkheader < 128)
		{
			short counter;
			chunkheader++;
			for (counter = 0; counter < chunkheader; counter++)
			{
				if (fread(colorbuffer, 1, bytesPerPixel, in_file) != bytesPerPixel)
				{
					printf("error, could not read image data.\n");
					free(colorbuffer);
					return false;
				}
				
				imageData[currentbyte    ] = colorbuffer[2];
				imageData[currentbyte + 1] = colorbuffer[1];
				imageData[currentbyte + 2] = colorbuffer[0];
				
				if (bytesPerPixel == 4)
					imageData[currentbyte + 3] = colorbuffer[3];
				
				currentbyte += bytesPerPixel;
				currentpixel++;
				
				if (currentpixel > pixelcount)
				{
					printf("error, too many pixels read.\n");
					free(colorbuffer);
					return false;
				}
			}
		}
		else
		{
			short counter;
			chunkheader -= 127;
			if (fread(colorbuffer, 1, bytesPerPixel, in_file) != bytesPerPixel)
			{
				printf("error, could not read from file.\n");
				free(colorbuffer);
				return false;
			}
			
			for (counter = 0; counter < chunkheader; counter++)
			{
				imageData[currentbyte    ] = colorbuffer[2];
				imageData[currentbyte + 1] = colorbuffer[1];
				imageData[currentbyte + 2] = colorbuffer[0];
				
				if (bytesPerPixel == 4)
					imageData[currentbyte + 3] = colorbuffer[3];
				
				currentbyte += bytesPerPixel;
				currentpixel++;
				
				if(currentpixel > pixelcount)
				{
					printf("error, too many pixels read.\n");
					free(colorbuffer);
					return false;
				}
			}
		}
	} while (currentpixel < pixelcount);
	free(colorbuffer);
	
	return true;
}

bool FSTexture::LoadTGA_RAW(FILE *in_file, GLubyte *imageData)
{
	GLuint bytesPerPixel = bpp / 8;
	GLuint imageSize = width * height * bytesPerPixel;
	GLuint temp;
	
	if (fread(imageData, 1, imageSize, in_file) != imageSize)
	{
		printf("error, file appears to be damaged!\n");
		return false;
	}
	
	for(int i = 0; i < int(imageSize); i += bytesPerPixel)
	{
		temp = imageData[i];
      imageData[i] = imageData[i + 2];
      imageData[i + 2] = temp;
   }

   return true;
}
// LoadTexture()
// desc: loads a texture given the filename
bool FSTexture::LoadTexture(char *filename)
{
	GLubyte  RAW_Header[12] = { 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	GLubyte  CMP_Header[12] = { 0, 0,10, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
	GLubyte  TGAcompare[12];
	GLubyte  header[6];
	GLuint   type = GL_RGBA;
	GLubyte *imageData;
	
	int fType = 0;
	
	FILE *in_file = fopen(filename, "rb");
	
	printf("Loading texture %s: ", filename);
	
	if (in_file == NULL)
	{
		printf("error, file not found!\n");
		return NULL;
	}
	
	if (fread(TGAcompare, 1, sizeof(TGAcompare), in_file) != sizeof(TGAcompare))
	{
		printf("error, invalid format!\n");
		fclose(in_file);
		return NULL;
	}
	
	if (memcmp(RAW_Header, TGAcompare, sizeof(TGAcompare)) == 0)
		fType = 0;
	else if (memcmp(CMP_Header, TGAcompare, sizeof(TGAcompare)) == 0)
		fType = 1;
	else
	{
		printf("error, invalid format!\n");
		fclose(in_file);
		return false;
	}
	
	if (fread(header, 1, sizeof(header), in_file) != sizeof(header))
	{
		printf("error, invalid format!\n");
		fclose(in_file);
		return false;
	}
	
	width  = header[1] * 256 + header[0];
	height = header[3] * 256 + header[2];
	bpp    = header[4];
	if (width <= 0 || height <= 0 || (bpp != 24 && bpp != 32))
	{
		printf("error, invalid format!\n");
		fclose(in_file);
		return false;
	}
	
	imageData = (GLubyte*)malloc(width * height * bpp);
	if (imageData == NULL)
	{
		printf("error, unable to allocate memory!\n");
		fclose(in_file);
		return false;
	}
	
	if (fType)
	{
		if (!LoadTGA_CMP(in_file, imageData))
		{
			fclose(in_file);
			free(imageData);
			return false;
		}
	}
	else
	{
		if (!LoadTGA_RAW(in_file, imageData))
		{
			fclose(in_file);
			free(imageData);
			return false;
		}
	}
	fclose(in_file);
	
	glGenTextures(1, &texID);
	if (bpp == 24)
		type = GL_RGB;
	
	glBindTexture(GL_TEXTURE_2D, texID);
	/*if (UseMipMaps)
	{
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
		gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, type, GL_UNSIGNED_BYTE, (GLvoid *)imageData);
	}
	else*/
	{
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
		glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
		glTexImage2D(GL_TEXTURE_2D, 0, type, width, height, 0, type, GL_UNSIGNED_BYTE, (GLvoid *)imageData);
	}
	
	free(imageData);
	imageData = NULL;
	
	printf("done.\n");
	if (fType)
		printf("-> Format of texture:    RLE Compressed.\n");
	else
		printf("-> Format of texture:    Uncompressed.\n");
	printf("-> Width of texture:     %d\n", (int)width);
	printf("-> Height of texture:    %d\n", (int)height);
   printf("-> Bitdepth of texture:  %d\n", (int)bpp);
   return true;

	
	
	
	
	/*char *extStr;

	// get extension from filename
	extStr = strchr(filename, '.');
	extStr++;
/*
	// set the texture type based on extension of filename
	if ((strcmp(extStr, "BMP") == 0) || (strcmp(extStr, "bmp") == 0))
		;//LoadBMPTexture(filename);
	else if ((strcmp(extStr, "PCX") == 0) || (strcmp(extStr, "pcx") == 0) )
		LoadPCXTexture(filename);*/
}
