/**************************************************************************
 *			MAND7.C - Save Dragon (Write Picture)
 *	     	       Mandelbrot Self-Squared Dragon Generator
 *			     For the Commodore Amiga
 *				Version 2.05
 *
 *			Copyright (C) 1986, =Robert J. Mical=
 *		  Further feached and even considerably optimized by
 *				     >>SDB<<
 *		       Copyright (C) 1986, Scott D. Ballantyne
 *			      Placed in the Public Domain
 *
 *	This program may be distributed free of charge as long as the above
 *	notice is retained.  You may extract any or all information contained
 *	in this file for use in your own programs
 *
 **************************************************************************

#include "mand.h"

extern struct 	Menu MainMenu[MENU_COUNT];
extern struct 	MenuItem OptionsItems[OPTIONS_COUNT];
extern SHORT 	Color0, Color1, Color2;
extern SHORT 	UserPalette[29];
/*----------------------*/
/* Graphics definitions */
extern struct GfxBase		*GfxBase;
extern struct IntuitionBase	*IntuitionBase;
 
extern struct RastPort		*rp;
extern struct ViewPort		*vp;
 
extern struct Window		*w, *ColorWindow;
extern struct Screen  		*screen;
extern struct IntuiMessage	*message;
 
extern SHORT			last_color;
extern BOOL			SettingCenter, SettingBoxSize;
/*----------------------------------*/
/* Miscellaneous Global Definitions */
extern FLOAT	start_r, end_r, start_i, end_i;	/* Block bounds for set */
extern int	max_x, max_y;
extern int	max_count, color_inc, color_offset;
extern int	color_set, color_mode, color_div;
extern int	color_inset, func_num;
extern UWORD	*color_table;
extern FILE	*console, *redir_fp;
 
extern SHORT	ZoomCenterX, ZoomCenterY, ZoomBoxSizeX, ZoomBoxSizeY;
extern SHORT	ZoomBoxStartX, ZoomBoxStartY;
extern struct	NewScreen	ns;
extern SHORT	y_coord;	/* These allow restarting */
extern BOOL	Resume;
extern UWORD	Compressed;	/* 0 = no compression, 1 = compression */

/* === variables defined here ============================================= */

char *FriendlyTools[] = {
	"FILETYPE=Mandelbrot",
};

UWORD ProjectObjectData[] = {
	0x1FF0, 0x0000,
	0x783C, 0x6000,
	0xE00F, 0xF800,
	0xE00F, 0xF800,
	0x783C, 0x6000,
	0x1FF0, 0x0000,
	/* */
	0x0000, 0x0000,
	0x07C0, 0x0000,
	0x1FF0, 0x0000,
	0x1FF0, 0x0000,
	0x07C0, 0x0000,
	0x0000, 0x0000,
};
struct Image ProjectObjectImage = {
	0, 0,
	21, 6, 2,
	ProjectObjectData,
	3, 0,
	NULL,
};

struct DiskObject ProjectObject = {
	WB_DISKMAGIC,
	WB_DISKVERSION,
	{ /* the Gadget structure */
		NULL,
		0, 0,
		25, 12,
		GADGHBOX | GADGIMAGE,
		RELVERIFY | GADGIMMEDIATE,
		BOOLGADGET,
		(APTR)&ProjectObjectImage,
		NULL,
		NULL,
		0, 0, 0, 0,
	},
	WBPROJECT,
	":Mandelbrot",
	FriendlyTools,
	NO_ICON_POSITION, NO_ICON_POSITION,
	NULL,
	NULL,
	PROCESS_STACKSIZE,
};

LONG PictModes[3] = {
	CAMG,
	4,
	NULL,
};
LONG PictBody[2] = {
	BODY,
	40000,
};

LONG MandelHeader[2] = {
	MNDL,
	sizeof(struct MandelInfo)
};

struct PaintingHeader PaintingHeader = {
	FORM,		/* IFFID */
	(40156),	/* Set later, actually */
	ILBM,               
	BMHD,               
	sizeof(struct BitMapHeader),
        { /* start of the BitMapHeader structure */
		0, 0,
		0, 0,
		5,
		0, 
		0,
		0,
		0,
		10, 11,
		320, 200,	/* end of BitMapHead */
	},
	CMAP,               
	96,		/* Actual value set later, and followed by color regs */
};

extern ULONG	PackPlanes();
extern UWORD	packem_in();

BOOL SavePicture(filename)
UBYTE *filename;
{
	REGISTER WORD		i, j, offset;
	struct BitMap		*bitmap = &screen->BitMap;
	ULONG			length, file;
	UBYTE			*p, component, rgbbyte;
	UWORD			rgb;
	struct MandelInfo	mi;
	BOOL			written = FALSE;

	/* Open the data file */

	if ( !(file = Open(filename, MODE_NEWFILE)) )
		goto ERROR;

	/* Initialize the BitMapHeader */

	BM_W = PAGE_WIDTH = ns.Width;
	BM_H = PAGE_HEIGHT = ROWS;
	N_PLANES = DEPTH;	
	COMPRESSION = Compressed;

	if (BM_W == 320)
	{
		if (BM_H == 200)
		{
			X_ASPECT = X320x200;
			Y_ASPECT = Y320x200;
		}
		else /* 400 */
		{
			X_ASPECT = X320x400;
			Y_ASPECT = X320x400;
		}
	}
	else /* 640 */
	{
		if (BM_H == 200)
		{
			X_ASPECT = X640x200;
			Y_ASPECT = Y640x200;
		}
	}

	/* Compute the sizes of the various chunks */

	CMAP_LENGTH = (1 << DEPTH) * 3;
	if (CMAP_LENGTH > 96)
		CMAP_LENGTH = 96;
	length = sizeof(struct PaintingHeader);
	FILE_LENGTH = length + CMAP_LENGTH + MandelHeader[1] + 8 + 12;

	if (Compressed)
	{
		if ( (PictBody[1] = PackPlanes(NULL, bitmap)) == 0)
			goto WRITE_ERROR;
	}
	else
	{
		PictBody[1] = BPR * ROWS * DEPTH;
	}

	FILE_LENGTH += PictBody[1];
	if (Write(file, &PaintingHeader, length) != length)
		goto WRITE_ERROR;

	/* Followed by the color register values */

	length = (1 << DEPTH);
	if (length > 32)
		length = 32;

	for (i = 0; i < length; i++)
	{
		for (component = 0; component < 3; component++)
		{
			rgb = GetRGB4(vp->ColorMap, (LONG)i);
			rgbbyte = ( rgb >> (4 * (2 - component)) );
			rgbbyte <<= 4;
			if (Write(file, &rgbbyte, 1L) != 1)
				goto WRITE_ERROR;
		}
	}

	/* Followed by an initialized CAMG chunk */

	PictModes[2] = vp->Modes;
	if (Write(file, PictModes, 12L) != 12)
		goto WRITE_ERROR;

	/* Now the body chunk. First the header: */

	if (Write(file, PictBody, 8L) != 8)
		goto WRITE_ERROR;

	/* Then the data - compressed or not depending on Compressed */

	if (Compressed)
	{
		if (PackPlanes(file, bitmap) == 0)
			goto WRITE_ERROR;
	}
	else /* User doesn't want it compressed - who knows why? */
	{
		offset = 0;
		length = BPR;

		for (j = 0; j < ROWS; j++)	/* for every line */
		{
			for (i = 0; i < DEPTH; i++)
			{
				/* p = Plane( i ) + offset; */
				if (Write(file, (Plane( i ) + offset), length) != length)
					goto WRITE_ERROR;
			}
			offset += length;
		}
	}
	/* Finally, initialize and write out this programs special data
	 * structure. We place it after the body since it is less likely
	 * to cornfuse unsophisticated IFF readers that way. I hope.
	 */

	mi.mi_start_r = start_r;
	mi.mi_end_r = end_r;
	mi.mi_start_i = start_i;
	mi.mi_end_i = end_i;
	mi.mi_max_x = max_x;
	mi.mi_max_y = max_y;
	mi.mi_max_count = max_count;
	mi.mi_color_inc = color_inc;
	mi.mi_color_offset = color_offset;
	mi.mi_color_set = color_set;
	mi.mi_color_mode = color_mode;
	mi.mi_color_inset = color_inset;
	mi.mi_color_div = color_div;
	mi.mi_func_num = func_num;
	mi.mi_starty = y_coord;

	/* And send it on it's way... */

	if (Write(file, MandelHeader, 8L) != 8)		/* The header */
		goto WRITE_ERROR;

	length = sizeof(struct MandelInfo);
	if (Write(file, &mi, length) != length)		/* The chunk */
		goto WRITE_ERROR;

	if ( (!PutDiskObject(filename, &ProjectObject)) && IoErr() )
		goto WRITE_ERROR;

	written = TRUE;

WRITE_ERROR:
	Close(file);
ERROR:
	if (NOT written)
	{
		DeleteFile(file);
		fprintf(console, "*** Couldn't write your file! ***\n");
		DisplayBeep(NULL);
	}
	return(written);
}

ULONG PackPlanes(file, bitmap)
ULONG	file;
struct BitMap	*bitmap;
{
	UBYTE		pad = 0;
	REGISTER UWORD	i, j;
	UWORD		row_length;
	REGISTER ULONG	total_length = 0;
	UWORD		offset = 0;

	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < DEPTH; j++)
		{
			if ( (row_length = packem_in(file, Plane( j ) + offset,
				BPR )) == 0)
					return(0);
			total_length += row_length;
		}
		offset += BPR;
	}
	if (total_length & 1)	/* odd lengths not allowed in IFF */
	{
		if (file)
		{
			if (Write(file, &pad, 1L) != 1)
				return(0);
		}
		total_length++;
	}
	return(total_length);
}

/* Following compresses a single source line of data, and writes it to file
 * if file != NULL.
 *
 * The compression scheme, as stated in the IFF standard is as follows:
 *
 *	{ 0 .. 127 }	followed by n+1 bytes of data, copied literally.
 *	{ -1.. -127 }	followed by byte to be repeated (-n) + 1 times.
 *	-128		NOOP
 */

#define DUMP	0
#define RUN	1

#define MINRUN	3
#define MAXRUN	128
#define MAXDAT	128

UWORD packem_in(file, source, size)
ULONG	file;
REGISTER BYTE *source;
REGISTER WORD size;
{
	BYTE		current_c, last_c;
	BYTE		mode = DUMP;
	WORD		run_start = 0;
	REGISTER WORD	bytes_written = 0;
	BYTE		buf[MAXDAT * 3 / 2];
	REGISTER WORD	nbuf = 0;
	BYTE		byte_buffer;

	buf[nbuf++] = last_c = *source++;
	size--;

	for( ; size; --size)
	{
		buf[nbuf++] = current_c = *source++;
		switch(mode)
		{
			case DUMP:
				if (nbuf > MAXDAT)
				{
					if (file)
					{
						byte_buffer = MAXDAT - 1;
						if (Write(file, &byte_buffer, 1L) != 1)
							return(0);
						if (Write(file, buf, (LONG)MAXDAT) != MAXDAT)
							return(0);
					}
					bytes_written += (MAXDAT + 1);
					buf[0] = current_c;
					nbuf = 1;
					run_start = 0;
					break;
				}
				if (current_c == last_c)
				{
					if (nbuf - run_start >= MAXRUN)
					{
						if (run_start > 0)
						{
							if (file)
							{
								byte_buffer = run_start - 1;
								if (Write(file, &byte_buffer, 1L) != 1)
									return(0);
								if (Write(file, buf, (LONG)run_start) != run_start)
									return(0);
							}
							bytes_written += (run_start + 1);
						}
						mode = RUN;
					}
					else
						if (run_start == 0)
							mode = RUN;
				}
				else
					run_start = nbuf - 1;
				break;
			case RUN:
				if ( (current_c != last_c) || (nbuf - run_start > MAXRUN) )
				{
					if (file)
					{
						byte_buffer = -(nbuf - run_start - 2);
						if (Write(file, &byte_buffer, 1L) != 1L)
							return(0);
						if (Write(file, &last_c, 1L) != 1L)
							return(0);
					}
					bytes_written += 2;
					buf[0] = current_c;
					nbuf = 1;
					run_start = 0;
					mode = DUMP;
				}
				break;
		} /* End switch */
		last_c = current_c;
	} /* End for */
	switch(mode)
	{
		case DUMP:
			if (file)
			{
				byte_buffer = nbuf - 1;
				if (Write(file, &byte_buffer, 1L) != 1)
					return(0);
				if (Write(file, buf, (LONG)nbuf) != nbuf)
					return(0);
			}
			bytes_written += (nbuf + 1);
			break;
		case RUN:
			if (file)
			{
				byte_buffer = -(nbuf - run_start - 1);
				if (Write(file, &byte_buffer, 1L) != 1)
					return(0);
				if (Write(file, &last_c, 1L) != 1)
					return(0);
			}
			bytes_written += 2;
			break;
	}
	return(bytes_written);
}

