/*
 *  loadTGA.cpp
 *  Advanced2D
 *
 *  Created by Ralph Smith on 7/13/10.
 *  Copyright 2010 Ralph Smith. All rights reserved.
 *
 */

#include <stdlib.h>
#include <string.h>

#include "loadTGA.h"

namespace Advanced2D {

//	bool LoadTGA(TextureImage *texture, char *filename)			// Loads A TGA File Into Memory
/*	{    
		GLubyte		TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0};	// Uncompressed TGA Header
		GLubyte		TGAcompare[12];								// Used To Compare TGA Header
		GLubyte		header[6];									// First 6 Useful Bytes From The Header
		GLuint		bytesPerPixel;								// Holds Number Of Bytes Per Pixel Used In The TGA File
		GLuint		imageSize;									// Used To Store The Image Size When Setting Aside Ram
		GLuint		temp;										// Temporary Variable
		GLuint		type=GL_RGBA;								// Set The Default GL Mode To RBGA (32 BPP)
		
		FILE *file = fopen(filename, "rb");						// Open The TGA File
		
		if(	file==NULL ||										// Does File Even Exist?
		   fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) ||	// Are There 12 Bytes To Read?
		   memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0				||	// Does The Header Match What We Want?
		   fread(header,1,sizeof(header),file)!=sizeof(header))				// If So Read Next 6 Header Bytes
		{
			if (file == NULL) {									// Did The File Even Exist? *Added Jim Strong*
				printf("failed to open the file\n");
				return false;									// Return False
			}
			else {
				fclose(file);									// If Anything Failed, Close The File
				printf("opened the file but failed to read it\n");
				return false;									// Return False
			}
		}
		
		texture->width  = header[1] * 256 + header[0];			// Determine The TGA Width	(highbyte*256+lowbyte)
		texture->height = header[3] * 256 + header[2];			// Determine The TGA Height	(highbyte*256+lowbyte)
		
		if(	texture->width	<=0	||								// Is The Width Less Than Or Equal To Zero
		   texture->height	<=0	||								// Is The Height Less Than Or Equal To Zero
		   (header[4]!=24 && header[4]!=32))					// Is The TGA 24 or 32 Bit?
		{
			fclose(file);										// If Anything Failed, Close The File
			return false;										// Return False
		}
		
		texture->bpp	= header[4];							// Grab The TGA's Bits Per Pixel (24 or 32)
		bytesPerPixel	= texture->bpp/8;						// Divide By 8 To Get The Bytes Per Pixel
		imageSize		= texture->width*texture->height*bytesPerPixel;	// Calculate The Memory Required For The TGA Data
		
		texture->imageData=(GLubyte *)malloc(imageSize);		// Reserve Memory To Hold The TGA Data
		
		if(	texture->imageData==NULL ||							// Does The Storage Memory Exist?
		   fread(texture->imageData, 1, imageSize, file)!=imageSize)	// Does The Image Size Match The Memory Reserved?
		{
			if(texture->imageData!=NULL)						// Was Image Data Loaded
				free(texture->imageData);						// If So, Release The Image Data
			
			fclose(file);										// Close The File
			return false;										// Return False
		}

		for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel)		// Loop Through The Image Data
		{														// Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
			temp=texture->imageData[i];							// Temporarily Store The Value At Image Data 'i'
			texture->imageData[i] = texture->imageData[i + 2];	// Set The 1st Byte To The Value Of The 3rd Byte
			texture->imageData[i + 2] = temp;					// Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
		}

		fclose (file);											// Close The File
		
		if (texture[0].bpp==24)									// Was The TGA 24 Bits
		{
			type=GL_RGB;										// If So Set The 'type' To GL_RGB
		}
		
		return true;											// Texture Building Went Ok, Return True
	}
*/
	
	TGAHeader tgaheader;									// TGA header
	TGAImage tga;												// TGA image data
	
	GLubyte uTGAcompare[12] = {0,0,2, 0,0,0,0,0,0,0,0,0};	// Uncompressed TGA Header
	GLubyte cTGAcompare[12] = {0,0,10,0,0,0,0,0,0,0,0,0};	// Compressed TGA Header
	
	bool LoadUncompressedTGA(TextureImage*, const char*, FILE*);	// Load an Uncompressed file
	bool LoadCompressedTGA(TextureImage*, const char*, FILE*);	// Load a Compressed file
	
	bool LoadTGA(TextureImage* texture, char* filename)				// Load a TGA file
	{
		FILE * fTGA;												// File pointer to texture file
		fTGA = fopen(filename, "rb");								// Open file for reading
		
		if(fTGA == NULL)											// If it didn't open....
		{
			printf("ERROR: Could not open texture file\n");	// Display an error message
			return false;														// Exit function
		}
		
		if(fread(&tgaheader, sizeof(TGAHeader), 1, fTGA) == 0)					// Attempt to read 12 byte header from file
		{
			printf("ERROR: Could not read file header\n");		// If it fails, display an error message 
			if(fTGA != NULL)													// Check to seeiffile is still open
			{
				fclose(fTGA);													// If it is, close it
			}
			return false;														// Exit function
		}
		
		if(memcmp(uTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)				// See if header matches the predefined header of
		{																		// an Uncompressed TGA image
			LoadUncompressedTGA(texture, filename, fTGA);						// If so, jump to Uncompressed TGA loading code
		}
		else if(memcmp(cTGAcompare, &tgaheader, sizeof(tgaheader)) == 0)		// See if header matches the predefined header of
		{																		// an RLE compressed TGA image
			LoadCompressedTGA(texture, filename, fTGA);							// If so, jump to Compressed TGA loading code
		}
		else																	// If header matches neither type
		{
			printf("Invalid Image: TGA file be type 2 or type 10\n");		// Display an error
			fclose(fTGA);
			return false;																// Exit function
		}
		return true;															// All went well, continue on
	}
	
	bool LoadUncompressedTGA(TextureImage * texture, const char * filename, FILE * fTGA)	// Load an uncompressed TGA (note, much of this code is based on NeHe's 
	{																			// TGA Loading code nehe.gamedev.net)
		if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)					// Read TGA header
		{										
			printf("ERROR: Could not read info header\n");		// Display error
			if(fTGA != NULL)													// if file is still open
			{
				fclose(fTGA);													// Close it
			}
			return false;														// Return failular
		}	
		
		texture->width  = tga.header[1] * 256 + tga.header[0];					// Determine The TGA Width	(highbyte*256+lowbyte)
		texture->height = tga.header[3] * 256 + tga.header[2];					// Determine The TGA Height	(highbyte*256+lowbyte)
		texture->bpp	= tga.header[4];										// Determine the bits per pixel
		tga.Width		= texture->width;										// Copy width into local structure						
		tga.Height		= texture->height;										// Copy height into local structure
		tga.Bpp			= texture->bpp;											// Copy BPP into local structure
		
		if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32)))	// Make sure all information is valid
		{
			printf("ERROR: Invalid texture information\n");	// Display Error
			if(fTGA != NULL)													// Check if file is still open
			{
				fclose(fTGA);													// If so, close it
			}
			return false;														// Return failed
		}
		
		if(texture->bpp == 24)													//If the BPP of the image is 24...
		{
			texture->type	= GL_RGB;											// Set Image type to GL_RGB
		}
		else																	// Else if its 32 BPP
		{
			texture->type	= GL_RGBA;											// Set image type to GL_RGBA
		}
		
		tga.bytesPerPixel	= (tga.Bpp / 8);									// Compute the number of BYTES per pixel
		tga.imageSize		= (tga.bytesPerPixel * tga.Width * tga.Height);		// Compute the total amout ofmemory needed to store data
		texture->imageData	= (GLubyte *)malloc(tga.imageSize);					// Allocate that much memory
		
		if(texture->imageData == NULL)											// If no space was allocated
		{
			printf("ERROR: Could not allocate memory for image\n");	// Display Error
			fclose(fTGA);														// Close the file
			return false;														// Return failed
		}
		
		if(fread(texture->imageData, 1, tga.imageSize, fTGA) != tga.imageSize)	// Attempt to read image data
		{
			printf("ERROR: Could not read image data\n");		// Display Error
			if(texture->imageData != NULL)										// If imagedata has data in it
			{
				free(texture->imageData);										// Delete data from memory
			}
			fclose(fTGA);														// Close file
			return false;														// Return failed
		}
				
//		for(GLuint i=0; i<int(tga.imageSize); i+=tga.bytesPerPixel)		// Loop Through The Image Data
//		{														// Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue)
//			GLubyte temp = texture->imageData[i];				// Temporarily Store The Value At Image Data 'i'
//			texture->imageData[i] = texture->imageData[i + 2];	// Set The 1st Byte To The Value Of The 3rd Byte
//			texture->imageData[i + 2] = temp;					// Set The 3rd Byte To The Value In 'temp' (1st Byte Value)
//		}
		
		// Byte Swapping Optimized By Steve Thomas
		for(GLuint cswap = 0; cswap < (int)tga.imageSize; cswap += tga.bytesPerPixel)
		{
			texture->imageData[cswap] ^= texture->imageData[cswap+2];
			texture->imageData[cswap+2] ^= texture->imageData[cswap];
			texture->imageData[cswap] ^= texture->imageData[cswap+2];
		}
		
		fclose(fTGA);															// Close file
		return true;															// Return success
	}
	
	bool LoadCompressedTGA(TextureImage * texture, const char * filename, FILE * fTGA)		// Load COMPRESSED TGAs
	{ 
		if(fread(tga.header, sizeof(tga.header), 1, fTGA) == 0)					// Attempt to read header
		{
			printf("ERROR: Could not read info header\n");		// Display Error
			if(fTGA != NULL)													// If file is open
			{
				fclose(fTGA);													// Close it
			}
			return false;														// Return failed
		}
		
		texture->width  = tga.header[1] * 256 + tga.header[0];					// Determine The TGA Width	(highbyte*256+lowbyte)
		texture->height = tga.header[3] * 256 + tga.header[2];					// Determine The TGA Height	(highbyte*256+lowbyte)
		texture->bpp	= tga.header[4];										// Determine Bits Per Pixel
		tga.Width		= texture->width;										// Copy width to local structure
		tga.Height		= texture->height;										// Copy width to local structure
		tga.Bpp			= texture->bpp;											// Copy width to local structure
		
		if((texture->width <= 0) || (texture->height <= 0) || ((texture->bpp != 24) && (texture->bpp !=32)))	//Make sure all texture info is ok
		{
			printf("ERROR: Invalid texture information\n");	// If it isnt...Display error
			if(fTGA != NULL)													// Check if file is open
			{
				fclose(fTGA);													// Ifit is, close it
			}
			return false;														// Return failed
		}
		
		tga.bytesPerPixel	= (tga.Bpp / 8);									// Compute BYTES per pixel
		tga.imageSize		= (tga.bytesPerPixel * tga.Width * tga.Height);		// Compute amout of memory needed to store image
		texture->imageData	= (GLubyte *)malloc(tga.imageSize);					// Allocate that much memory
		
		if(texture->imageData == NULL)											// If it wasnt allocated correctly..
		{
			printf("Could not allocate memory for image\n");	// Display Error
			fclose(fTGA);														// Close file
			return false;														// Return failed
		}
		
		GLuint pixelcount	= tga.Height * tga.Width;							// Nuber of pixels in the image
		GLuint currentpixel	= 0;												// Current pixel being read
		GLuint currentbyte	= 0;												// Current byte 
		GLubyte * colorbuffer = (GLubyte *)malloc(tga.bytesPerPixel);			// Storage for 1 pixel
		
		while(currentpixel < pixelcount)										// Loop while there are still pixels left
		{
			GLubyte chunkheader = 0;											// Storage for "chunk" header
			
			if(fread(&chunkheader, sizeof(GLubyte), 1, fTGA) == 0)				// Read in the 1 byte header
			{
				printf("ERROR: Could not read RLE header\n");	// Display Error
				if(fTGA != NULL)												// If file is open
				{
					fclose(fTGA);												// Close file
				}
				if(texture->imageData != NULL)									// If there is stored image data
				{
					free(texture->imageData);									// Delete image data
				}
				return false;													// Return failed
			}
			
			if(chunkheader < 128)												// If the ehader is < 128, it means the that is the number of RAW color packets minus 1
			{																	// that follow the header
				chunkheader++;													// add 1 to get number of following color values
				for(short counter = 0; counter < chunkheader; counter++)		// Read RAW color values
				{
					if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel) // Try to read 1 pixel
					{
						printf("ERROR: Could not read image data\n");		// IF we cant, display an error
						
						if(fTGA != NULL)													// See if file is open
						{
							fclose(fTGA);													// If so, close file
						}
						
						if(colorbuffer != NULL)												// See if colorbuffer has data in it
						{
							free(colorbuffer);												// If so, delete it
						}
						
						if(texture->imageData != NULL)										// See if there is stored Image data
						{
							free(texture->imageData);										// If so, delete it too
						}
						
						return false;														// Return failed
					}
					// write to memory
					texture->imageData[currentbyte		] = colorbuffer[2];				    // Flip R and B vcolor values around in the process 
					texture->imageData[currentbyte + 1	] = colorbuffer[1];
					texture->imageData[currentbyte + 2	] = colorbuffer[0];
					
					if(tga.bytesPerPixel == 4)												// if its a 32 bpp image
					{
						texture->imageData[currentbyte + 3] = colorbuffer[3];				// copy the 4th byte
					}
					
					currentbyte += tga.bytesPerPixel;										// Increase thecurrent byte by the number of bytes per pixel
					currentpixel++;															// Increase current pixel by 1
					
					if(currentpixel > pixelcount)											// Make sure we havent read too many pixels
					{
						printf("ERROR: Too many pixels read\n");			// if there is too many... Display an error!
						
						if(fTGA != NULL)													// If there is a file open
						{
							fclose(fTGA);													// Close file
						}	
						
						if(colorbuffer != NULL)												// If there is data in colorbuffer
						{
							free(colorbuffer);												// Delete it
						}
						
						if(texture->imageData != NULL)										// If there is Image data
						{
							free(texture->imageData);										// delete it
						}
						
						return false;														// Return failed
					}
				}
			}
			else																			// chunkheader > 128 RLE data, next color reapeated chunkheader - 127 times
			{
				chunkheader -= 127;															// Subteact 127 to get rid of the ID bit
				if(fread(colorbuffer, 1, tga.bytesPerPixel, fTGA) != tga.bytesPerPixel)		// Attempt to read following color values
				{	
					printf("ERROR: Could not read from file\n");			// If attempt fails.. Display error (again)
					
					if(fTGA != NULL)														// If thereis a file open
					{
						fclose(fTGA);														// Close it
					}
					
					if(colorbuffer != NULL)													// If there is data in the colorbuffer
					{
						free(colorbuffer);													// delete it
					}
					
					if(texture->imageData != NULL)											// If thereis image data
					{
						free(texture->imageData);											// delete it
					}
					
					return false;															// return failed
				}
				
				for(short counter = 0; counter < chunkheader; counter++)					// copy the color into the image data as many times as dictated 
				{																			// by the header
					texture->imageData[currentbyte		] = colorbuffer[2];					// switch R and B bytes areound while copying
					texture->imageData[currentbyte + 1	] = colorbuffer[1];
					texture->imageData[currentbyte + 2	] = colorbuffer[0];
					
					if(tga.bytesPerPixel == 4)												// If TGA images is 32 bpp
					{
						texture->imageData[currentbyte + 3] = colorbuffer[3];				// Copy 4th byte
					}
					
					currentbyte += tga.bytesPerPixel;										// Increase current byte by the number of bytes per pixel
					currentpixel++;															// Increase pixel count by 1
					
					if(currentpixel > pixelcount)											// Make sure we havent written too many pixels
					{
						printf("ERROR: Too many pixels read\n");			// if there is too many... Display an error!
						
						if(fTGA != NULL)													// If there is a file open
						{
							fclose(fTGA);													// Close file
						}	
						
						if(colorbuffer != NULL)												// If there is data in colorbuffer
						{
							free(colorbuffer);												// Delete it
						}
						
						if(texture->imageData != NULL)										// If there is Image data
						{
							free(texture->imageData);										// delete it
						}
						
						return false;														// Return failed
					}
				}
			}
		}
		
		fclose(fTGA);																		// Close the file
		return true;																		// return success
	}};
