/************************************************************************
*  Version 3.00  MANDELBROT - Self-Squared Dragon Generator  29-May-86  *
*  Commodore Amiga             Command Parsing                  MAND.C  *
*************************************************************************
*         Copyright (c) 1986, Robert S. French and R. J. Mical          *
* --------------------------------------------------------------------- *
*  This program has been placed in the public domain.  A limited        *
*  license is hereby granted for the unlimited use and distribution of  *
*  this program, provided it is not used for commercial or profit-      *
*  making purposes.  Thank you.                                         *
*************************************************************************
*  Author information:            | Name:   R. J. Mical                 *
*                                 | USnail: Commodore-Amiga, Inc.       *
*  Name:   Robert S. French       |         983 University Avenue       *
*  USnail: 2740 Frankfort Avenue  |         Los Gatos, CA  95030        *
*          Louisville, KY  40206  \-------------------------------------*
*  Phone:  (502) 897-5096              UUCP: ihnp4!ptsfa!well!french    *
*  ARPA:   French#Robert%d@LLL-MFE or RFrench@MIT-MULTICS               *
*************************************************************************
*  Please send any comments, suggestions, or bugs to one of the above   *
*  addresses.                                                           *
************************************************************************/

#include "mand.h"
#include "globals.h"


/*-------------*/
/* Here we go! */

main()
{
	FILE *fopen(),*freopen();
	char *fgets(),*stopblnk();
	long ftell();
#ifndef DOUBLEPREC
	float cnvf();
#endif
	void anal_mand(),wait_close();

	double dtemp;
	int temp,t1,t2,t3,x,y,i,filetype;
	UWORD data1,data2,repcount;
	char *cmd,inp_buf[80],secchar,*argpos,*argpos2;
	FLOATSTRUCT scale,ftemp;
	FILE *fp;

	max_mem_y = MAXMY;

	color_table = (UWORD *)AllocMem(4096*sizeof(UWORD),MEMF_CHIP|MEMF_PUBLIC);
	if (color_table == NULL)
		abort("Can't allocate memory for color table");

	v_mand_store = (UWORD *)AllocMem(MAXX*max_mem_y*sizeof(UWORD),MEMF_CHIP|MEMF_PUBLIC);
	if (v_mand_store == NULL)
		abort("Can't allocate memory for set storage");

#ifndef DOUBLEPREC

	MathBase = OpenLibrary("mathffp.library",0);
	if (MathBase == NULL)
		abort("Can't open mathffp.library");

	MathTransBase = OpenLibrary("mathtrans.library",0);
	if (MathTransBase == NULL)
		abort("Can't open mathtrans.library");

#endif

	GfxBase = (struct GfxBase *)OpenLibrary("graphics.library",0);
	if (GfxBase == NULL)
		abort("Can't open graphics.library");

	IntuitionBase = (struct IntuitionBase *)OpenLibrary("intuition.library",0);
	if (IntuitionBase == NULL)
		abort("Can't open intuition.library");

	IconBase = (struct IconBase *)OpenLibrary("icon.library",0);
	if (IconBase == NULL)
		abort("Can't open icon.library");

/* This is a kludge to allow Manx to run nice and fast without hurting
   execution under Lattice */

	console = freopen("con:0/0/640/200/Mandelbrot Commands","w+",stdout);

	if (console == NULL)
		abort("Can't open console window");

	printf("Mandelbrot Self-Squared Dragon Generator - Version %s - %s\n",VERSION,MOD_DATE);
#ifdef DOUBLEPREC
	printf("Precision: DOUBLE\n");
#else
	printf("Precision: SINGLE\n");
#endif
	printf("Copyright (C) 1986, Robert S. French and Robert J. Mical\n\n");
	printf("Placed in the public domain -- Read the source and learn!\n\n");
	printf("Type '?' for help\n\n");

	InitParams();

	UserPalette[0] = 0x000;
	UserPalette[1] = 0xFFF;
	UserPalette[2] = 0x22F;

	for (i = 0; i <= 28; i++)
		UserPalette[i+3] = ((i & 0xF) << 8) | ((i<16?0:15) << 4) | ((i & 0xF) << 0);

	for (EVER) {

command:

		if (redir_fp) {
			printf("Redir: ");		
			if (Chk_Abort()) {
				printf("\nRedirected input aborted!\n");
				fclose(redir_fp);
				redir_fp = NULL;
			}
			else {
				cmd = fgets(inp_buf,78,redir_fp);
				if (cmd == NULL) {
					printf("End of file encountered.\n");
					fclose(redir_fp);
					redir_fp = NULL;
					goto command;
				}
				else
					printf(inp_buf);
			}
		}
		else {
			printf("Command: ");
			rewind(stdout);
			cmd = fgets(inp_buf,78,stdout);
		}
		*(cmd+strlen(cmd)-1) = '\0';

proc_command:

		cmd = stopblnk(cmd);
		secchar = toupper(*(cmd+1));
		argpos = stopblnk(cmd+1);
		argpos2 = stopblnk(cmd+2);
		rewind(stdout);

		switch (toupper(*cmd)) {
			case 'H':
				Help();
				goto command;
			case 'S':
				if (secchar == 'R') {
					FP(start_r) = atof(argpos2);
					MAKEFP(start_r) = FIEEE(MAKEFP(start_r));
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				if (secchar == 'I') {
					FP(start_i) = atof(argpos2);
					MAKEFP(start_i) = FIEEE(MAKEFP(start_i));
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				if (secchar == 'P' || secchar == 'C') {
					fp = fopen(argpos2,"w");
					if (fp == NULL) {
						printf("Cannot open file '%s'\n",argpos2);
						goto command;
					}
					if (secchar == 'P') {
						fprintf(fp,"SR");
						prtflt(CNVFLT(MAKEFP(start_r)),fp);
						fprintf(fp,"\nER");
						prtflt(CNVFLT(MAKEFP(end_r)),fp);
						fprintf(fp,"\nSI");
						prtflt(CNVFLT(MAKEFP(start_i)),fp);
						fprintf(fp,"\nEI");
						prtflt(CNVFLT(MAKEFP(end_i)),fp);
						fprintf(fp,"\nMX%d\n",max_x);
						fprintf(fp,"MY%d\n",max_y);
						fprintf(fp,"MM%d\n",max_mem_y);
						fprintf(fp,"MC%d\n",max_count);
						fprintf(fp,"F%d\n",func_num);
					}
					fprintf(fp,"CI%d\n",color_inc);
					fprintf(fp,"CO%d\n",color_offset);
					fprintf(fp,"CS%d\n",color_set);
					fprintf(fp,"CM%d\n",color_mode);
					fprintf(fp,"CD%d\n",color_div);
					fprintf(fp,"CT%d\n",color_inset);
					for (i=0;i<32;i++)
						fprintf(fp,"CR %d %d %d %d\n",i,(UserPalette[i] >> 8) & 0xf,
						(UserPalette[i] >> 4) &0xf, UserPalette[i] & 0xf);
					fclose(fp);
					goto command;
				}
				if (secchar == 'H') {
					CurrentSettings();
					goto command;
				}
				if (secchar == 'A' || secchar == 'Q') {
					if (v_fp == NULL) {
						printf("Must <G>enerate or <LO>ad first!\n");
						goto command;
					}
					fp = fopen(argpos2,"w");
					if (fp == NULL) {
						printf("Cannot open file '%s'\n",argpos2);
						goto command;
					}
					if (secchar == 'A')
						putc((char) 1,fp);
					else
						putc((char) 2,fp);
					dtemp = (double)CNVFLT(MAKEFP(start_r));
					fwrite(&dtemp,sizeof(dtemp),1,fp);
					dtemp = (double)CNVFLT(MAKEFP(end_r));
					fwrite(&dtemp,sizeof(dtemp),1,fp);
					dtemp = (double)CNVFLT(MAKEFP(start_i));
					fwrite(&dtemp,sizeof(dtemp),1,fp);
					dtemp = (double)CNVFLT(MAKEFP(end_i));
					fwrite(&dtemp,sizeof(dtemp),1,fp);
					fwrite(&max_x,sizeof(max_x),1,fp);
					fwrite(&max_y,sizeof(max_y),1,fp);
					want_read = TRUE;
					for (y=0;y<max_y;y++) {
						v_pos_line(y);
						if (secchar == 'A')
							if (fwrite(v_mand_store+(y-v_starty)*max_x,
							max_x*sizeof(UWORD),1,fp) != 1)
								goto badsave;
							else
								;
						else {
							data1 = ~0;
							for (i=0;i<max_x;i++) {
								data2 = *(v_mand_store+(y-v_starty)*max_x+i);
								if (fwrite(&data2,sizeof(data2),1,fp) != 1)
									goto badsave;
								if (data1 == data2) {
									for (x=i+1;x<max_x;x++)
										if (*(v_mand_store+(y-v_starty)*max_x+x) != data1)
											break;
									repcount = x-i-1;
									if (fwrite(&repcount,sizeof(repcount),1,fp) != 1)
										goto badsave;
									i=x-1;
									data1 = ~0;
								}
								else
									data1 = data2;
							}
						}
					}
					fclose(fp);
					goto command;
badsave:
					printf("Error while writing to file!\n");
					fclose(fp);
					goto command;
				}
				ill_cmd();
				goto command;
			case 'E':
				if (secchar == 'R') {
					FP(end_r) = atof(argpos2);
					MAKEFP(end_r) = FIEEE(MAKEFP(end_r));
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				if (secchar == 'I') {
					FP(end_i) = atof(argpos2);
					MAKEFP(end_i) = FIEEE(MAKEFP(end_i));
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				ill_cmd();
				goto command;
			case 'M':
				if (secchar == 'X') {
					sscanf(argpos2,"%d",&temp);
					if (temp < 30 ||
					(temp > 320 && !(color_mode & 4)) ||
					(temp > 640 && (color_mode & 4))) {
						printf("Illegal maximum X value!\n");
						goto command;
					}
					max_x = temp;
					max_mem = max_mem_y * MAXX;
					max_mem /= max_x;
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				if (secchar == 'Y') {
					sscanf(argpos2,"%d",&temp);
					if (temp < 30 ||
					(temp > 200-STARTY && !(color_mode & 2)) ||
					(temp > 400-STARTY && (color_mode & 2))) {
						printf("Illegal maximum Y value!\n");
						goto command;
					}
					max_y = temp;
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				if (secchar == 'C') {
					sscanf(argpos2,"%d",&temp);
					if (temp * color_inc + color_offset > 4095) {
						printf("More than 4096 colors!\n");
						goto command;
					}
					if (temp < 2) {
						printf("MaxCount must be greater than 1\n");
						goto command;
					}
					max_count = temp;
					goto command;
				}
				if (secchar == 'M') {
					sscanf(argpos2,"%d",&temp);
					if (temp < 10) {
						printf("Number of virtual lines must be >9!\n");
						goto command;
					}
					FreeMem(v_mand_store,MAXX*max_mem_y*sizeof(UWORD));
					v_mand_store = (UWORD *)AllocMem(MAXX*temp*sizeof(UWORD),MEMF_CHIP|MEMF_PUBLIC);
					if (v_mand_store == NULL) {
						printf("Can't allocate that much memory for set storage");
						v_mand_store = (UWORD *)AllocMem(MAXX*max_mem_y*sizeof(UWORD),MEMF_CHIP|MEMF_PUBLIC);
						if (v_mand_store == NULL)
							abort("Can't reallocate memory!!!");
						goto command;
					}
					max_mem_y = temp;
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					max_mem = MAXX * max_mem_y;
					max_mem /= max_x;
					goto command;
				}
				ill_cmd();
				goto command;
			case 'L':
				if (secchar == 'O') {
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					fp = fopen(argpos2,"r");
					if (fp == NULL) {
						printf("Cannot open file '%s'\n",argpos2);
						goto command;
					}
					filetype = getc(fp);
					if (filetype != 1 && filetype != 2) {
						printf("File is not in proper format\n");
						fclose(fp);
						fp = NULL;
						goto command;
					}
					if (fread(&dtemp,sizeof(dtemp),1,fp) != 1)
						goto bad_load;
					FP(ftemp) = CNVSNG(dtemp);
					MAKEFP(start_r) = FIEEE(MAKEFP(ftemp));
					if (fread(&dtemp,sizeof(dtemp),1,fp) != 1)
						goto bad_load;
					FP(ftemp) = CNVSNG(dtemp);
					MAKEFP(end_r) = FIEEE(MAKEFP(ftemp));
					if (fread(&dtemp,sizeof(dtemp),1,fp) != 1)
						goto bad_load;
					FP(ftemp) = CNVSNG(dtemp);
					MAKEFP(start_i) = FIEEE(MAKEFP(ftemp));
					if (fread(&dtemp,sizeof(dtemp),1,fp) != 1)
						goto bad_load;
					FP(ftemp) = CNVSNG(dtemp);
					MAKEFP(end_i) = FIEEE(MAKEFP(ftemp));
					if (fread(&max_x,sizeof(max_x),1,fp) != 1)
						goto bad_load;
					if (fread(&max_y,sizeof(max_y),1,fp) != 1)
						goto bad_load;
					if (max_x > 320)
						color_mode |= (HIRES_MODE|NOT_HOLDANDMODIFY);
					if (max_y > 200)
						color_mode |= INTERLACE_MODE;
					max_mem = max_mem_y * MAXX;
					max_mem /= max_x;

					if (filetype == 1) {
						v_offset = ftell(fp);
						v_starty = 1;
						v_fp = fp;
						fp = NULL;
						modified = FALSE;
					}
					else {
						v_fp = fopen(TEMPNAME,"w+");
						v_offset = 0L;
						v_starty = 1;
						modified = FALSE;
						want_read = FALSE;
						if (v_fp == NULL)
							abort("Can't open temporary file!");
						for (y=0;y<max_y;y++) {
							v_pos_line(y);
							modified = TRUE;
							if (fread(&data1,sizeof(data1),1,fp) != 1)
								goto bad_load;
							*(v_mand_store+(y-v_starty)*max_x) = data1;
							for (x=1;x<max_x;) {
								if (fread(&data2,sizeof(data2),1,fp) != 1)
									goto bad_load;
								*(v_mand_store+(y-v_starty)*max_x+x++) = data2;
								if (data1 == data2) {
									if (fread(&repcount,sizeof(repcount),1,fp) != 1)
										goto bad_load;
									if (repcount)
										for (i=0;i<repcount;i++)
											*(v_mand_store+(y-v_starty)*max_x+x++) =
											data2;
									data1 = -1;
								}
								else
									data1 = data2;
							}
						}
						fclose(fp);
						rewind(v_fp);
						modified = TRUE;
					}
					want_read = TRUE;
					v_pos_line(0);
					goto command;
				}
				ill_cmd();
				goto command;
bad_load:
				if (v_fp) {
					fclose(v_fp);
					v_fp = NULL;
				}
				fclose(fp);
				fp = NULL;
				printf("Error reading file!\n");
				goto command;
			case 'X':
				if (secchar == 'R') {
					FP(scale) = atof(argpos2);
					MAKEFP(scale) = FIEEE(MAKEFP(scale));
					MAKEFP(start_r) = ADD(MAKEFP(start_r),MAKEFP(scale));
					MAKEFP(end_r) = ADD(MAKEFP(end_r),MAKEFP(scale));
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				if (secchar == 'I') {
					FP(scale) = atof(argpos2);
					MAKEFP(scale) = FIEEE(MAKEFP(scale));
					MAKEFP(start_i) = ADD(MAKEFP(start_i),MAKEFP(scale));
					MAKEFP(end_i) = ADD(MAKEFP(end_i),MAKEFP(scale));
					if (v_fp) {
						fclose(v_fp);
						v_fp = NULL;
					}
					goto command;
				}
				ill_cmd();
				goto command;
			case 'Z':
				if (secchar != 'R' && secchar != 'I' && secchar != 'B') {
					ill_cmd();
					goto command;
				}
				if (v_fp) {
					fclose(v_fp);
					v_fp = NULL;
				}
				if (secchar != 'I' ) {
					/* scale along the real axis */
					FP(scale) = atof(argpos2);
					ZoomAlongDarling(FIEEE(MAKEFP(scale)), FLT(1));
				}
				if (secchar != 'R') {
					/* scale along the complex axis */
					FP(scale) = atof(argpos2);
					ZoomAlongDarling(FLT(1), FIEEE(MAKEFP(scale)));
				}
				goto command;
			case 'C':
				if (secchar == 'R') {
					sscanf(argpos2,"%d %d %d %d",&temp,&t1,&t2,&t3);
					if (temp < 0 || temp > 31) {
						printf("Color number out of range!\n");
						goto command;
					}
					UserPalette[temp] = (t1 << 8) | (t2 << 4) | t3;
					goto command;
				}
				if (secchar == 'I') {
					sscanf(argpos2,"%d",&temp);
					if (max_count * temp + color_offset > 4095) {
						printf("More than 4096 colors!\n");
						goto command;
					}
					if (temp < 1) {
						printf("Increment must be greater than 0!\n");
						goto command;
					}
					color_inc = temp;
					goto command;
				}
				if (secchar == 'O') {
					sscanf(argpos2,"%d",&temp);
					if (max_count * color_inc + temp > 4095) {
						printf("More than 4096 colors!\n");
						goto command;
					}
					if (temp < 0) {
						printf("Negative offset illegal!\n");
						goto command;
					}
					color_offset = temp;
					goto command;
				}
				if (secchar == 'S') {
					sscanf(argpos2,"%d",&temp);
					if (temp < 0 || temp > 2) {
						printf("Illegal color set!\n");
						goto command;
					}
					color_set = temp;
					init_colors();
					goto command;
				}
				if (secchar == 'M') {
					sscanf(argpos2,"%d",&temp);
					if (temp < 0 || temp > 7 || ((temp&4) && !(temp&1))) {
						printf("Illegal graphics mode!\n");
						goto command;
					}
					color_mode = temp;
					if (!(color_mode & 0x2))
						if (max_y > 200-STARTY) {
							if (v_fp) {
								fclose(v_fp);
								v_fp = NULL;
							}
							max_y = 200-STARTY;
						}
						if (!(color_mode & 0x4))
							if (max_x > 320) {
								if (v_fp) {
									fclose(v_fp);
									v_fp = NULL;
								}
								max_x = 320;
								max_mem = max_mem_y * MAXX;
								max_mem /= max_x;
							}
							goto command;
				}
				if (secchar == 'D') {
					sscanf(argpos2,"%d",&temp);
					if (temp < 1) {
						printf("Divisor must be greater than 0!\n");
						goto command;
					}
					color_div = temp;
					goto command;
				}
				if (secchar == 'T') {
					sscanf(argpos2,"%d",&temp);
					if (temp < 0 || temp > 4095) {
						printf("Color must be between 0 and 4095!\n");
						goto command;
					}
					color_inset = temp;
					goto command;
				}
				ill_cmd();
				goto command;
			case 'F':
				sscanf(argpos,"%d",&temp);
				if (temp < 0 || temp > 1) {
					printf("Function number must be 0 or 1!\n");
					goto command;
				}
				func_num = temp;
				if (v_fp) {
					fclose(v_fp);
					v_fp = NULL;
				}
				goto command;
			case 'P':
				sscanf(argpos,"%d",&temp);
				sprintf(inp_buf,"< Preset%d",temp);
				goto proc_command;
			case 'D':
				if (v_fp == NULL) {
					printf("Must <G>enerate or <LO>ad first!\n");
					goto command;
				}
				if (disp_mand() == NULL)
					wait_close();
				goto command;
			case 'G':
				if (gen_mand() == NULL)
					wait_close();
				goto command;
			case ';':
				goto command; /* Lattice will complain about this line! */
			case '<':
				if (redir_fp) {
					fclose(redir_fp);
					redir_fp = NULL;
				}
				redir_fp = fopen(argpos,"r");
				if (redir_fp == NULL)
					printf("Cannot open file '%s'\n",argpos);
				goto command;
			case 'Q':
				abort(NULL);
			case '?':
				AvailableCommands();
				goto command;
		}
	}
}

ill_cmd()
{
	printf("Unknown command!\n");
}

#ifndef DOUBLEPREC

float cnvf(i)
int i;
{
	union kludge n;

	n.i = i;
	n.i = SPTieee(n.i);
	return (n.f);
}

#endif

/*-----------------------------------------*/
/* Non-intelligent virtual memory handling */

v_pos_line(l)
int l;
{
	if (l < v_starty) {
		if (modified)
			v_flush();
		v_starty = 0;
		fseek(v_fp,v_offset,0);
		if (want_read)
			fread(v_mand_store,max_x*sizeof(UWORD),max_mem,v_fp);
	}
	if (l-v_starty > max_mem-1) {
		if (modified)
			v_flush();
		v_starty += max_mem;
		fseek(v_fp,(long)(v_starty*max_x*sizeof(UWORD)+v_offset),0);
		if (want_read)
			fread(v_mand_store,max_x*sizeof(UWORD),max_mem,v_fp);
	}
	return (0);
}

v_flush()
{
	fseek(v_fp,(long)(v_starty*max_x*sizeof(UWORD)+v_offset),0);
	fwrite(v_mand_store,max_x*sizeof(UWORD),max_mem>max_y?max_y:max_mem,v_fp);
	modified = FALSE;
	return (0);
}

abort(s)
char *s;
{

	CloseDisplay();

#ifndef DOUBLEPREC
	if (MathTransBase)
		CloseLibrary(MathTransBase);
	if (MathBase)
		CloseLibrary(MathBase);
#endif
	if (console)
		fclose(console);
	if (v_fp)
		fclose(v_fp);
	if (redir_fp)
		fclose(redir_fp);
	if (GfxBase)
		CloseLibrary(GfxBase);
	if (IntuitionBase)
		CloseLibrary(IntuitionBase);
	if (IconBase)
		CloseLibrary(IconBase);
	if (v_mand_store)
		FreeMem(v_mand_store,max_mem_y*MAXX*sizeof(UWORD));
	if (color_table)
		FreeMem(color_table,4096*sizeof(UWORD));

	DeleteFile(TEMPNAME);

	if (s)
		fprintf(stderr,"%s\n",s);

	exit(0);
}


InitParams()
{
	max_x = 320;
	max_y = 200;
	max_count = 50;
	color_inc = 2;
	color_set = 1;
	color_mode = 1;
	color_div = 1;
	func_num = 0;
	fl_assign(&FP(start_r),(double)-.5);
	MAKEFP(start_r) = FIEEE(MAKEFP(start_r));
	fl_assign(&FP(end_r),(double)2.0);
	MAKEFP(end_r) = FIEEE(MAKEFP(end_r));
	fl_assign(&FP(start_i),(double)-1.25);
	MAKEFP(start_i) = FIEEE(MAKEFP(start_i));
	fl_assign(&FP(end_i),(double)1.25);
	MAKEFP(end_i) = FIEEE(MAKEFP(end_i));
	ZoomCenterX = max_x >> 1;
	ZoomCenterY = max_y >> 1;
	ZoomBoxSizeX = max_x;
	ZoomBoxSizeY = max_y;
	max_mem = max_mem_y * MAXX / max_x;
	init_colors();
}

fl_assign(f,v)  /* Kludge to fix bug in Lattice 3.02 */
#ifdef DOUBLEPREC
double *f;
#else
float *f;
#endif
double v;
{
	*f = v;
}

char *stopblnk(cp)
char *cp;
{
	while (isspace(*cp))
		cp++;
	return(cp);
}

prtflt(f,fp)
#ifdef DOUBLEPREC
double f;
#else
float f;
#endif
FILE *fp;
{
#ifdef MANX
	char bfr[25];

#ifdef DOUBLEPREC
	ftoa((double) f,bfr,17);
#else
	ftoa((double) f,bfr,7);
#endif
	if (fp == stdout)
		printf("%s",bfr);
	else
		fprintf(fp,"%s",bfr);
#endif
#ifdef LATTICE
	fprintf(fp,PERCFLT,f);
#endif
}

iabs(v)
int v;
{
	if (v < 0)
		return (-v);
	else
		return (v);
}
