#include <stdio.h>
#include <exec/types.h>
#include <exec/memory.h>
#include <graphics/gfx.h>
#include <libraries/dos.h>
#include <intuition/intuition.h>
#include <exec/nodes.h>
#include <exec/libraries.h>
#include <graphics/rastport.h>
#include <graphics/text.h>
#include <intuition/intuition.h>
#include "jiff.h"
/* Code from in this file modified from WBLander Fred Fish #114 */
/* Orginal authors Peter da Silva and Karl Lehenbauer */
/* #define DEBUG */
/* #define PARANOID */

#ifdef DEBUG
# define D(x) x
#else
# define D(x)
#endif

#ifdef PARANOID
# define P(x) x
#else
# define P(x)
#endif

struct Library *GfxBase, *IntuitionBase,*OpenLibrary();

static struct ILBM_info *read_ilbm(), *read_body();
static struct ILBM_info *root_info;

void free_image();
#define CHIP (MEMF_PUBLIC|MEMF_CHIP)

struct Screen *show_iff();
struct ILBM_info *load_iff();

extern struct Screen *OpenScreen();
struct NewScreen ShowNewScreen;
struct TextAttr Tfont = { "topaz.font", 8, 0, 0};

void
put_ea_cmap(screen, cmap, colors)
struct Screen *screen;
unsigned char *cmap;
int colors;
{
	int i;

	for (i=0; i<colors; i++)
		SetRGB4(&screen->ViewPort, i,
			(long)cmap[3*i+0]>>4, (long)cmap[3*i+1]>>4, (long)cmap[3*i+2]>>4);
}

struct Screen *show_iff(info)
struct ILBM_info *info;
{
	struct Screen *screen;

	ShowNewScreen.LeftEdge = 0;
	ShowNewScreen.TopEdge = 0;
	ShowNewScreen.Width = info->header.w;
	ShowNewScreen.Height = info->header.h;
	ShowNewScreen.Depth = info->header.nPlanes;
	ShowNewScreen.DetailPen = 0;
	ShowNewScreen.BlockPen = 1;
	if((info->viewmode & HAM) != HAM &&
	   (info->viewmode & DUALPF) != DUALPF &&
	   info->header.nPlanes > 5
	) {
		info->viewmode |= HAM;
	}
	ShowNewScreen.ViewModes = info->viewmode;
	ShowNewScreen.Type = CUSTOMSCREEN | CUSTOMBITMAP | SCREENBEHIND;
	ShowNewScreen.Font = &Tfont;
	ShowNewScreen.DefaultTitle = "Fish Tank";
	ShowNewScreen.Gadgets = NULL;
	ShowNewScreen.CustomBitMap = &info->bitmap;
	if ( (screen = OpenScreen(&ShowNewScreen) ) != NULL)
		put_ea_cmap(screen, info->cmap, info->colors);
	ScreenToFront(screen);
	return screen;
}

struct ILBM_info *load_iff(file)
char *file;
{
	return read_iff(file, 0);
}

struct Screen *picture(mode,fname)
int mode;
char *fname;
{
	static struct ILBM_info *iff_info;
	static struct Screen *iff_screen;
	if(!mode)		/* For mode=0 show picture */
	{
		iff_info=load_iff(fname);
		iff_screen=show_iff(iff_info);
		return(iff_screen);
	}
	if(iff_screen!=NULL)
	{
		CloseScreen(iff_screen);
		free_image(iff_info);
		return(NULL);
	}
	return(NULL);
}

/* ********************************************************************* */
/*                                                                       */
/*			jiff.c   Jim Kent's iff - ilbm  reader           */
/*                                                                       */
/* This is the (sortof) short (sortof) simple no-frills IFF reader       */
/* to get something out of DPaint, Images, or the Animator.  It          */
/* works well with the Aztec C compiler.  It should work with Lattice    */
/* but you never know until you try it.  I haven't.                      */
/*                                                                       */
/* The main interface to this is through the routine read_iff(filename). */
/* This returns a ILBM_info structure-pointer on success, and NULL on    */
/* failure.  It cleans up after itself on failure.                       */
/*                                                                       */
/* I hope you will find this useful and easy to use.  Please forgive     */
/* my funky indentation style?  Well at least I'm consistent!            */
/*                                                                       */
/* 	[ I didn't forgive it. I changed it -- Peter ]                   */
/* ********************************************************************* */

#ifdef PARANOID
/* a little paranoid routine that say's where we got before EOF */
static void
iff_truncated(info,where)
struct ILBM_info *info;
int where;
{
	printf("ILBM truncated %d\n", where);
	free_image(info);
}
#else
#define iff_truncated(i,w) free_image(i)
#endif

struct ILBM_info *
read_iff(name, just_colors)
char *name;
short just_colors;
{
	struct ILBM_info *info;
	FILE *file;
	struct form_chunk chunk;

	info = 0;

	if((file = fopen(name, "r") ) == 0)
	{
		P(printf("couldn't Open %s to read\n", name));
		return(NULL);
	}

	if( fread(&chunk, sizeof(struct form_chunk), 1, file) != 1)
	{
		iff_truncated(NULL, 0);
		fclose(file);
		return(NULL);
	}

	if(chunk.fc_type.b4_type != FORM)
	{
		P(printf("not a FORM - %s\n", name));
		fclose(file);
		return(NULL);
	}

	if(chunk.fc_subtype.b4_type != ILBM)
	{
		P(printf("FORM not an ILBM - %s\n", name));
		fclose(file);
		return(NULL);
	}

	D(printf("FORM %ld ILBM\n", chunk.fc_length));

	root_info = AllocMem(sizeof(struct ILBM_info), MEMF_PUBLIC);
	if(!root_info) {
		P(printf("Can't allocate ILBM_info.\n"));
		fclose(file);
		return NULL;
	}

	info = read_ilbm(file, root_info, chunk.fc_length - sizeof(chunk), just_colors);
	if(info) {
		if(info->viewmode & HAM)
			info->colors = 16;
		if(info->header.h > 298)
			info->viewmode |= LACE;
		if(info->header.w > 376)
			info->viewmode |= HIRES;
	}
	fclose(file);
	D(printf("info = %lx\n", info));
	return(info);
}

static
struct ILBM_info *
read_ilbm(file, info, length, just_colors)
FILE *file;
struct ILBM_info *info;
long length;
short just_colors;
{
	struct iff_chunk chunk;
	int i;
	long read_in = 0;
	int got_header = FALSE;  /*to make sure gots the header first*/
	int got_cmap = FALSE;  /*make sure get cmap before "BODY" */

	info->viewmode = 0L;

	/*make sure the Planes are all NULL so can free up memory easily
	  on error abort */
	for (i=0; i<8; i++)
		info->bitmap.Planes[i] = NULL;

	while (read_in < length)
	{
		if(fread(&chunk, sizeof(chunk), 1, file) != 1)
		{
			iff_truncated(info, 1);
			return(NULL);
		}
		switch (chunk.iff_type.b4_type)
		{
			case BMHD:
				D(printf("\tBMHD %ld\n", chunk.iff_length));
				if(fread(&info->header, sizeof(info->header), 1, file) != 1)
				{
					iff_truncated(info, 2);
					return(NULL);
				}
				got_header = TRUE;
				break;
			case CMAP:
				D(printf("\tCMAP %ld\n", chunk.iff_length));
				if(!got_header) {
					printf("CMAP befor BMHD\n");
					free_image(info);
					return(NULL);
				}
				if(chunk.iff_length <= 3*MAXCOL ) {
					if(fread(info->cmap, (int)chunk.iff_length, 1, file) != 1) {
						iff_truncated(info, 3);
						return(NULL);
					}
					info->colors = chunk.iff_length/3;
				} else {
					P(printf("warning, more than %d colors in ILBM CMAP\n",
						MAXCOL));
					if(fread(info->cmap, 3*MAXCOL, 1, file) != 1)
					{
						iff_truncated(info, 4);
						return(NULL);
					}
					bit_bucket(file, chunk.iff_length - 3*MAXCOL);
					info->colors = MAXCOL;
				}
				got_cmap = TRUE;
				if(just_colors)
					return(info);
				break;
			case CAMG:
				D(printf("\tCAMG %ld\n", chunk.iff_length));
				if(chunk.iff_length <= sizeof(long) ) {
					if(fread(&info->viewmode, (int)chunk.iff_length, 1, file) != 1) {
						iff_truncated(info, 8);
						return(NULL);
					}
				} else {
					P(printf("warning, more than viewport mode in ILBM CMAP\n"));
					if(fread(&info->viewmode, sizeof(long), 1, file) != 1) {
						iff_truncated(info, 9);
						return(NULL);
					}
					bit_bucket(file, chunk.iff_length - sizeof(long));
				}
				break;
			case BODY:
				if(!got_cmap) {
					P(printf("BODY before CMAP\n"));
					free_image(info);
					return(NULL);
				}
				D(printf("\tBODY %ld\n", chunk.iff_length));
				return( read_body(file, info, chunk.iff_length) );

			default:
#ifdef PARANOID
				printf("Unknown type ");
				printf("%c", (chunk.iff_type.b4_type>>24)&0x7F);
				printf("%c", (chunk.iff_type.b4_type>>16)&0x7F);
				printf("%c", (chunk.iff_type.b4_type>>8)&0x7F);
				printf("%c", (chunk.iff_type.b4_type)&0x7F);
				printf(" of b4_type\n");
#endif PARANOID
			case GRAB:  /*ignore documented but unwanted types*/
			case DEST:
			case SPRT:
			case CRNG:
			case CCRT:
				bit_bucket(file, chunk.iff_length);
				break;
		}
		read_in += chunk.iff_length + sizeof(chunk);
	}
	P(printf("no BODY in ILBM\n"));
	free_image(info);
	return(NULL); 
}

static
struct ILBM_info *
read_body(file, info, length)
FILE *file;
register struct ILBM_info *info;
long length;
{
	struct ILBM_header *header;
	struct BitMap *bm;
	int i, j;
	int rlength;
	int plane_offset;

	D(printf("read_body( %lx %lx %ld)\n", file, info, length));

#ifdef PARANOID
	/* when paranoid do a little error checking first ... fail fast! */
	if(info->header.nPlanes > 8) {
		printf("Whoa, woe  Dale only speaks 8 planes boy, not %d\n",
			info->header.nPlanes);
		free_image(info);
		return(NULL);
	}
#endif PARANOID

	/* ok a little more error checking */
	if(info->header.compression != 0 && info->header.compression != 1) {
		P(printf("unrecognized compression type %d\n", info->header.compression));
		free_image(info);
		return(NULL);
	}

	/*set up the bitmap part that doesn't involve memory allocation first -
	  hey this part does get done, and let's be optimistic...*/
	info->bitmap.BytesPerRow = line_bytes(info->header.w);
	info->bitmap.Rows = info->header.h;
	info->bitmap.Depth = info->header.nPlanes;
	info->bitmap.Flags = info->bitmap.pad = 0;

	rlength = info->bitmap.Rows * info->bitmap.BytesPerRow;

	for (i=0; i<info->header.nPlanes; i++) {
		if((info->bitmap.Planes[i] = AllocMem(rlength, CHIP)) == NULL) {
			P(printf("couldn't alloc plane %d in read_body\n",i));
			free_image(info);
			return(NULL);
		}
	}

	plane_offset = 0;
	for (i=0; i<info->bitmap.Rows; i++) {
		/* this test should be in the inner loop for shortest code,
		   in the outer loop for greatest speed, so sue me I compromised */
		if(info->header.compression == 0) {
			for (j = 0; j < info->bitmap.Depth; j++) {
				if(fread(info->bitmap.Planes[j] + plane_offset,
				   info->bitmap.BytesPerRow, 1, file) != 1) {
					iff_truncated(info, 6);
					return(NULL);
				}
			}
		} else {
			register char *dest, value;
			register int so_far, count;  /*how much have unpacked so far*/

			for (j = 0; j < info->bitmap.Depth; j++) {
				so_far = info->bitmap.BytesPerRow;
				dest = (char *)info->bitmap.Planes[j] + plane_offset;
				while (so_far > 0) {
					if( (value = getc(file)) == 128) {
						D(printf("NOP\n"));
					} else if(value > 0) {
						count = (int)value + 1;
						so_far -= count;
						if( fread(dest, count, 1, file) != 1) {
							iff_truncated(info, 7);
							return(NULL);
						}
						dest += count;
					} else  {
						count = (int)-value + 1;
						so_far -= count;
						value = getc(file);
						while (--count >= 0)  /*this is fastest loop in C */
							*dest++ = value;
					}
				}
				if(so_far != 0) {
					P(printf("compression quite screwed up, aborting %d\n", so_far));
					free_image(info);
					return(NULL);
				}
			}
		}
		plane_offset += info->bitmap.BytesPerRow;
	}
	return(info);
}

void free_image(info)
struct ILBM_info *info;
{
	if(info) {
		free_planes(&info->bitmap);
		FreeMem(info, sizeof(struct ILBM_info));
	}
}

void
free_planes(bmap)
register struct BitMap *bmap;
{
	PLANEPTR plane;
	long length;
	short i;

	length = bmap->BytesPerRow * bmap->Rows;

	for (i=0; i<8; i++)
		if( (plane = bmap->Planes[i]) != NULL)
			FreeMem(plane, length);
}

