#ifdef JA2_PRECOMPILED_HEADERS
	#include "JA2 SGP ALL.H"
#elif defined( WIZ8_PRECOMPILED_HEADERS )
	#include "WIZ8 SGP ALL.H"
#else
	#include <stdio.h>
	#include "pcx.h"
	#include "memman.h"
	#include "fileman.h"
#endif

// Local typedefs

#define PCX_NORMAL		 1
#define PCX_RLE			2
#define PCX_256COLOR		4
#define PCX_TRANSPARENT	8
#define PCX_CLIPPED		16
#define PCX_REALIZEPALETTE 32
#define PCX_X_CLIPPING	 64
#define PCX_Y_CLIPPING	 128
#define PCX_NOTLOADED		256

#define PCX_ERROROPENING	1
#define PCX_INVALIDFORMAT	2
#define PCX_INVALIDLEN	 4
#define PCX_OUTOFMEMORY	8


BOOLEAN SetPcxPalette( PcxObject *pCurrentPcxObject, HIMAGE hImage );
BOOLEAN BlitPcxToBuffer( PcxObject *pCurrentPcxObject, UINT8 *pBuffer, UINT16 usBufferWidth, UINT16 usBufferHeight, UINT16 usX, UINT16 usY, BOOLEAN fTransp);
PcxObject *LoadPcx(STR8 pFilename);


BOOLEAN LoadPCXFileToImage( HIMAGE hImage, UINT16 fContents )
{
	PcxObject *pPcxObject;

	// First Load a PCX Image
	pPcxObject = LoadPcx( hImage->ImageFile );

	if ( pPcxObject == NULL )
	{
		return( FALSE );
	}

	// Set some header information
	hImage->usWidth = pPcxObject->usWidth;
	hImage->usHeight = pPcxObject->usHeight;
	hImage->ubBitDepth = 8;
	hImage->fFlags = hImage->fFlags | fContents;

	// Read and allocate bitmap block if requested
	if ( fContents & IMAGE_BITMAPDATA )
	{
		// Allocate memory for buffer
		hImage->p8BPPData = (UINT8 *) MemAlloc( hImage->usWidth * hImage->usHeight );

		if ( !BlitPcxToBuffer( pPcxObject, hImage->p8BPPData, hImage->usWidth, hImage->usHeight, 0, 0, FALSE ) )
		{
			MemFree( hImage->p8BPPData );
			return( FALSE );
		}
	}

	if ( fContents & IMAGE_PALETTE )
	{
		SetPcxPalette( pPcxObject, hImage );

		// Create 16 BPP palette if flags and BPP justify
		hImage->pui16BPPPalette = Create16BPPPalette( hImage->pPalette );

	}

	// Free and remove pcx object
	MemFree( pPcxObject->pPcxBuffer );
	MemFree( pPcxObject );

	return( TRUE );
}


PcxObject *LoadPcx(STR8 pFilename)
{
	PcxHeader	Header;
	PcxObject *pCurrentPcxObject;
	HWFILE	 hFileHandle;
	UINT32	 uiFileSize;
	UINT8	 *pPcxBuffer;

	// Open and read in the file
	if ((hFileHandle = FileOpen(pFilename, FILE_ACCESS_READ | FILE_OPEN_EXISTING, FALSE)) == 0)
	{ // damn we failed to open the file
	return NULL;
	}

	uiFileSize = FileGetSize(hFileHandle);
	if (uiFileSize == 0)
	{ // we failed to size up the file
	return NULL;
	}

	// Create enw pCX object
	pCurrentPcxObject = (PcxObject *) MemAlloc( sizeof( PcxObject ) );

	if ( pCurrentPcxObject == NULL )
	{
		return( NULL );
	}

	pCurrentPcxObject->pPcxBuffer = (UINT8 *) MemAlloc( uiFileSize - (sizeof(PcxHeader) + 768) );

	if ( pCurrentPcxObject->pPcxBuffer == NULL )
	{
		return( NULL );
	}

	// Ok we now have a file handle, so let's read in the data
	FileRead(hFileHandle, &Header, sizeof(PcxHeader), NULL);
	if ((Header.ubManufacturer != 10)||(Header.ubEncoding != 1))
	{ // We have an invalid pcx format
	// Delete the object
	MemFree( pCurrentPcxObject->pPcxBuffer );
		MemFree( pCurrentPcxObject );
	return( NULL );
	}

	if (Header.ubBitsPerPixel == 8)
	{
	pCurrentPcxObject->usPcxFlags = PCX_256COLOR;
	} else
	{
	pCurrentPcxObject->usPcxFlags	= 0;
	}

	pCurrentPcxObject->usWidth		= 1 + (Header.usRight - Header.usLeft);
	pCurrentPcxObject->usHeight	 = 1 + (Header.usBottom - Header.usTop);
	pCurrentPcxObject->uiBufferSize = uiFileSize - 768 - sizeof(PcxHeader);

	// We are ready to read in the pcx buffer data. Therefore we must lock the buffer
	pPcxBuffer = pCurrentPcxObject->pPcxBuffer;

	FileRead(hFileHandle, pPcxBuffer, pCurrentPcxObject->uiBufferSize, NULL);

	// Read in the palette
	FileRead(hFileHandle, &(pCurrentPcxObject->ubPalette[0]), 768, NULL);

	// Close file
	FileClose( hFileHandle );

	return pCurrentPcxObject;
}

BOOLEAN BlitPcxToBuffer( PcxObject *pCurrentPcxObject, UINT8 *pBuffer, UINT16 usBufferWidth, UINT16 usBufferHeight, UINT16 usX, UINT16 usY, BOOLEAN fTransp)
{
	UINT8	 *pPcxBuffer;
	UINT8		ubRepCount;
	UINT16	 usMaxX, usMaxY;
	UINT32	 uiImageSize;
	UINT8		ubCurrentByte = 0;
	UINT8		ubMode;
	UINT16	 usCurrentX, usCurrentY;
	UINT32	 uiOffset, uiIndex;
	UINT32	 uiNextLineOffset, uiStartOffset, uiCurrentOffset;

	pPcxBuffer = pCurrentPcxObject->pPcxBuffer;

	if (((pCurrentPcxObject->usWidth + usX) == usBufferWidth)&&((pCurrentPcxObject->usHeight + usY)== usBufferHeight))
	{ // Pre-compute PCX blitting aspects.
	uiImageSize = usBufferWidth * usBufferHeight;
	ubMode		= PCX_NORMAL;
	uiOffset	= 0;
	ubRepCount	= 0;

	// Blit Pcx object. Two main cases, one for transparency (0's are skipped and for without transparency.
	if (fTransp == TRUE)
	{
		for (uiIndex = 0; uiIndex < uiImageSize; uiIndex++)
		{
		if (ubMode == PCX_NORMAL)
		{
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (ubCurrentByte > 0x0BF)
			{
			ubRepCount = ubCurrentByte & 0x03F;
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (--ubRepCount > 0)
			{
				ubMode = PCX_RLE;
			}
			}
		}
		else
		{
			if (--ubRepCount == 0)
			{
			ubMode = PCX_NORMAL;
			}
		}
		if (ubCurrentByte != 0)
		{
			*(pBuffer + uiIndex) = ubCurrentByte;
		}
		}
	}
	else
	{
		for (uiIndex = 0; uiIndex < uiImageSize; uiIndex++)
		{
		if (ubMode == PCX_NORMAL)
		{
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (ubCurrentByte > 0x0BF)
			{
			ubRepCount = ubCurrentByte & 0x03F;
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (--ubRepCount > 0)
			{
				ubMode = PCX_RLE;
			}
			}
		}
		else
		{
			if (--ubRepCount == 0)
			{ ubMode = PCX_NORMAL;
			}
		}
		*(pBuffer + uiIndex) = ubCurrentByte;
		}
	}
	} else
	{ // Pre-compute PCX blitting aspects.
	if ((pCurrentPcxObject->usWidth + usX) >= usBufferWidth)
	{
		pCurrentPcxObject->usPcxFlags |= PCX_X_CLIPPING;
		usMaxX = usBufferWidth - 1;
	}
	else
	{
		usMaxX = pCurrentPcxObject->usWidth + usX;
	}

	if ((pCurrentPcxObject->usHeight + usY) >= usBufferHeight)
	{
		pCurrentPcxObject->usPcxFlags |= PCX_Y_CLIPPING;
		uiImageSize = pCurrentPcxObject->usWidth * (usBufferHeight - usY);
		usMaxY = usBufferHeight - 1;
	}
	else
	{ uiImageSize = pCurrentPcxObject->usWidth * pCurrentPcxObject->usHeight;
		usMaxY = pCurrentPcxObject->usHeight + usY;
	}

	ubMode	 = PCX_NORMAL;
	uiOffset	= 0;
	ubRepCount = 0;
	usCurrentX = usX;
	usCurrentY = usY;

	// Blit Pcx object. Two main cases, one for transparency (0's are skipped and for without transparency.
	if (fTransp == TRUE)
	{
		for (uiIndex = 0; uiIndex < uiImageSize; uiIndex++)
		{
		if (ubMode == PCX_NORMAL)
		{
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (ubCurrentByte > 0x0BF)
			{
			ubRepCount = ubCurrentByte & 0x03F;
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (--ubRepCount > 0)
			{
				ubMode = PCX_RLE;
			}
			}
		}
		else
		{
			if (--ubRepCount == 0)
			{ ubMode = PCX_NORMAL;
			}
		}
		if (ubCurrentByte != 0)
		{ *(pBuffer + (usCurrentY*usBufferWidth) + usCurrentX) = ubCurrentByte;
		}
		usCurrentX++;
		if (usCurrentX > usMaxX)
		{
			usCurrentX = usX;
			usCurrentY++;
		}
		}
	} else
	{
		uiStartOffset = (usCurrentY*usBufferWidth) + usCurrentX;
		uiNextLineOffset = uiStartOffset + usBufferWidth;
		uiCurrentOffset = uiStartOffset;

		for (uiIndex = 0; uiIndex < uiImageSize; uiIndex++)
		{

		if (ubMode == PCX_NORMAL)
		{
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (ubCurrentByte > 0x0BF)
			{
			ubRepCount = ubCurrentByte & 0x03F;
			ubCurrentByte = *(pPcxBuffer + uiOffset++);
			if (--ubRepCount > 0)
			{
				ubMode = PCX_RLE;
			}
			}
		}
		else
		{
			if (--ubRepCount == 0)
			{
			ubMode = PCX_NORMAL;
			}
		}

		if (usCurrentX < usMaxX)
		{ // We are within the visible bounds so we write the byte to buffer
			*(pBuffer + uiCurrentOffset) = ubCurrentByte;
			uiCurrentOffset++;
			usCurrentX++;
		}
		else
		{ if ((uiCurrentOffset + 1)< uiNextLineOffset)
			{ // Increment the uiCurrentOffset
			uiCurrentOffset++;
			}
			else
			{ // Go to next line
			usCurrentX = usX;
			usCurrentY++;
			if (usCurrentY > usMaxY)
			{
				break;
			}
			uiStartOffset = (usCurrentY*usBufferWidth) + usCurrentX;
			uiNextLineOffset = uiStartOffset + usBufferWidth;
			uiCurrentOffset = uiStartOffset;
			}
		}
		}
	}
	}

	return( TRUE );
}

BOOLEAN SetPcxPalette( PcxObject *pCurrentPcxObject, HIMAGE hImage )
{
	UINT16 Index;
	UINT8	*pubPalette;

	pubPalette = &(pCurrentPcxObject->ubPalette[0]);

	// Allocate memory for palette
	hImage->pPalette = (SGPPaletteEntry *) MemAlloc( sizeof( SGPPaletteEntry ) * 256 );

	if ( hImage->pPalette == NULL )
	{
		return( FALSE );
	}

	// Initialize the proper palette entries
	for (Index = 0; Index < 256; Index++)
	{
		hImage->pPalette[ Index ].peRed	= *(pubPalette+(Index*3));
	hImage->pPalette[ Index ].peGreen = *(pubPalette+(Index*3)+1);
	hImage->pPalette[ Index ].peBlue	= *(pubPalette+(Index*3)+2);
	hImage->pPalette[ Index ].peFlags = 0;
	}

	return TRUE;
}
