//
// TGA lib for cocos2d-iphone
//
// sources from: http://www.lighthouse3d.com/opengl/terrain/index.php3?tgasource
//
// TGA RLE compression support by Ernesto Corvi

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

#import "TGAlib.h"

void tgaLoadRLEImageData(FILE *file, tImageTGA *info);
void tgaFlipImage( tImageTGA *info );

// load the image header fields. We only keep those that matter!
void tgaLoadHeader(FILE *file, tImageTGA *info) {
	unsigned char cGarbage;
	short int iGarbage;

	fread(&cGarbage, sizeof(unsigned char), 1, file);
	fread(&cGarbage, sizeof(unsigned char), 1, file);

	// type must be 2 or 3
	fread(&info->type, sizeof(unsigned char), 1, file);

	fread(&iGarbage, sizeof(short int), 1, file);
	fread(&iGarbage, sizeof(short int), 1, file);
	fread(&cGarbage, sizeof(unsigned char), 1, file);
	fread(&iGarbage, sizeof(short int), 1, file);
	fread(&iGarbage, sizeof(short int), 1, file);

	fread(&info->width, sizeof(short int), 1, file);
	fread(&info->height, sizeof(short int), 1, file);
	fread(&info->pixelDepth, sizeof(unsigned char), 1, file);

	fread(&cGarbage, sizeof(unsigned char), 1, file);

	info->flipped = 0;
	if ( cGarbage & 0x20 ) info->flipped = 1;
}

// loads the image pixels. You shouldn't call this function directly
void tgaLoadImageData(FILE *file, tImageTGA *info) {

	int mode,total,i;
	unsigned char aux;

	// mode equal the number of components for each pixel
	mode = info->pixelDepth / 8;
	// total is the number of unsigned chars we'll have to read
	total = info->height * info->width * mode;

	fread(info->imageData,sizeof(unsigned char),total,file);

	// mode=3 or 4 implies that the image is RGB(A). However TGA
	// stores it as BGR(A) so we'll have to swap R and B.
	if (mode >= 3)
		for (i=0; i < total; i+= mode) {
			aux = info->imageData[i];
			info->imageData[i] = info->imageData[i+2];
			info->imageData[i+2] = aux;
		}
}

// loads the RLE encoded image pixels. You shouldn't call this function directly
void tgaLoadRLEImageData(FILE *file, tImageTGA *info)
{
	unsigned int mode,total,i, index = 0;
	unsigned char aux[4], runlength = 0;
	unsigned int skip = 0, flag = 0;

	// mode equal the number of components for each pixel
	mode = info->pixelDepth / 8;
	// total is the number of unsigned chars we'll have to read
	total = info->height * info->width;

	for( i = 0; i < total; i++ )
	{
		// if we have a run length pending, run it
		if ( runlength != 0 )
		{
			// we do, update the run length count
			runlength--;
			skip = (flag != 0);
		}
		else
		{
			// otherwise, read in the run length token
			if ( fread(&runlength,sizeof(unsigned char),1,file) != 1 )
				return;

			// see if it's a RLE encoded sequence
			flag = runlength & 0x80;
			if ( flag ) runlength -= 128;
			skip = 0;
		}

		// do we need to skip reading this pixel?
		if ( !skip )
		{
			// no, read in the pixel data
			if ( fread(aux,sizeof(unsigned char),mode,file) != mode )
				return;

			// mode=3 or 4 implies that the image is RGB(A). However TGA
			// stores it as BGR(A) so we'll have to swap R and B.
			if ( mode >= 3 )
			{
				unsigned char tmp;

				tmp = aux[0];
				aux[0] = aux[2];
				aux[2] = tmp;
			}
		}

		// add the pixel to our image
		memcpy(&info->imageData[index], aux, mode);
		index += mode;
	}
}

void tgaFlipImage( tImageTGA *info )
{
	// mode equal the number of components for each pixel
	int mode = info->pixelDepth / 8;
	int rowbytes = info->width*mode;
	unsigned char *row = (unsigned char *)malloc(rowbytes);
	int y;

	if (row == NULL) return;

	for( y = 0; y < (info->height/2); y++ )
	{
		memcpy(row, &info->imageData[y*rowbytes],rowbytes);
		memcpy(&info->imageData[y*rowbytes], &info->imageData[(info->height-(y+1))*rowbytes], rowbytes);
		memcpy(&info->imageData[(info->height-(y+1))*rowbytes], row, rowbytes);
	}

	free(row);
	info->flipped = 0;
}

// this is the function to call when we want to load an image
tImageTGA * tgaLoad(const char *filename) {

	FILE *file;
	tImageTGA *info;
	int mode,total;

	// allocate memory for the info struct and check!
	info = (tImageTGA *)malloc(sizeof(tImageTGA));
	if (info == NULL)
		return(NULL);


	// open the file for reading (binary mode)
	file = fopen(filename, "rb");
	if (file == NULL) {
		info->status = TGA_ERROR_FILE_OPEN;
		return(info);
	}

	// load the header
	tgaLoadHeader(file,info);

	// check for errors when loading the header
	if (ferror(file)) {
		info->status = TGA_ERROR_READING_FILE;
		fclose(file);
		return(info);
	}

	// check if the image is color indexed
	if (info->type == 1) {
		info->status = TGA_ERROR_INDEXED_COLOR;
		fclose(file);
		return(info);
	}
	// check for other types (compressed images)
	if ((info->type != 2) && (info->type !=3) && (info->type !=10) ) {
		info->status = TGA_ERROR_COMPRESSED_FILE;
		fclose(file);
		return(info);
	}

	// mode equals the number of image components
	mode = info->pixelDepth / 8;
	// total is the number of unsigned chars to read
	total = info->height * info->width * mode;
	// allocate memory for image pixels
	info->imageData = (unsigned char *)malloc(sizeof(unsigned char) *
											  total);

	// check to make sure we have the memory required
	if (info->imageData == NULL) {
		info->status = TGA_ERROR_MEMORY;
		fclose(file);
		return(info);
	}
	// finally load the image pixels
	if ( info->type == 10 )
		tgaLoadRLEImageData(file, info);
	else
		tgaLoadImageData(file,info);

	// check for errors when reading the pixels
	if (ferror(file)) {
		info->status = TGA_ERROR_READING_FILE;
		fclose(file);
		return(info);
	}
	fclose(file);
	info->status = TGA_OK;

	if ( info->flipped )
	{
		tgaFlipImage( info );
		if ( info->flipped ) info->status = TGA_ERROR_MEMORY;
	}

	return(info);
}

// converts RGB to greyscale
void tgaRGBtogreyscale(tImageTGA *info) {

	int mode,i,j;

	unsigned char *newImageData;

	// if the image is already greyscale do nothing
	if (info->pixelDepth == 8)
		return;

	// compute the number of actual components
	mode = info->pixelDepth / 8;

	// allocate an array for the new image data
	newImageData = (unsigned char *)malloc(sizeof(unsigned char) *
										   info->height * info->width);
	if (newImageData == NULL) {
		return;
	}

	// convert pixels: greyscale = o.30 * R + 0.59 * G + 0.11 * B
	for (i = 0,j = 0; j < info->width * info->height; i +=mode, j++)
		newImageData[j] =
		(unsigned char)(0.30 * info->imageData[i] +
						0.59 * info->imageData[i+1] +
						0.11 * info->imageData[i+2]);


	//free old image data
	free(info->imageData);

	// reassign pixelDepth and type according to the new image type
	info->pixelDepth = 8;
	info->type = 3;
	// reassing imageData to the new array.
	info->imageData = newImageData;
}

// releases the memory used for the image
void tgaDestroy(tImageTGA *info) {

	if (info != NULL) {
		if (info->imageData != NULL)
			free(info->imageData);
		free(info);
	}
}
