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

#include "GIFMachine.h"
#include <workbench/startup.h>

struct GIFdescriptor gdesc;

UWORD **BitPlane;
UWORD GlobalColourTable[256];

extern UWORD *SHAMmem;

extern BYTE *PlaneBuf;

extern BOOL Laced;

extern UBYTE *Planes[6];

ULONG ImageNumber;
extern UWORD TotalColours;

/* here we have some defines relating to our GADS call */
#define ESC "\x9B"
#define GIFMACH ESC "33;42mGIFMachine" ESC "32;40m"
#define ARP_HELP	GIFMACH " v1.0 \xA9 1990 by " \
			ESC "4;33;42mChristopher A. Wichura" \
			ESC "0;33;40m (" ESC "32mcaw" ESC "33m@" \
			ESC "32mmiroc.chi.il.us" ESC "33m)\n" \
			ESC "31mUsage:     " GIFMACH " <" ESC \
			"31mGIFfile(s)" ESC "32m> [" ESC "33mTO " \
			ESC "31mDirectory | File" ESC "32m] [" \
			ESC "33mALL" ESC "32m]\n                      [" \
			ESC "33mNOBORDER " ESC "32m<" ESC "31mLine " \
			"Thresh" ESC "32m>] [" ESC "33mXCOMP" \
			ESC "32m]\n" ESC "31m"

#define ARP_TEMPLATE "GIFfiles/...,TO/k,ALL/s,NOBORDER/k,XCOMP/s"
#define ARG_FILES  0
#define ARG_TO     1
#define ARG_ALL    2
#define ARG_NOBORD 3
#define ARG_XCOMP  4
#define ARG_sizeof 5

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

int NoBorderLineThresh = 0;

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

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

/* storage for the math library base */
struct Library *MathIeeeDoubBasBase;

/* here we have our main routine */
int __regargs main(char *cmdptr, int cmdlen, struct WBStartup *WBMsg)
{
	register char **FilesPtr;

	if (WBMsg) {
		WarnMustUseCLI();
		XCEXIT(5);
	}

	if (!(MathIeeeDoubBasBase = ArpOpenLibrary("mathieeedoubbas.library", 0))) {
		Puts("Unable to access mathieeedoubbas.library!");
		XCEXIT(5);
	}

	memset((char *)ArgArray, 0, sizeof(ArgArray));

	if (GADS(cmdptr, cmdlen, ARP_HELP, ArgArray, ARP_TEMPLATE) == -1) {
		Puts(ArgArray[0]);
		XCEXIT(5);
	}

	if (ArgArray[ARG_TO])
		ArgToIsDir = IsDir(ArgArray[ARG_TO]);

	if (ArgArray[ARG_NOBORD]) {
		NoBorderLineThresh = Atol(ArgArray[ARG_NOBORD]);
		if (NoBorderLineThresh < 0 || NoBorderLineThresh > 100) {
			Puts("Invalid NOBORDER line threshhold specified.");
			XCEXIT(3);
		}
	}

	if (!(FilesPtr = (char **)ArgArray[ARG_FILES])) {
		Puts("No GIF files selected.");
		XCEXIT(3);
	}

	InitDiff();	/* one time init for the RGBdiff function */

	while (*FilesPtr)
		DoPattern(*FilesPtr++);	
}

/* this will walk through a pattern doing conversions */
void DoPattern(char *pat)
{
	register struct Anchor {
		struct AnchorPath APath;
		char              Path[256];
	} *anchor;
	register int error;

	if (!(anchor = (struct Anchor *)ArpAlloc(sizeof(struct Anchor)))) {
		Puts("Out of memory!");
		XCEXIT(10);
	}

	anchor->APath.ap_StrLen = sizeof(anchor->Path);
	anchor->APath.ap_Flags = APF_DoWild;
	anchor->APath.ap_BreakBits = SIGBREAKF_CTRL_C;

	error = FindFirst(pat, &anchor->APath);

	while (!error) {
		if (anchor->APath.ap_Info.fib_DirEntryType > 0) {
			if (ArgArray[ARG_ALL]) {
				if (!(anchor->APath.ap_Flags & APF_DidDir))
					anchor->APath.ap_Flags |= APF_DoDir;
				anchor->APath.ap_Flags &= ~APF_DidDir;
			}
		} else
			Convert(anchor->APath.ap_Buf);

		error = FindNext(&anchor->APath);
	}

	FreeAnchorChain(&anchor->APath);

	switch(error) {
		case ERROR_BREAK:
			Puts(AbortMsg);
			XCEXIT(ABORTEXITVAL);
			break;

		case ERROR_OBJECT_NOT_FOUND:
			Puts("File not found.");
			break;

		case ERROR_BUFFER_OVERFLOW:
			Puts("Path too long!");
			break;

		case ERROR_NO_MORE_ENTRIES:	/* normal termination */
			break;

		default:
			Printf("I/O Error #%ld!\n", error);
			break;
	}

	FreeTrackedItem((struct DefaultTracker *)anchor);
}

/* here we have the routine that gets ready to do the conversion */
void Convert(char *name)
{
	register BPTR fh;
	register int index;
	char *basename;
	char *ptr;
	char sig[7];
	UBYTE buf[3];
	int size;
	int error;
	int colours;
	UWORD NewBackground;

	struct DateStamp StartTime, EndTime;

	if (!CreateTaskResList()) {
		Puts("Fatal tracker error!");
		XCEXIT(10);
	}

	if (!(fh = ArpOpen(name, MODE_OLDFILE))) {
		Printf("Error #%ld trying to open %s...", IoErr(), name);
		goto LeaveConvert;
	}

	sig[6] = NULL;

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

	Printf("Converting %s ", name);

	basename = BaseName(name);
	ptr = basename + strlen(basename) - 4;

	if (!strnicmp(".gif", ptr, 4))
		*ptr = NULL;

	size = strlen(basename) + 6;

	if (ArgArray[ARG_TO]) {
		if (ArgToIsDir)
			size += strlen(ArgArray[ARG_TO]) + 1;
		else
			size = strlen(ArgArray[ARG_TO]) + 1;
	}

	if (!(ptr = ArpAlloc(size))) {
		Puts("... Out of memory!");
		goto LeaveConvert;
	}

	if (ArgArray[ARG_TO]) {
		strcpy(ptr, ArgArray[ARG_TO]);

		if (ArgToIsDir) {
			TackOn(ptr, basename);
			strcat(ptr, ".sham");
		}
	} else {
		strcpy(ptr, basename);
		strcat(ptr, ".sham");
	}

	Printf("to %s...\n", ptr);

	DateStamp((LONG *)&StartTime);

	if (Read(fh, (char *)&gdesc, 7) != 7) {
		Puts("Error reading screen descriptor.");
		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 = TotalColours = 0;
	colours = 1L << ((gdesc.gd_ColInfo & 7) + 1);

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

		for (index = 0; index < colours; index++) {
			buf[0] = buf[1] = buf[2] = CVRTGIF(index);
			GlobalColourTable[index] = AddColour(buf);
		}
	} else {
		Printf("Global colour map contains %ld entries.\n", colours);

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

			GlobalColourTable[index] = AddColour(buf);
			if (index == gdesc.gd_BackGround)
				NewBackground = GlobalColourTable[index];
		}

		gdesc.gd_BackGround = NewBackground;
	}

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

	if (!(BitPlane = (UWORD **)ArpAlloc(gdesc.gd_Height * sizeof(UWORD *))) ||
	    !(SHAMmem  = (UWORD *)ArpAlloc(gdesc.gd_Height * 16 * sizeof(UWORD))) ||
	    !(PlaneBuf  = (BYTE *)ArpAlloc(size))) {
		Puts("Out of memory trying to allocate picture.");
		goto LeaveConvert;
	}

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

	for (index = 0; index < gdesc.gd_Height; index++)
		if (!(BitPlane[index] = (UWORD *)ArpAlloc(size))) {
			Puts("Out of memory trying to allocate picture.");
			goto LeaveConvert;
		}

	size = ((gdesc.gd_Width + 7) / 8) + 1;
	for (index = 0; index < 6; index++)
		if (!(Planes[index] = (UBYTE *)ArpAlloc(size))) {
			Puts("Out of memory trying to allocate picture.");
			goto LeaveConvert;
		}

	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(fh, buf, 1) != 1) {
			Puts("Error reading GIF file.");
			break;
		}

		switch(buf[0]) {
			case GIF_IMAGE:
				error = DoImage(fh);
				break;

			case GIF_EXTENSION:
				error = DoExtension(fh);
				break;

			case GIF_TERMINATOR:
				Printf("...%ld unique colours used by GIF file.\n",
					TotalColours);

				if (ArgArray[ARG_NOBORD])
					StripBorder();

				if (ArgArray[ARG_XCOMP] && ISTOOBIG) {
					DoXComp();
					DidXComp = 1;
				}

				if (gdesc.gd_Height > 200 && (!ISTOOBIG || DidXComp))
					Laced = TRUE;
				else
					Laced = FALSE;

				GIFtoSHAM();
				error = WriteIFF(ptr);
				break;

			default:
				Printf("...Unknown directive #%ld encountered.\n",
					buf[0]);
				error = TRUE;
		}
	}

	DateStamp((ULONG *)&EndTime);

	{
		register ULONG Hours;
		register ULONG Minutes;
		register ULONG Seconds;
		register ULONG Seconds2;
	
		Seconds = (EndTime.ds_Days * 86400) + (EndTime.ds_Minute * 60) + (EndTime.ds_Tick / TICKS_PER_SECOND);
		Seconds2 = (StartTime.ds_Days * 86400) + (StartTime.ds_Minute * 60) + (StartTime.ds_Tick / TICKS_PER_SECOND);

		Seconds -= Seconds2;

		Hours = Seconds / 3600;
		Seconds -= Hours * 3600;

		Minutes = Seconds / 60;
		Seconds -= Minutes * 60;

		Printf("...Conversion time was %ld hour%s, %ld minute%s and %ld second%s.\n",
			Hours, (Hours != 1 ? "s" : ""),
			Minutes, (Minutes != 1 ? "s" : ""),
			Seconds, (Seconds != 1 ? "s" : ""));
	}

LeaveConvert:
	FreeTaskResList();
}

/* this will check to see if we have a directory or not */
BOOL IsDir(char *name)
{
	register BPTR lock;
	register BOOL result = FALSE;

	/* kludge to word allign our FileInfoBlock to avoid having to */
	/* call AllocMem() for it.  Gotten from a UseNet message.     */

	char c_fib[sizeof(struct FileInfoBlock) + 3];
	struct FileInfoBlock *fib = (struct FileInfoBlock *)
		((unsigned long)(((unsigned long)c_fib+3)>>2)<<2);

	if (lock = Lock(name, ACCESS_READ)) {
		if (Examine(lock, fib)) {
			if (fib->fib_DirEntryType > 0)
				result = TRUE;
		}
		UnLock(lock);
	}

	return result;
}

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

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