/*  :ts=8 bk=0
 *
 * tiff2.c:	Frame concept ILBM scanner.
 * 		Alpha Concept Prototype.
 *
 * All the code needed for an ILBM reader, except the stuff to process the
 * ILBM itself.  New one with a twist - the "frame" idea.
 *
 * Managing all the interactions of end of context and properties and
 * data chunks is kind of a hassle to get right without groking all the
 * complexities of recursive data structures and parsing, etc.  Frames
 * are an idea to make things a little easier.  The client just creates
 * a frame structure and a couple of handlers for dealing with them in
 * a generic way.  Then the client just has to fill in the details of the
 * frame by handling data chunks himself.  At the end of the context, 
 * the client gets handed a completed frame and he can go about his
 * business without concern for what got partly done or whatever.
 *
 * NOTE: This sucker ain't, like, totally done.
 *
 * Stuart Ferguson					8901.02
 * ewhac (Updated to 1.4 Beta)				8912.06
 * Latticeification					9005.31
 */
#include <exec/types.h>
#include <libraries/dos.h>
#include <utility/hooks.h>
#include <libraries/iffparse.h>
#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include "iffparse_protos.h"
#include "iffparse.p"

#define	ID_ILBM	MAKE_ID('I','L','B','M')
#define	ID_BODY	MAKE_ID('B','O','D','Y')
#define	ID_BMHD	MAKE_ID('B','M','H','D')
#define	ID_CMAP	MAKE_ID('C','M','A','P')
#define	ID_CAMG	MAKE_ID('C','A','M','G')
#define	ID_CRNG	MAKE_ID('C','R','N','G')


/*
 * Forward function declarations.  (I hate ANSI.)
 */
struct Frame *FindFrame (struct IFFHandle *, LONG);
LONG MakeFrame (struct IFFHandle *, LONG, struct Frame *(*)(), LONG (*)());
LONG __saveds __asm EnterFrame (register __a0 struct Hook *, register __a2 struct IFFHandle *, register __a1 LONG *);
LONG __saveds __asm FramePurge (register __a0 struct Hook *, register __a2 struct LocalContextItem *, register __a1 LONG *);
void DisplayILBM (struct ILBMframe *);
void ProcessBody (struct IFFHandle *, struct ILBMframe *);
void ProcessCycle (struct IFFHandle *, struct ILBMframe *);
static struct ILBMframe *CreateILBMframe (struct IFFHandle *);
static LONG FreeILBMframe (struct ILBMframe *);

static long	ilbmprops[] = {
	ID_ILBM, ID_BMHD,
	ID_ILBM, ID_CMAP,
	ID_ILBM, ID_CAMG
};

static long	ilbmstops[] = {
	ID_ILBM, ID_BODY,
	ID_ILBM, ID_CRNG,
};


#define MAXCRNG	8

/*
 * The "Frame" for a complete ILBM.  This stupid one will just be the
 * sizes of each chunk (just to show that it works).
 */
struct ILBMframe {
	LONG bmhd,body,cmap,camg;
	int ncrng;
	LONG crng[MAXCRNG];
};


struct Library	*IFFParseBase;


main (argc, argv)
int	argc;
char	**argv;
{
	struct IFFHandle	*iff;
	struct ContextNode	*top;
	struct ILBMframe	*fr;
	long			error;
	int			foundilbm = 0;

	if (!(IFFParseBase = OpenLibrary ("iffparse.library", 0L))) {
		puts ("Cannot open library.");
		goto die;
	}

	/*
	 * Create IFF file and open a DOS stream on it.
	 */
	if (!(iff = AllocIFF())) {
		puts ("AllocIFF() failed.");
		goto die;
	}

	if (!(iff -> iff_Stream = Open (argv[1], MODE_OLDFILE))) {
		puts ("File open failed.");
		goto die;
	}
	InitIFFasDOS (iff);

	/*
	 * Declare frame handlers for the ILBM format.
	 */
	if (error = MakeFrame (iff, ID_ILBM, CreateILBMframe, FreeILBMframe))
		goto err;

	/*
	 * Declare property, collection and stop chunks.
	 * (You still have to do this because YOU handle the data.)
	 */
	if (error = PropChunks (iff, ilbmprops, 3L))
		goto err;
	if (error = StopChunks (iff, ilbmstops, 2L))
		goto err;

	if (error = OpenIFF (iff, IFFF_READ))
		goto err;

	error = 0;
	while (!error) {
		/*
		 * ParseIFF() will return on BODY and CRNG chunks
		 * or when the frame is filled.  In any case, we 
		 * should nab a frame for this context (even if
		 * it's really NULL).
		 */
		error = ParseIFF (iff, IFFPARSE_SCAN);
		fr = (struct ILBMframe *) FindFrame (iff, ID_ILBM);

		/*
		 * At end of context, do something with the finished frame.
		 * This code will scan and do the same thing with all the
		 * ILBM FORM's in the file.
		 */
		if (error == IFFERR_EOC) {
			foundilbm = 1;
			DisplayILBM (fr);
			error = 0;
			continue;
		}

		/*
		 * Other errors are real and possibily nasty errors.
		 */
		if (error)
			break;

		/*
		 * We've hit data.  Switch and decode.
		 * Make sure it's inside a FORM, otherwise storing
		 * data into the frame (if there even is one) is wrong.
		 */
		top = CurrentChunk (iff);
		if (ParentChunk (top) -> cn_ID == ID_FORM)
			switch (top -> cn_ID) {
			    case ID_BODY:
				ProcessBody (iff, fr);
				break;
			    case ID_CRNG:
				ProcessCycle (iff, fr);
				break;
			}
	}
err:
	if (error == IFFERR_EOF) {
		if (foundilbm)
			puts ("File scan complete.");
		else
			puts ("Failed to find a FORM ILBM.");
	} else
		printf ("File scan aborted (%ld)\n", error);

die:
	if (iff) {
		CloseIFF (iff);
		if (iff -> iff_Stream)
			Close (iff -> iff_Stream);
		FreeIFF (iff);
	}

	if (IFFParseBase)	CloseLibrary (IFFParseBase);
}


/*
 * Just show that something happened.
 */
void
DisplayILBM (fr)
struct ILBMframe	*fr;
{
	int	i;

	printf ("A Picture:  BMHD:%ld, CMAP:%ld, CAMG:%ld, BODY:%ld\n",
		fr -> bmhd, fr -> cmap, fr -> camg, fr -> body);
	if (fr -> ncrng) {
		for (i = 0; i < fr -> ncrng; i++)
			printf ("CRNG:%ld  ", fr -> crng[i]);
		printf ("\n");
	} else
		printf ("No CRNG chunks.\n");
}


/*
 * Process a BODY chunk in an ILBM.  Just get the sizes of the various
 * properties and data for now.
 */
void
ProcessBody (iff, fr)
struct IFFHandle 	*iff;
struct ILBMframe	*fr;
{
	register struct StoredProperty	*sp;

	printf ("Body.\n");

	if (sp = FindProp (iff, ID_ILBM, ID_BMHD))
		fr -> bmhd = sp -> sp_Size;

	if (sp = FindProp (iff, ID_ILBM, ID_CMAP))
		fr -> cmap = sp -> sp_Size;

	if (sp = FindProp (iff, ID_ILBM, ID_CAMG))
		fr -> camg = sp -> sp_Size;

	fr -> body = CurrentChunk (iff) -> cn_Size;
}


void
ProcessCycle (iff, fr)
struct IFFHandle 	*iff;
struct ILBMframe	*fr;
{
	printf ("Cycle.\n");

	fr -> crng[fr -> ncrng ++] = CurrentChunk (iff) -> cn_Size;
}


/*
 * Demi-handler to create a new frame and init to default values.
 * The IFF is provided in case any default values are there.
 */
static struct ILBMframe *
CreateILBMframe (iff)
struct IFFHandle	*iff;
{
	struct ILBMframe	*f;

	if (!(f = (struct ILBMframe *) AllocMem ((LONG) sizeof (*f), 0L)))
		return NULL;
	printf ("Creating new frame: %lx\n", f);

	f -> bmhd = f -> body = f -> cmap = f -> camg = -1;
	f -> ncrng = 0;
	return f;
}


/*
 * Client call-back to free up his frame.  Deletes frame and all
 * associated memory.
 */
static LONG
FreeILBMframe (f)
struct ILBMframe	*f;
{
	printf ("Freeing frame: %lx\n", f);
	FreeMem (f, (LONG) sizeof (*f));
	return (0);
}


/*
 * ====
 * This stuff would be in the library to make the Frame management
 * capability available to all.
 * ====
 */

struct FrameDescriptor {
	struct Frame		*(*fd_Create)();
	LONG			(*fd_Delete)();
};

struct FrameInstance {
	struct Frame		*fi_Frame;
	struct IFFHandle	*fi_iff;	/*  We need this pointer  */
};

#define IFFLCI_FRAMEDESC	MAKE_ID('f','d','e','s')
#define IFFLCI_FRAME		MAKE_ID('f','r','a','m')

/*
 * Declare the handlers for a frame.  Client provides functions for
 * creating and deleting frames and the library will call them at the
 * appropriate times (entering and exiting the specified FORM).  These
 * function vectors get stored in a frame descriptor which will be
 * used by the generic entry and exit handlers.
 */
LONG
MakeFrame (iff, type, create_f, delete_f)
struct IFFHandle *iff;
LONG		 type;
struct Frame	 *(*create_f)();
LONG		 (*delete_f)();
{
	register struct LocalContextItem *lci;
	register struct FrameDescriptor	 *fd;
	register LONG			 error;
	static struct Hook		 EnterHook = {
		{ NULL },
		(ULONG (*)()) EnterFrame,
		NULL,
		NULL
	};

	if (!(lci = AllocLocalItem (type, 0L, IFFLCI_FRAMEDESC,
				    (long) sizeof (*fd))))
		return (IFFERR_NOMEM);

	fd = (struct FrameDescriptor *) LocalItemData (lci);
	fd -> fd_Create = create_f;
	fd -> fd_Delete = delete_f;

	if (error = StoreLocalItem (iff, lci, IFFSLI_TOP))
		goto fail;
	if (error = EntryHandler
		     (iff, type, ID_FORM, IFFSLI_TOP, &EnterHook, (APTR)iff))
		goto fail;
	if (error = StopOnExit (iff, type, ID_FORM))
		goto fail;
	return (0);

fail:
	FreeLocalItem (lci);
	return (error);
}


/*
 * Returns the current frame for the given context.
 */
struct Frame *
FindFrame (iff, type)
struct IFFHandle *iff;
LONG		 type;
{
	struct LocalContextItem *lci;

	if (!(lci = FindLocalItem (iff, type, 0L, IFFLCI_FRAME)))
		return (NULL);

	return (((struct FrameInstance *) LocalItemData (lci)) -> fi_Frame);
}


/*
 * On entering the FORM associated with a frame, create a new
 * frame and store it for this context.
 */
LONG __saveds __asm
EnterFrame (
register __a0 struct Hook	*hook,
register __a2 struct IFFHandle	*iff,
register __a1 LONG		*cmd
)
{
	struct LocalContextItem *lci;
	struct ContextNode	*cn;
	struct FrameInstance	*fi;
	struct FrameDescriptor	*fd;
	LONG			error, type;
	static struct Hook	PurgeHook = {
		{ NULL },
		(ULONG (*)()) FramePurge,
		NULL,
		NULL
	};

	cn = CurrentChunk (iff);
	type = cn -> cn_Type;

	if (!(lci = FindLocalItem (iff, type, 0L, IFFLCI_FRAMEDESC))) {
		puts ("It's where you thought.");
		return (IFFERR_NOSCOPE);
	}

	fd = (struct FrameDescriptor *) LocalItemData (lci);

	if (!(lci = AllocLocalItem (type, 0L, IFFLCI_FRAME,
				    (LONG) sizeof (*fi))))
		return (IFFERR_NOMEM);
	fi = (struct FrameInstance *) LocalItemData (lci);

	fi -> fi_iff = iff;
	if (!(fi -> fi_Frame = (*(fd -> fd_Create)) (iff))) {
		error = IFFERR_NOMEM;
		goto fail;
	}
	SetLocalItemPurge (lci, &PurgeHook);

	if (error = StoreLocalItem (iff, lci, IFFSLI_TOP))
		goto fail;
	return (0);

fail:
	FreeLocalItem (lci);
	return (error);
}


/* What's this, Stu?
 *
 * Hmmm..  What's what?  <whistle, whistle ...>
 *
static LONG
ExitFrame (hook, iff, cn)
struct Hook		*hook;
struct IFFHandle	*iff;
LONG			*cmd;
{
}
 *
 */


LONG __saveds __asm
FramePurge (
register __a0 struct Hook		*hook,
register __a2 struct LocalContextItem	*lci,
register __a1 LONG			*cmd
)
{
	struct FrameInstance	*fi;
	struct FrameDescriptor	*fd;

	fi = (struct FrameInstance *) LocalItemData (lci);

	fd = (struct FrameDescriptor *) LocalItemData (FindLocalItem
		(fi -> fi_iff, lci -> lci_Type, 0L, IFFLCI_FRAMEDESC));

	(*(fd -> fd_Delete)) (fi -> fi_Frame);

	FreeLocalItem (lci);
	return (0);
}

/*
 * Disable Lattice's default ^C trap.
 */
chkabort ()
{
	return (0);
}

CXBRK ()
{
	return (0);
}
