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

#include <exec/types.h>
#include <exec/lists.h>
#include <exec/nodes.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <exec/memory.h>
#include <libraries/dos.h>
#include <graphics/rastport.h>
#include <graphics/gfxbase.h>
#include <graphics/gfx.h>
#include <intuition/intuition.h>

extern void ReadCodex(void);
extern void getgif(void);

/* Nur fuer shoot */
#include "shoot.h"
extern int numtiles;
extern UBYTE tiles[ TILESMAX ][ DEPTH ][ TILESHEIGHT ][ TILESWIDTH ];

void beak( char * );

int images;
USHORT picinfowidth;
USHORT picinfoheight;
UBYTE ByteBuf;
USHORT pimx, pimy;
APTR pimdata;
USHORT pimstart, pimlines;
USHORT hires;

struct MyMem {
	struct MinNode mm_Node;
	ULONG mm_Size;
};

/* some structures to make parsing the gif files a bit easier */
struct GIFdescriptor {
	UWORD gd_Width;
	UWORD gd_Height;
	UBYTE gd_ColInfo;
	UBYTE gd_BackGround;
	UBYTE gd_PixelAspect;	/* Aspect Ratio = (Pixel Aspect + 15) / 64 */
};

struct ImageDesc {
	UWORD id_Left;
	UWORD id_Top;
	UWORD id_Width;
	UWORD id_Height;
	UBYTE id_Info;
};

struct RGB {
	UBYTE rgb_Red;
	UBYTE rgb_Green;
	UBYTE rgb_Blue;
};

#define GIF_IMAGE      0x2C
#define GIF_EXTENSION  0x21
#define GIF_TERMINATOR 0x3B

#define GIF_COMMENT_EXT 0xFE

#define MAXCOLOURS 4096

/* these next macros are used to get the r, g, and b values of a given
   index */
#define GetRed(a)	(UBYTE)(a >> 8)
#define GetGreen(a)	(UBYTE)((a >> 4) & 0xF)
#define GetBlue(a)	(UBYTE)(a & 0xF)

/* whenever we want to abort we will return this as our error code */
#define ABORTEXITVAL 1

/* this struct is used to hold the linked list of comments found in the GIF
   file so that each can be written as a seperate ANNO chunk. */
struct CommentNode {
	struct MinNode cn_Node;
	char *cn_Comment;
	ULONG cn_CommentLength;
};

/* function prototypes we use */
void	WarnMustUseCLI(void);
void	DoPattern(char *);
void	Convert(UWORD, UWORD, char *);
BOOL	IsDir(char *);
void	FlipWord(UWORD *);
BOOL	DoImage(APTR);
BOOL	DoExtension(APTR);
BOOL	WriteIFF(char *, BOOL);
void	PutValue(UWORD, UWORD, UWORD);
UWORD	GetValue(UWORD, UWORD);
int	ReadCode(APTR);
void	AddPixel(UBYTE);
void	StripBorder(void);
BOOL	BorderCheck(UWORD, UWORD);
void	DoXComp(void);
void	GIFtoSHAM(void);
void	InitDiff(void);
ULONG	RGBdiff(UBYTE, UBYTE, UBYTE, UBYTE, UBYTE, UBYTE);
void	OutDump(int);
void	OutRun(int, int);
void	InitMemory(void);
char *	MyAlloc(ULONG);
void	MyFree(char *);
void	FreeAll(UWORD);
void	MyExit(ULONG);
void	DoXFlip(void);
void	DoYFlip(void);
void	ReduceTo12(void);
void	DitherTo12(void);
UWORD	AddColour(struct RGB *);

/* modules that want to use the Get/PutValue macros should include this
   next macro to externally reference the picture storage array */
#define EXTERNBITPLANE extern struct RGB **BitPlane

/* for use once colour palette has been reduced to 12 bits */
#define GetValue(a, b) *((UWORD *)&BitPlane[b][a])
#define PutValue(a, b, c) *((UWORD *)&BitPlane[b][a]) = c

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

#include <workbench/startup.h>

struct GIFdescriptor gdesc;

struct RGB **BitPlane;
struct RGB GlobalColourTable[256];

ULONG ImageNumber;

/* the current GIF file handle */
struct FileHandle *GIFfh = NULL;

/* here we have some defines relating to our GADS call */
#define ESC "\x9B"
#define GIFMACH ESC "33;42mGIFMachine" ESC "32;40m"

#define ARG_TEMPLATE "GIFfiles/M/A,TO/K,ALL/S,NOBORDER/K/N,XCOMP/S,DITHER/S,XFLIP/s,YFLIP/s,DEEP/S,BUFSIZE/K/N"
#define ARG_FILES  0
#define ARG_TO     1
#define ARG_ALL    2
#define ARG_NOBORD 3
#define ARG_XCOMP  4
#define ARG_DITHER 5
#define ARG_FLIPX  6
#define ARG_FLIPY  7
#define ARG_DEEP   8
#define ARG_BUFSIZ 9
#define ARG_sizeof 10

/* we will make the argument array global so that other modules can get at
   the ARG_TO, ARG_ALL and ARG_XCOMP fields easily */
struct RDArgs *ArgsPtr;
char *ArgArray[ARG_sizeof];
BOOL ArgToIsDir;

/* size of the read buffered i/o space */
static ULONG BufSize = 2048;	/* default size is 2k */

int NoBorderLineThresh = 0;

/* some mem pointers used when we do dithering */
BYTE *CurrentLineErr[3];
BYTE *LastLineErr[3];

/* this flag says if we scaled the image */
BOOL DidXComp;

/* we print this when the user hits the break key */
char *AbortMsg = "*** User Interruption!\n";

void MyExit( result)
ULONG result;
{
	if (GIFfh)
		Close(GIFfh);

	FreeAll( (UWORD) 1 );
	FreeAll( (UWORD) 0 );

	return(result);
}


APTR DosBase;
FILE *out;

void main( argc, argv  )
int argc;
char *argv[];
{
	DosBase = (APTR) OpenLibrary( "dos.library", 0 );

	if (DosBase)
	{
		{
			int f;
			printf("%s %s to %s\n", argv[0], argv[1], argv[2] );
	
			out = fopen( argv[2], "w" );
			beak( argv[1] );

			fclose( out );
		}
		CloseLibrary( DosBase );
	}
	else
	{
		printf("Could not open dos.library\n");
	}
}

/* here we have the routine that gets ready to do the conversion */

void beak( name )
char *name;
{
	register int index;
	/*char *basename;
	char *ptr;*/
	char sig[7];
	int size;
	int error;
	int colours;
	LONG cmdcode;
	unsigned char rid;

	printf("name: %s\n", name );

	if (!(GIFfh = (struct FileHandle *) Open(name, MODE_OLDFILE))) {
		printf("Error #%ld trying to open %s...\n", IoErr(), name);
		goto LeaveConvert;
	}

	sig[6] = NULL;

	if ((Read(GIFfh, sig, 6) != 6) || strncmp("GIF", sig, 3)) {
		printf("%s is not a GIF file...\n", name);
		printf("%s\n", sig );
		goto LeaveConvert;
	}

	if (Read(GIFfh, (char *)&gdesc, 7) != 7) {
		printf("Error reading screen descriptor.\n");
		goto LeaveConvert;
	}

	FlipWord(&gdesc.gd_Width);
	FlipWord(&gdesc.gd_Height);

	printf("Signature = \"%s\", Width = %ld, Height = %ld\n",
		sig, gdesc.gd_Width, gdesc.gd_Height);

	DidXComp = 0;
	colours = 1L << ((gdesc.gd_ColInfo & 7) + 1);

	if (!(gdesc.gd_ColInfo & 1L << 7)) {
		printf("No global colour map supplied, using internal.\n");

		for (index = 0; index < colours; index++) {
			GlobalColourTable[index].rgb_Red   =
			GlobalColourTable[index].rgb_Green =
			GlobalColourTable[index].rgb_Blue  = index;
		}
	} else {
		printf("Global colour map contains %ld entries.\n", colours);

		for (index = 0; index < colours; index++) {
			if (Read(GIFfh, &GlobalColourTable[index], 3) != 3) {
				printf("Error reading global colour #%ld.\n",
					index);
				goto LeaveConvert;
			}
		}
	}

	size = ((gdesc.gd_Width + 7) / 8) + 1;
	size += (size + 127) >> 7;

	size = (gdesc.gd_Width + 1) * sizeof(struct RGB);


	ImageNumber = 1;

	/* at this point we start looking for images, extensions or the gif
	   terminator.  we call the appropriate routine as we find each. */

	for (error = FALSE; error == FALSE;) {
		if ( Read( GIFfh, &rid, 1) == -1) {
			printf("...I/O error reading GIF file.\n");
			goto LeaveConvert;
		}
		cmdcode=rid;

		switch(cmdcode) {
			case GIF_IMAGE:
				error = DoImage( (APTR) GIFfh);
				break;

			case GIF_EXTENSION:
				error = DoExtension( (APTR) GIFfh);
				break;

			case GIF_TERMINATOR:
				break;

			default:
				printf("...Unknown directive #%ld encountered.\n",
					cmdcode);
				error = TRUE;
		}
	}

LeaveConvert:
	if (GIFfh) {
		Close(GIFfh);
		GIFfh = NULL;
	}
}

struct MinList Mem[2];
UWORD CurrentMem;

char *MyAlloc(size)
ULONG size;
{
	register struct MyMem *theMemory;
	register ULONG newsize;

	newsize = size + sizeof(struct MyMem);

	if (!(theMemory = (struct MyMem *)AllocMem(newsize, MEMF_PUBLIC|MEMF_CLEAR)))
		return NULL;

	AddTail((struct List *)&Mem[CurrentMem], (struct Node *)&theMemory->mm_Node);
	theMemory->mm_Size = newsize;

	return (char *)theMemory + sizeof(struct MyMem);
}

void MyFree(MemPtr)
char *MemPtr;
{
	register struct MyMem *theMemory;

	theMemory = (struct MyMem *)(MemPtr - sizeof(struct MyMem));

	Remove((struct Node *)&theMemory->mm_Node);
	FreeMem((char *)theMemory, theMemory->mm_Size);
}

void FreeAll(memlist)
UWORD memlist;
{
	register struct MyMem *theMemory;

	while (theMemory = (struct MyMem *)RemTail((struct List *)&Mem[memlist]))
		FreeMem((char *)theMemory, theMemory->mm_Size);
}

struct MinList CommentList;
static UBYTE buf[256];

BOOL DoExtension(fh)
APTR fh;
{
	register LONG size;
	register LONG cmdcode;
	register char *Comment;
	register char *OldComment;
	register int CommentLength;
	register int OldCommentLength;
	register struct CommentNode *cn;
	unsigned char rid;

	if (Read( fh, &rid, 1) == -1) {
		printf("...I/O error reading extension block function code\n");
		return TRUE;
	}
	cmdcode=rid;

	switch(cmdcode) {
		case GIF_COMMENT_EXT:
			printf("...Comment extension encountered.  Contents will be stored in an ANNO chunk.\n");

			if (!(cn = (struct CommentNode *)MyAlloc(sizeof(struct CommentNode)))) {
				printf("......Out of memory allocating comment block.\n");
				return TRUE;
			}

			Comment = NULL;
			CommentLength = 0;

			for (;;) {
				if ((size = fgetc(fh)) == -1) {
					printf("......I/O Error reading comment block.\n");
					return TRUE;
				}

				if (size) {
					if (fread(fh, buf, 1, size) != size) {
						printf("......I/O Error reading comment block.\n");
						return TRUE;
					}

					OldComment = Comment;
					OldCommentLength = CommentLength;
					CommentLength += size;

					if (!(Comment = MyAlloc(CommentLength))) {
						printf("......Out of memory allocating comment block.\n");
						return TRUE;
					}

					if (OldCommentLength) {
						CopyMem(OldComment, Comment, OldCommentLength);
						MyFree(OldComment);
					}

					CopyMem(buf, &Comment[OldCommentLength], size);
				} else {	/* end of comment so store it */
					cn->cn_Comment = Comment;
					cn->cn_CommentLength = CommentLength;
					AddTail((struct List *)&CommentList, (struct Node *)cn);
					return FALSE;
				}
			}
			break;

		default:
			printf("...Extension block function code #%ld not know, skipping.\n",
			 cmdcode);

			/* we must skip over any data for the extension block */
			for (;;) {
				if ((size = fgetc(fh)) == -1) {
					printf("...I/O Error skipping unknown extension block.\n");
					return TRUE;
				}

				if (size == 0)
					return FALSE;

				if (fread(fh, buf, 1, size) != size) {
					printf("...I/O Error skipping unknown extension block.\n");
					return TRUE;
				}
			}
			break;
	}
}

/* this will convert a word from LSB/MSB to MSB/LSB */
void FlipWord(word)
UWORD *word;
{
	register UBYTE swap1;
	register UBYTE swap2;

	swap1 = *word & 0xFF;
	swap2 = (*word & 0xFF00) >> 8;
	*word = swap1 << 8 | swap2;
}

/*struct GIFdescriptor gdesc;*/

static struct ImageDesc idesc;
/*struct RGB GlobalColourTable[256];*/
static struct RGB LocalColourTable[256];
/*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 */
UWORD ReadError;
UBYTE CodeSize;
UWORD EOFCode;
UBYTE ReadMask;
UWORD	CompDataPointer;
UWORD CompDataCount;
UBYTE CompData[256];

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

APTR	dagiffh;
UWORD Code;
APTR date;

BOOL DoImage(fh)
APTR fh;
{
	register int index;
	register int colours;

	date= (APTR) &CompData[0];
	dagiffh=fh;

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

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

	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, &LocalColourTable[index], 3) != 3) {
				printf("......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;

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

		/* get the codesize and do general setup for decompression */
		if (Read(fh, &CodeSize, 1) == -1) {
			printf("\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);
/*		ReadCodex();*/
		while( Code != EOFCode ) {
			if (ReadError)
				return TRUE;

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

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

				while (CurCode > BitMask) {
					if (OutCount > 1024) {
						printf("\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);
/*			ReadCodex();*/
		}
	}

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

	/* done decompressing.  Erase decompressing message and exit */
	printf("\x9B22D\x9BKed.\n");

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

	return FALSE;
}

int ReadCode(fh)
APTR fh;
{
	register int temp;
	register int DstMasked;
	register int DstMask;
	register LONG size;
	unsigned char rid;

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

				if (Read(fh, (char *)CompData, size) != size) {
					printf("\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(index)
UBYTE index;
{
	register UWORD tile;
	UWORD row;
	UWORD byte;

	register UWORD manip2;
	register UWORD manip;
	register UBYTE schreib;

/*	SetAPen( rastport, index );*/
/*	WritePixel( rastport, XStore, YStore+20 );*/

	fprintf( out, "%c", index );

/*	tile = (Ypos/16)*20 + Xpos/16;

	row = Ypos & 15;
	byte = (Xpos & 8)/8;

	{
		manip = 1 << ( 7 - ( Xpos & 7 ) );
		manip2 = 255 ^ manip;
	
		if( ( index & 1 ) != 0 )
		{
			tiles[ tile ][ 0 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 0 ][ row ][ byte ] &= manip2;
		}

		if( ( index & 2 ) != 0 )
		{
			tiles[ tile ][ 1 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 1 ][ row ][ byte ] &= manip2;
		}

		if( ( index & 4 ) != 0 )
		{
			tiles[ tile ][ 2 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 2 ][ row ][ byte ] &= manip2;
		}

		if( ( index & 8 ) != 0 )
		{
			tiles[ tile ][ 3 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 3 ][ row ][ byte ] &= manip2;
		}

		if( ( index & 16 ) != 0 )
		{
			tiles[ tile ][ 4 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 4 ][ row ][ byte ] &= manip2;
		}

		if( ( index & 32 ) != 0 )
		{
			tiles[ tile ][ 5 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 5 ][ row ][ byte ] &= manip2;
		}

		if( ( index & 64 ) != 0 )
		{
			tiles[ tile ][ 6 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 6 ][ row ][ byte ] &= manip2;
		}

		if( ( index & 128 ) != 0 )
		{
			tiles[ tile ][ 7 ][ row ][ byte ] |= manip;
		}
		else
		{
			tiles[ tile ][ 7 ][ row ][ byte ] &= manip2;
		}

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

		printf("\x9B5D%5ld", Ypos + idesc.id_Top);
	}
}
