/** SGX

The   sss    gggg x   x   
     s      g      x x    Engine
      sss   g  gg   x
         s  g   g  x x          Copyright 1995-2009.
     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: sgx/src/utils/imagelib/bmpparser.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
//
CBMPParser *CBMPParser::m_pSingleton = NULL;

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

//
// BMP-specific structures
//
typedef struct {
	tINT16	type;
	tUINT32	size;
	tINT16	res0, res1;
	tUINT32 offset;
} sgxBMPHeader;

typedef struct {
	tUINT32	size;
	tUINT32	width, height;
	tUINT16	planes;
	tUINT16	bpp;
	tUINT32	compression;
	tUINT32	image_size;
	tUINT32	xppm, yppm;		// pixels per meter
	tUINT32	colors;
	tUINT32	important_colors;
} sgxBMPInfoHeader;

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

	file.read((tBYTE *)&id, sizeof(id));
	if (id[0] == 'B' && id[1] == 'M') {
		return TRUE;
	}
	
	return FALSE;
}

tBOOL CBMPParser::open(CSGXFile &file, CImageData &properties) const
{
	sgxBMPHeader bm_fileheader;
	sgxBMPInfoHeader bm_infoheader;
	tBYTE hdr[14];

	properties.release();

	// Special code to load the header, which is 14 bytes
	file.read(&hdr[0], sizeof(hdr));
	// Note: This is not endian safe
	bm_fileheader.type = sgxMakeInt16(&hdr[0]);
	bm_fileheader.size = sgxMakeInt32(&hdr[2]);
	bm_fileheader.res0 = sgxMakeInt16(&hdr[6]);
	bm_fileheader.res1 = sgxMakeInt16(&hdr[8]);
	bm_fileheader.offset = sgxMakeInt16(&hdr[10]);

	// Bring in info header
	file.read((tBYTE *)&bm_infoheader, sizeof(bm_infoheader));

	// Note: Normally this information would come from our tool
	// chain and be pre-swapped. This code assumes a pre-swapped
	// bitmap format.
	file.seek(bm_fileheader.offset, CSGXDeviceBlockState::eSeekFromBegin);


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

	tBYTE *ptr = properties.getBitmap();

	ptr += properties.getSpan() * properties.getHeight();

	for(tUINT32 y=0;y<bm_infoheader.height;++y) {
		
		ptr -= properties.getSpan();
	
		file.read(ptr, properties.bpl());
		for(tUINT32 x=0;x<bm_infoheader.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 += bm_infoheader.bpp/8;
		}

		ptr -= properties.bpl();
	}

	return TRUE;
}

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