/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2008.
     ssss    gggg x   x         Steven Goodwin. All Rights Reserved.
    
This version has been released under the GNU Public License v2.0.

Please see the license files for details.

File: src/utils/imagelib/tgaparser.cpp

*/

#include "sgx/core/core.hpp"
#include "sgx/filesystem/filesystem.hpp"
#include "domains/graphics/surface/surface.hpp"
#include "utils/imagelib/imagelib.hpp"

//
// Generic parser code
//
CTGAParser *CTGAParser::m_pSingleton = NULL;

void
CTGAParser::create()
{
	if (!m_pSingleton) {
		m_pSingleton = new CTGAParser();
		if (m_pSingleton) {
			CImageLoader::get()->registerObject(m_pSingleton);
		}
	}
}

//
// TGA-specific structures
//
typedef struct {
	tBYTE	identsize;
	tBYTE	colourmaptype;
	tBYTE	imagetype;

	tBYTE	colourmapstart;
	tINT16	colourmaplength;
	tBYTE	colourmapbits;

	tINT16	xstart, ystart;
	tINT16	width, height;

	tBYTE	bits;
	tBYTE	descriptor;
} sgxTGAHeader;


// BMP-specific source code
//
tBOOL CTGAParser::isRecognised(CSGXFile &file) const
{
	char id[3];

	// The TGA header doesn't really describe the file, but we can
	// determine whether the format is one we recognize.
	file.read((tBYTE *)&id, sizeof(id));
	if (id[1] == 0 && (id[2] == 2 || id[2] == 10)) {
		return TRUE;
	}
	
	return FALSE;
}

tBOOL CTGAParser::open(CSGXFile &file, CImageData &properties) const
{
	sgxTGAHeader fileheader;

	properties.release();

	// Special code to load the header, which is 18 bytes
	file.read((tBYTE *)&fileheader, sizeof(sgxTGAHeader));

	tUINT32 attributeBits = (fileheader.descriptor & 0x0f);
	tUINT32 screenOriginAtBottom = (fileheader.descriptor & 0x20) ? 0 : 1;
	tUINT32 interleaving = (fileheader.descriptor & 0xc0) >> 6;	//0=non-interleaved.  1=two-way (even/odd) interleaving,2=four way interleaving. 

	if (fileheader.bits == 32) {
		properties.create(fileheader.width, fileheader.height, CImageData::eRGBA);
	} else if (fileheader.bits == 24) {
		properties.create(fileheader.width, fileheader.height, CImageData::eRGB);
	} else {
		return FALSE;
	}

	if (fileheader.imagetype & 8) {	// compressed
		// Compressed images are loaded into a separate buffer
		// and then decompressed into the bitmap buffer.

		// BUGWARN: This doesn't consider the span

		tUINT32 fileSize = file.getFileSize();
		tBYTE *pFileData = new tBYTE[fileSize];
		tUINT32 sz = file.read(pFileData, fileSize);	// this will over-read a few bytes

		tUINT32 pixelsLeft = fileheader.width * fileheader.height;
		tUINT32 total = 0;

		tBYTE *p = pFileData;
		tBYTE *ptr = properties.getBitmap();

		if (screenOriginAtBottom) {
			ptr += fileheader.width * (fileheader.height-1);
		}

		while(total < pixelsLeft) {
			tBYTE code = *p++;
			tINT32 count = (code & 0x7f) + 1;
			total += count;
			if (fileheader.bits == 32) {
				if (code & 0x80) {
					// dup
					tBYTE b = *p++;
					tBYTE g = *p++;
					tBYTE r = *p++;
					tBYTE a = *p++;
					while(--count >= 0) {
						*ptr++ = r;
						*ptr++ = g;
						*ptr++ = b;
						*ptr++ = a;
					}
				} else {
					// copy
					while(--count >= 0) {
						*ptr++ = *(p+2);
						*ptr++ = *(p+1);
						*ptr++ = *(p+0);
						*ptr++ = *(p+3);
						p += 4;
					}
				}
			} else {	// 24 bits
				if (code & 0x80) {
					// dup
					tBYTE b = *p++;
					tBYTE g = *p++;
					tBYTE r = *p++;
					while(--count >= 0) {
						*ptr++ = r;
						*ptr++ = g;
						*ptr++ = b;
					}
				} else {
					// copy
					while(--count >= 0) {
						*ptr++ = *(p+2);
						*ptr++ = *(p+1);
						*ptr++ = *(p+0);
						p += 3;
					}
				}
			}
		}

		delete[] pFileData;
	} else {	// uncompressed
		tBYTE *ptr = properties.getBitmap();

		if (screenOriginAtBottom) {
			ptr += properties.getSpan() * properties.getHeight();
		}

		for(tINT16 y=0;y<fileheader.height;++y) {
			
			if (screenOriginAtBottom) {
				ptr -= properties.getSpan();
			}
		
			file.read(ptr, properties.bpl());

			if (fileheader.bits == 32) {
				for(tINT16 x=0;x<fileheader.width;++x) {
					// Swap RGB: on disc it's BGRA, on memory it's RGBA
					tBYTE t = *(ptr+0);
					*(ptr+0) = *(ptr+2);
					*(ptr+2) = t;
		
					// This let's us read RGB and RGBA surfaces. We could also have
					// used .bpp()
					ptr += fileheader.bits/8;
				}
			} else {
				for(tINT16 x=0;x<fileheader.width;++x) {
					// Swap RGB
					tBYTE t = *(ptr+0);
					*(ptr+0) = *(ptr+2);
					*(ptr+2) = t;
					
					// This let's us read RGB and RGBA surfaces. We could also have
					// used .bpp()
					ptr += fileheader.bits/8;
				}
			}

			ptr -= properties.bpl();
			if (!screenOriginAtBottom) {
				ptr += properties.getSpan();
			}
		}	
	}

	return TRUE;
}

tBOOL CTGAParser::close(CImageData &properties) const
{
	SGX_UNUSED(properties);
	return TRUE;
}
