/* -----------------------------------------------------------------------------
	GSFramework
	Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
------------------------------------------------------------------------------*/


		#include "picture/pict.h"
		#include "picture/pict_io.h"
		#include "log/log.h"

		using namespace GS;

		#define		IMGBMP_BI_RGB		0
		#define		IMGBMP_BI_RLE8		1
		#define		IMGBMP_BI_RLE4		2


//-----------------------------------------------------------------------------
bool				PictureIO::BmpLoad(Picture &picture, IO::Handle &handle)
{
	handle.Rewind();
	size_t size = handle.GetSize();
	if	(size < 10)
		return false;
													
	// Magic number
	if	((handle.Read <char> () != 'B') || (handle.Read <char> () != 'M'))
		return false;

	handle.Seek(8);
	uint OffBits = handle.Read <uint> (), width, height;
	ushort bpp;

	if	(handle.Read <uint> () == 40)	// we met a BITMAPCOREHEADER
	{
		width = handle.Read <uint> ();
		height = handle.Read <uint> ();
	}
	else								// we met a BITMAPINFOHEADER
	{
		width = handle.Read <ushort> ();
		height = handle.Read <ushort> ();
	}
	handle.Seek(2);
	bpp = handle.Read <ushort> ();

	if	((bpp != 8) && (bpp != 24) && (bpp != 32))
		__ERR__(__LOG_E__ << "BMP format unhandled (neither RGB8, RGB24 or BGR8).\n", false)

	// Decode bitmap data.
	if	(!picture.AllocAs(width, height, PixelFormat::BGR8))
		__ERR__(__LOG_E__ << "Failed to allocate output buffer.\n", false)

	uint *rgb = (uint *)picture.GetData();
	Array <uchar> bmp(size - OffBits);
	if	(!bmp)
		__ERR__(__LOG_E__ << "Failed to allocate input framebuffer.\n", false)

	uchar *_bmp = &bmp[0];
	handle.Seek(OffBits, GS::IO::Base::SeekStart);
	handle.Read((void *)_bmp, OffBits);

	rgb += (picture.GetHeight() - 1) * picture.GetWidth();

	// Load palette
	Array <uint> palette;

	if	(bpp < 16)
	{
		__LOG__ << "Picture is palletized.\n";
		if	(palette.Allocate(1 << bpp))
		{
			char cbuf[4];
			handle.Seek(54, GS::IO::Base::SeekStart);
			for	(int n = 0; n < (1 << bpp); ++n)
			{
				handle.Read(cbuf, 4);
				palette[n] = (cbuf[3] << 24) + (cbuf[2] << 16) + (cbuf[1] << 8) + cbuf[0];
			}
		}
		else
			__ERR__(__LOG_E__ << "Failed to allocate palette.\n", false)
	}

	// Even width
	switch (bpp)
	{
		case 8:
		{
			// 32 bit 0 padding.
			uint pad = 0;
			if	(picture.GetWidth() & 3)
				pad = 4 - (picture.GetWidth() & 3);

			for	(uint c2 = 0; c2 < picture.GetHeight(); c2++)
			{
				for (uint c = 0; c < picture.GetWidth(); c++)
					rgb[c] = palette[*_bmp++];
				_bmp += pad;
				rgb -= picture.GetWidth();
			}
		}
		break;		

		case 16:
			break;

		case 24:
			for	(uint c2 = 0; c2 < picture.GetHeight(); c2++)
			{
				for (uint c = 0; c < picture.GetWidth(); c++)
				{
					rgb[c] = (_bmp[2] << 16) + (_bmp[1] << 8) + _bmp[0];
					_bmp += 3;
				}
				uint	dt = 3 * picture.GetWidth();
				if	(dt & 3)
					_bmp += (4 - (dt & 3));
				rgb -= picture.GetWidth();
			}
			break;

		case 32:
			for	(uint c2 = 0; c2 < picture.GetHeight(); c2++)
			{
				for (uint c = 0; c < picture.GetWidth(); c++)
				{
					rgb[c] = (_bmp[2] << 16) + (_bmp[1] << 8) + _bmp[0];
					_bmp += 4;
				}
				rgb -= picture.GetWidth();
			}
			break;
	}
	return true;
}
//-----------------------------------------------------------------------------
