/* 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 UWORD GlobalColourTable[256];
static UWORD 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)
{
	UBYTE buf[3];
	register int index;
	register int colours;

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

	if (Read(fh, (char *)&idesc, 9) != 9) {
		Puts("......Error reading image descriptor.");
		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;

	Printf("......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);
		Printf("......Local colour map contains %ld entries.\n", colours);

		for (index = 0; index < colours; index++) {
			if (Read(fh, buf, 3) != 3) {
				Printf("......Error reading local colour #%ld.\n",
					index);
				return TRUE;
			}

			LocalColourTable[index] = AddColour(buf);
		}
	} 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 Code, MaxCode, ClearCode, CurCode,
		    OldCode, InCode, FreeCode;
		int OutCount;
		int FirstFree;
		UBYTE InitCodeSize, FinChar, BitMask;

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

		/* get the codesize and do general setup for decompression */
		if (Read(fh, buf, 1) != 1) {
			Puts("\n......I/O Error during decompression.");
			return TRUE;
		}

		CodeSize = buf[0];
		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 (CheckAbort(NULL)) {
				Printf("\n%s\n", AbortMsg);
				XCEXIT(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) {
						Puts("\n......Corrupt GIF file (OutCount)");
						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 (Read(fh, buf, 1) != 1) {
		return TRUE;
	}

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

	if (buf[0] != 0)
		Puts("......Warning:  Unaligned packet.");

	return FALSE;
}

static UBYTE ByteBuf;

int ReadCode(BPTR fh)
{
	register int temp;
	register int DstMasked;
	register int DstMask;
	UBYTE buf[1];

	temp = 0;
	DstMasked = 1L << CodeSize;
	for (DstMask = 1; DstMask != DstMasked; DstMask <<= 1) {
		if (!ReadMask) {
			if (CompDataPointer == CompDataCount) {
				if (Read(fh, (char *)buf, 1) != 1) {
					Puts("\n......I/O Error during decompression.");
					ReadError = 1;
					return EOFCode;
				}

				if (Read(fh, (char *)CompData, buf[0]) != buf[0]) {
					Puts("\n......I/O Error during decompression.");
					ReadError = 1;
					return EOFCode;
				}

				CompDataCount = buf[0];
				CompDataPointer = 0;
			}

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

		if (ByteBuf & ReadMask)
			temp |= DstMask;

		ReadMask <<= 1;
	}

	return temp;
}

void AddPixel(UBYTE index)
{
	PutValue((UWORD)(Xpos + idesc.id_Left), (UWORD)(Ypos + idesc.id_Top),
		LocalColourTable[index]);

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

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