/*
	Twilight Prophecy 3D/Multimedia SDK
	A multi-platform development system for virtual reality and multimedia.

	Copyright (C) 1997-2001 by Twilight 3D Finland Oy Ltd.

	This program is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2 of the License, or
	(at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to the Free Software
	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

	Please read the file LICENSE.TXT for additional details.


	source: 
		BitmapCodec "rgb"

	revision history:
		Sep/22/2000 - Jukka Liimatta - initial revision
		Jan/24/2001 - Jukka Liimatta - renaissance build
*/
#include <prcore/prcore.hpp>
using namespace prcore;



//////////////////////////////////////////////////////
// codec                                           //
////////////////////////////////////////////////////

static int NumEXT()
{
	return 4;
}


static const char* GetEXT(int index)
{
	switch ( index )
	{
		case 0:		return "rgb";
		case 1:		return "rgba";
		case 2:		return "bw";
		case 3:		return "sgi";
		default:	return "";
	}
}


static bool IsDecoder()
{
	return true;
}


static bool IsEncoder()
{
	return false;
}


static bool Decode(Bitmap& target, Stream& stream)
{
	// reset stream
	stream.Seek(0,Stream::START);

	// read header
	uint16 magic = ReadBigEndian<uint16>(stream);
	if ( magic != 474 )
		return false;

	uint8 format = ReadBigEndian<uint8>(stream);		// 0 - raw, 1 - rle
	uint8 bpc = ReadBigEndian<uint8>(stream);			// bytes per pixel channel ( 1,2 )
	uint16 dimension = ReadBigEndian<uint16>(stream);	// number of dimensions ( 1,2,3 )
	uint16 xsize = ReadBigEndian<uint16>(stream);		// width
	uint16 ysize = ReadBigEndian<uint16>(stream);		// height
	uint16 zsize = ReadBigEndian<uint16>(stream);		// number of channels
	ReadBigEndian<uint32>(stream);						// minimum pixel value
	ReadBigEndian<uint32>(stream);						// maximum pixel value
	stream.Seek( 4, Stream::CURRENT );					// ignored
	stream.Seek( 80, Stream::CURRENT );					// image name
	uint32 colormap = ReadBigEndian<uint32>(stream);	// colormap, 0 - normal, 1 - dithered, 2 - screen, 3 - colormap
	stream.Seek( 404, Stream::CURRENT );				// ignored

	if ( bpc != 1 )
		return false;

	if ( colormap != 0 )
		return false;

	// choose pixelformat
	PixelFormat pxf;
	switch ( dimension )
	{
		case 1:
			pxf = PixelFormat(8,0xff,0x00);
			ysize = 1;
			zsize = 1;
			break;

		case 2:
			pxf = PixelFormat(8,0xff,0x00);
			zsize = 1;
			break;

		case 3:
		{
			if ( zsize == 3 )
			{
				pxf = PixelFormat(24,0x0000ff,0x00ff00,0xff0000,0x000000);
				break;
			}

			if ( zsize == 4 )
			{
				pxf = PixelFormat(32,0x000000ff,0x0000ff00,0x00ff0000,0xff000000);
				break;
			}

			if ( zsize < 3 )
				return false;

			if ( zsize > 4 )
				return false;

			break;
		}

		default:
				return false;
	}


	// allocate memory
	int pitch = xsize * pxf.GetBytes();
	char* buffer = new char[ ysize * pitch ];

	// decode RAW
	if ( format == 0 )
	{
		for ( int plane=0; plane<zsize; plane++ )
		{
			for ( int y=0; y<ysize; y++ )
			{
				int scanline = ysize - y - 1;
				char* ptr = buffer + (pitch * scanline) + plane;

				int offset = (y + plane*ysize) * xsize;
				stream.Seek(offset,Stream::START);

				// raw unpack
				for ( int x=0; x<xsize; x++ )
				{
					*ptr = ReadBigEndian<uint8>(stream);
					ptr += zsize;
				}
			}
		}
	}

	// decore RLE
	if ( format == 1 )
	{
		// rle offset table
		int count = ysize * zsize;
		uint32* scanoffset = new uint32[ count ];

		for ( int i=0; i<count; i++ )
			scanoffset[i] = ReadBigEndian<uint32>(stream);

		for ( int plane=0; plane<zsize; plane++ )
		{
			for ( int y=0; y<ysize; y++ )
			{
				int scanline = ysize - y - 1;
				char* ptr = buffer + (pitch * scanline) + plane;

				int offset = scanoffset[ y + plane*ysize ];
				stream.Seek(offset,Stream::START);

				// rle unpack
				while( 1 ) 
				{
					uint8 pixel = ReadBigEndian<uint8>(stream);
					uint8 count = pixel & 0x7f; 

					if ( !count )
						break;

					if( pixel & 0x80 ) 
					{
						while( count-- ) 
						{
							*ptr = ReadBigEndian<uint8>(stream);
							ptr += zsize;
 						}
 					} 
					else 
					{
 						pixel = ReadBigEndian<uint8>(stream);
 						while( count-- )
						{
							*ptr = pixel;
							ptr += zsize;
						}
					}
				}
			}
		}

		delete[] scanoffset;
	}

	// copy image
	target.SetImage( xsize, ysize, pxf, buffer );

	return true;
}


static bool Encode(Stream& target, Surface& surface)
{
	return false;
}


//////////////////////////////////////////////////////
// factory                                         //
////////////////////////////////////////////////////

BitmapCodec CreateCodecRGB()
{
	BitmapCodec codec;
	
	codec.NumEXT = NumEXT;
	codec.GetEXT = GetEXT;
	codec.IsDecoder = IsDecoder;
	codec.IsEncoder = IsEncoder;
	codec.Decode = Decode;
	codec.Encode = Encode;
	
	return codec;
}
