/* Copyright 1990 by Christopher A. Wichura.
   See file GIFMachine.doc for full description of rights.
*/

#include "GIFMachine.h"

extern struct GIFdescriptor gdesc;
EXTERNBITPLANE;

static struct ImageDesc idesc;
extern struct RGB GlobalColourTable[256];
static struct RGB LocalColourTable[256];
extern ULONG ImageNumber;

extern char *AbortMsg;

static UWORD Xpos, Ypos;
static BOOL interleave;

static UBYTE LeaveStep[5]  = {1, 8, 8, 4, 2};
static UBYTE LeaveFirst[5] = {0, 0, 4, 2, 1};

/* some variables used by the decompressor */
static int ReadError;
static UBYTE CodeSize;
static int EOFCode;
static UBYTE ReadMask;
static int CompDataPointer;
static int CompDataCount;
static UBYTE CompData[256];

/* tables used by the decompressor */
static UWORD Prefix[4096];
static UBYTE Suffix[4096];
static UBYTE OutCode[1025];

BOOL DoImage(BPTR fh)
{
	register int index;
	register int colours;
	int Code;

	MyPrintf("...Image #%ld encountered.\n", ImageNumber++);

	if (FRead(fh, (char *)&idesc, 1, 9) != 9) {
		PutStr("......Error reading image descriptor.\n");
		return TRUE;
	}

	FlipWord(&idesc.id_Left);
	FlipWord(&idesc.id_Top);
	FlipWord(&idesc.id_Width);
	FlipWord(&idesc.id_Height);

	interleave = idesc.id_Info & 1L << 6;
	if (interleave)
		interleave = 1;

	MyPrintf("......Xpos from %ld to %ld, Ypos from %ld to %ld, %sinterlaced.\n",
		idesc.id_Left, idesc.id_Left + idesc.id_Width - 1,
		idesc.id_Top, idesc.id_Top + idesc.id_Height - 1,
		interleave ? "" : "not ");

	if (idesc.id_Info & 1L << 7) {
		colours = 1L << ((idesc.id_Info & 7) + 1);
		MyPrintf("......Local colour map contains %ld entries.\n", colours);

		for (index = 0; index < colours; index++) {
			if (FRead(fh, &LocalColourTable[index], 1, 3) != 3) {
				MyPrintf("......Error reading local colour #%ld.\n",
					index);
				return TRUE;
			}
		}
	} else {
		colours = 1L << ((gdesc.gd_ColInfo & 7) + 1);
		CopyMem((char *)GlobalColourTable, (char *)LocalColourTable,
			sizeof(LocalColourTable));
	}

	Xpos = Ypos = 0;

	/* now we are ready to read the image in so do it! */

	{
		int MaxCode, ClearCode, CurCode,
		    OldCode, InCode, FreeCode;
		int OutCount;
		int FirstFree;
		UBYTE InitCodeSize, FinChar, BitMask;

		MyPrintf("......Decompressing line number %5ld", Ypos);

		/* get the codesize and do general setup for decompression */
		if ((CodeSize = FGetC(fh)) == -1) {
			PutStr("\n......I/O Error during decompression.\n");
			return TRUE;
		}

		ClearCode = 1L << CodeSize;
		EOFCode = ClearCode + 1;
		FreeCode = FirstFree = ClearCode + 2;

		CodeSize++;	/* per GIF spec */
		InitCodeSize = CodeSize;
		MaxCode = 1L << CodeSize;
		ReadError = ReadMask = OutCount = 0;
		CompDataPointer = CompDataCount = 0;

		BitMask = colours - 1;

		Code = ReadCode(fh);
		while (Code != EOFCode) {
			if (ReadError)
				return TRUE;

			if (SetSignal(0L, SIGBREAKF_CTRL_C) & SIGBREAKF_CTRL_C) {
				MyPrintf("\n%s", AbortMsg);
				MyExit(ABORTEXITVAL);
			}

			if (Code == ClearCode) {
				CodeSize = InitCodeSize;
				MaxCode = 1L << CodeSize;
				FreeCode = FirstFree;
				FinChar = CurCode = OldCode = Code = ReadCode(fh);
				AddPixel(FinChar);
			} else {
				CurCode = InCode = Code;

				if (CurCode >= FreeCode) {
					CurCode = OldCode;
					OutCode[OutCount++] = FinChar;
				}

				while (CurCode > BitMask) {
					if (OutCount > 1024) {
						PutStr("\n......Corrupt GIF file (OutCount)\n");
						return TRUE;
					}

					OutCode[OutCount++] = Suffix[CurCode];
					CurCode = Prefix[CurCode];
				}

				FinChar = CurCode;
				AddPixel(FinChar);

				for (index = OutCount - 1; index >= 0; index--)
					AddPixel(OutCode[index]);
				OutCount = 0;

				Prefix[FreeCode] = OldCode;
				Suffix[FreeCode] = FinChar;
				OldCode = InCode;

				if (++FreeCode >= MaxCode) {
					if (CodeSize < 12) {
						CodeSize++;
						MaxCode <<= 1;
					}
				}
			}

			Code = ReadCode(fh);
		}
	}

	if ((Code = FGetC(fh)) == -1)
		return TRUE;

	/* done decompressing.  Erase decompressing message and exit */
	PutStr("\x9B" "22D\x9BKed.\n");

	if (Code != 0) {
		PutStr("......Warning:  Unaligned packet.\n");
		UnGetC(fh, Code);
	}

	return FALSE;
}

static UBYTE ByteBuf;

int ReadCode(BPTR fh)
{
	register int temp;
	register int DstMasked;
	register int DstMask;
	register LONG size;

	temp = 0;
	DstMasked = 1L << CodeSize;
	for (DstMask = 1; DstMask != DstMasked; DstMask <<= 1) {
		if (!ReadMask) {
			if (CompDataPointer == CompDataCount) {
				if ((size = FGetC(fh)) == -1) {
					PutStr("\n......I/O Error during decompression.\n");
					ReadError = 1;
					return EOFCode;
				}

				if (FRead(fh, (char *)CompData, 1, size) != size) {
					PutStr("\n......I/O Error during decompression.\n");
					ReadError = 1;
					return EOFCode;
				}

				CompDataCount = size;
				CompDataPointer = 0;
			}

			ReadMask = 1;
			ByteBuf = CompData[CompDataPointer++];
		}

		if (ByteBuf & ReadMask)
			temp |= DstMask;

		ReadMask <<= 1;
	}

	return temp;
}

void AddPixel(UBYTE index)
{
	register UWORD XStore;
	register UWORD YStore;

	XStore = Xpos + idesc.id_Left;
	YStore = Ypos + idesc.id_Top;

	BitPlane[YStore][XStore] = LocalColourTable[index];

	if (++Xpos == idesc.id_Width) {
		Xpos = 0;
		Ypos += LeaveStep[interleave];
		if (Ypos >= idesc.id_Height)
			Ypos = LeaveFirst[++interleave];

		MyPrintf("\x9B" "5D%5ld", Ypos + idesc.id_Top);
	}
}
