/*	attract9.c

	This  is  a  program  to  show the 1D dynamics of logistic maps,
	the 2D dynamics of Henon attractors, the 3D dynamics of the
	Lorenz attractor, and the 2D dynamics of the Yorke attractors.

*/

#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <math.h>
#include <alloc.h>
#include <time.h>
#include "attract.h"
#include <dir.h>

/* The	basic  operation  of the  program is that I have a flock of at
most MAXFLOCK  point atom[i] which are 1, 2 or 3 dimensional depending
on whether I am using logistic, Yorke, or Lorenz system. During each
step, each atom[i] is replaced by its image under one of dynamical
systems.  In the case of Lorenz I draw a line between the old
and new positions.
 */

/* The lorenz attractor is generated by the dynamics of a set of three differential equations:

dx = s * (y - x) * dt;
dy = ( r * x - y - x * z ) * dt;
dz = b * z + x * y;

The canonical values of s, r and b are */

char frstring[20];
int stopped = false;
int onestep = false;

/* This works OK with the euler algorithm, but looks much more correct with Runge-Kutte.  According to Francis Moon, Chaotic Vibrations, (John Wiley 1987), p. 271, if you use R-K you can
get interesting "intermittency" by having s and b as before, but by then twiddling r between 166.0 and 167.0, with bursts of chaotic noise starting at r = 166.1. I had this in, but its not very interesting.*/

/* In 2D I do the henon map, see Moon p. 272 */
double fha = (double) (1.2 * 4.0) / PI; /* 1.2 is "true" value */
double cosa, sina;


/* finally I do the Yorke map as well */

double omega1 = 0.48566516831488;
double omega2 = 0.90519373301868;
double epsilon = 0.5;
double epsbar = EPSBAR1;	/* epsilon / 2 pi */
int locked = true;
int trihedrons_exist = true;

/*  The  program  works  by tracing the orbits of a flock of atoms.  I
   store an atom's color for use in determining its next color.  I use
   several  different  kinds  of  flocks of atoms, the largest being a
   cube of 10x10x10 atoms.*/

/*--------------Global Variables, General-----------*/

double flox, fhix, floy, fhiy, floz, fhiz;
double xscale, yscale, zscale, xyscale;
int prog_init = 0;

/* View volume is given as double. We maintain it during pans and zooms.*/
double pcentx, pcenty, pcentz, pcentfly;	/* center of projection on
						 * axis */
double pcanvasz;		/* plane of coord on axis, only need if
				 * nonzero */
int mode = 3, maxx, maxy;

double zoomfactor = 2.0;

int minx;
double xscreentostamp, yscreentostamp;
int stamprxmin, stamprxmax, stamprymin, stamprymax;
unsigned char maxcolor;
double dt = STARTDT, dt2 = STARTDT / 2.0, dt6 = STARTDT / 6.0;
int lorenzfreq = 1.0 / STARTDT;
double fxstep, fystep, fx, savefx, lvfx = STARTBEAT;
int pixelx, savepixelx;
int standardstartlog = 1, standardstoplog = 16, startlog, stoplog;
double fdeltax, fdeltay, fdeltaz, fcenterx, fcentery, fcenterz;
double fdeltax_div_64K, fdeltay_div_64K, fdeltaz_div_64K;
double startdeltax, startdeltay, startdeltaz;
double startcenterx, startcentery, startcenterz;
int cursoron;			/* 0 no cursor on screen, 1 cursor  on screen */
int cursorshape = 1;		/* 0 square cursor, 1 triangle cursor */
int logisticlaunchflag;
int cleanflag, loghumpselectflag;
double logstartval = LOGSTART;
int curxinc, curyinc, curx, cury;
double fcurx, fcury;
unsigned int cursoroffset;
unsigned char cursorpatch[64];
int cursorcolor = 0xf00;
int mouseflag = 1;		/* assume mouse present, set flag to 0 if
				 * none */
int singlestepflag;
int stepme;
int delayfactor;
int colorcycleflag = 0; 	/* 1 means change palette with each refresh */
int curspeed = 8, palettenumber = 0, spinflag = 0;
int openflag = 0;		/* 1 allows pans, 0 disables them */
int grayflag = 0, monoflag = 0;
view logisticview;
int hump[640], line[640];
fflockstruct1 *fflock1ptr;
fflockstruct2 *fflock2ptr;
fflockstruct2 *saved_fflock2ptr;
fflockstruct3 *fflock3ptr;
fflockstruct3 *saved_fflock3ptr;
int flocktype = 1;
int savepalnum; 		/* save old palette number during random pal */
int start_shiftregister;

 /*
  * 0 is 1 atom, 1 is 16, 25 or 27 atoms, 2 is 64, 64, or 64 more closely
  * spaced atoms.
  */
unsigned int iteration = 0;
unsigned long long_iteration = 0L;


 /*
  * counter used to time keyboard check and to pause cursor input after a pan
  */
char axis = 'y';
int dimension = LORENZ; 	/* 1 for logistic, 2 for henon, 3 for lorenz,
				 * 4 yorke */
int exitflag = 0, fancyflag = 0, trihedroncount = 3;
double humpshift = 1.0;
double humpspot = 0.5;
int tracetype = 1;		/* 0 is line, 1 is 1D ribbon in dimension 1
				 * and 3 0 is logistic, 1 is pop graph, 2 is
				 * curve jumping in dimension 1; and 0 is
				 * ribbon and 1 is permanent in dim 3 */
int tritracetype = 1;		/* 0  is point, 1 is line 2 is ribbon for
				 * trihedron */
screenflock *ribbon[MAXRIBBONLENGTH];	/* flocks of screen points */
int ribbonlength = 40;
int ribbonindex;
int ribbonfull;
ftriple newdr, oldtangent[MAXFLOCK];
int trihedronon;
pair ttip[MAXFLOCK], ntip[MAXFLOCK], bntip[MAXFLOCK];
pair antittip[MAXFLOCK], antintip[MAXFLOCK], antibntip[MAXFLOCK];
int yorkenumber;
int soundflag, yorketopologyflag;
int ParameterDisplayMode = 0;	/* 0 status line off, 1 status line on */

/* yorketopologyflag is 0,1,2 as yorke square is a torus, a klein
bottle (flip x's), or projective plane (flip x's & y's at odd wrap).*/
int lorenzflyflag;
ftriple flyspot, viewspot;
ftriple flytangent, flynormal, flybinormal;
ftriple viewtangent, viewnormal, viewbinormal;
int Stamping = 1;
int colorbarflag = 0;

/* ----------------Metashell thingies------------------*/
int CommPort = 0;
metaPort *thePort;
rect sR;
int FontHeight;
int StringWidthX;
int MenuBackColor = LIGHTGRAY;
int MenuTextColor = WHITE;
int ButtonBackColor = DARKGRAY, ButtonFrameColor = BLACK, ButtonTextColor = WHITE;
int ct_flag = 0, saved_ct = 0;
int minimum_luminosity = 50;
unsigned long next_release = 0L;
int starting_trihedroncount;
void fourtones(unsigned char clut[16][3]);
unsigned char defaultcolortable[16][3];
rect stampingR[6];
rect dR = {-1, 0, 639, 479};
rect stampR = {-1, 420, 78, 479};

/*-----------------------Main Function-------------*/

int main(int argc, char **argv)
{
	int i;
	int gif_ok = 1;
	extern int disk_error_handler(int errval, int ax, int bp, int si);
	int ok = true;
	char *p;
   long biggest_thing=0L;

	mode = 0;

	harderr(disk_error_handler);


	while (argc > 1)
	{
		if (argv[1][0] == '-')
		{
			switch (argv[1][1])
			{
			case 'e':
				mode = 0x10;
				break;
			case 'l':
				minimum_luminosity = atoi(&argv[1][2]);
				break;
			case 'b':
				colorbarflag = 1;
				break;

			case 'd':
				if (!stricmp(&argv[1][2], "LORENZ"))
					dimension = LORENZ;
				else if (!stricmp(&argv[1][2], "LOGISTIC"))
					dimension = LOGISTIC;
				else if (!stricmp(&argv[1][2], "YORKE"))
					dimension = YORKE;
				else if (!stricmp(&argv[1][2], "HENON"))
					dimension = HENON;

				switch (dimension)
				{
				case HENON:
				case YORKE:
					ribbonlength = 3;
					fancyflag = 1;
					tracetype = 0;
					flocktype = (dimension == YORKE) ? 1 : 2;
					break;
				case LORENZ:
					flocktype = 1;
					ribbonlength = 64;
					tracetype = 1;
					break;
				case LOGISTIC:
					cursorshape = 1;
					tracetype = 0;
					logstartval = LOGSTART;
					logisticlaunchflag = 1;
					break;
				}

				break;
			}
		}
		argv++, argc--;
	}

	iteration = 0;

	randomize();

	for (i = 0; i < MAXRIBBONLENGTH; i++)
   {
      safe_alloc = true;
		ok &= ((ribbon[i] = malloc(sizeof(screenflock))) != NULL);
   }
   safe_alloc = true;
	ok &= ((fflock3ptr = malloc(sizeof(fflockstruct3))) != NULL);
   safe_alloc = true;
	ok &= ((saved_fflock3ptr = malloc(sizeof(fflockstruct3))) != NULL);
   safe_alloc = true;
	ok &= ((fflock2ptr = malloc(sizeof(fflockstruct2))) != NULL);
   safe_alloc = true;
	ok &= ((saved_fflock2ptr = malloc(sizeof(fflockstruct2))) != NULL);
   safe_alloc = true;
	ok &= ((fflock1ptr = malloc(sizeof(fflockstruct1))) != NULL);
   safe_alloc = true;
	ok &= ((logsaviour = farmalloc(560 * 30 * 2L)) != NULL);

   /* Save the stamp stuff here as well. */
   biggest_thing = sizeof(fflockstruct3);
   biggest_thing = max(biggest_thing,sizeof(fflockstruct2));
   biggest_thing = max(biggest_thing,sizeof(fflockstruct1));


	/* Lets also make sure that we have a safety cushion to operate with */

	ok &= (memok(4*biggest_thing));

	if (!ok)
	{
		fprintf(stderr, "\n\nSorry, not enough memory to run ATTRACT.\n");
		exit(-1);
	}

	/* Lets warn the user if there is not enough memory to later do a Gif */

	if (!memok(20712L		 /* Added up mallocs in comprs.c */
		   + 4*biggest_thing))
		gif_ok = 0;

	if (!mode)
		mode = detectmode();
	if (mode != 0x10 && mode != 0x12 && mode != -1)
	{
		printf("Error: This program requires either EGA or VGA. Aborting.\n");
		exit(-1);
	}
	if (mode != -1)
		i = InitGrafix(mode == 0x12 ? -EGA640x480 : -EGA640x350);
	if (i != 0 || mode == -1)
	{
		printf("Error: Metagraphics not installed. Aborting.\n");
		exit(-1);
	}

	p = searchpath((mode == 0x10) ? "system16.fnt" : "system12.fnt");
	if (p)
		LoadFont(p);


	CommPort = QueryComm();

	if (CommPort & MsDriver)
		CommPort = MsDriver;
	else if (CommPort & 2)
		CommPort = MsCOM2;
	else if (CommPort & 1)
		CommPort = MsCOM1;
	if (CommPort)
		InitMouse(CommPort);

	/* We're going to event-drive the whole thing */
	EventQueue(true);

	prog_init = 1;		/* fatal alloc errors will now call
				 * StopEvent() and StopMouse()	*/

	/* get the time please */
	installmode();		/* need to do this here to set maxx maxy */
	fourtones(defaultcolortable);
	LoadDefaultPalette("ATTRACT.PAL");
	palettenumber = 0;
	Jusepalette();
	DrawButtons();
	TrackCursor(true);
	LimitMouseRect(&sR);


	setwindow(1);
	fillflock();
	fflock3ptr->n = saved_fflock3ptr->n = 9;
	starting_trihedroncount = trihedroncount;
	resetcursor();		/* center the cursor and turn it on. */
	drawcursor();

	if (!gif_ok)
		ErrorBox("There may not be enough memory to save or view a Gif.");

	while (exitflag == 0)
	{

		if (!stopped || onestep)
			lstep();

		onestep = 0;
		delay(delayfactor);
		iteration++;
		long_iteration++;
		if (long_iteration == next_release)
		{
			if (dimension == HENON)
			{
				fflock2ptr->n++;
				if (fflock2ptr->atom[fflock2ptr->n + 1].x != -9999)
					next_release = fflock2ptr->releasetime[fflock2ptr->n + 1];
				else
					next_release = 0;
				sound(55);
				delay(10);
				nosound();
			}
			else if (dimension == LORENZ)
			{
				ftriple fw;
				pair p;
				int n;


				n = fflock3ptr->n++;
				if (fflock3ptr->atom[fflock3ptr->n].x != -9999)
					next_release = fflock3ptr->releasetime[fflock3ptr->n];
				else
					next_release = 0;

				/*
				 * make sure theres not a ribbon for this
				 * fucker
				 */
				/*
				 * Fake it out by filling all of the
				 * ribbonindices with the right data for this
				 * guy.
				 */

				fw = fflock3ptr->atom[n];
				p = projectpixel(&fw, axis);
				for (i = 0; i < ribbonlength; i++)
					ribbon[i]->atom[n] = p;
				if (trihedrons_exist)
					trihedroncount++;
				sound(55);
				delay(10);
				nosound();



			}
		}


		checkkeyboard();

		if (spinflag && !(iteration & 3))
		{
			if (spinflag == 1)
				Jspinpalette();
			else
				revJspinpalette();
		}

	}
	grayflag = 0;
	grayscale();
	Jusepalette();
	restoretextmode();
	StopEvent();
	StopMouse();
	return exitflag;
}


/*------------------Mouse and Keyboard Functions-------------*/

void resetcursor()
{
	if (curx < minx)
		HideCursor();

	curx = minx + maxx / 2;
	cury = maxy / 2;
	MoveCursor(curx, cury);
	drawcursor();
}

void updatecursor()
{
	int oldcurx, oldcury, panx = 0, pany = 0;

	if (iteration < 10)
	{			/* dopan sets iteration to 0 */
		curxinc = 0;
		curyinc = 0;
		return;
	}
	oldcurx = curx;
	oldcury = cury;
	curx += curxinc;
	cury += curyinc;

	if (!mousemove)
	{
		if (curyinc && curx < minx && current_main_item != -1)
			LockToClosestUpdown();
		else if (curxinc < 0 && curx >= minx && curx <= minx + 7)
			LockToClosestLeft();
		else if (curx < minx && current_main_item != -1)
		{
			if (curxinc < 0)
			{
				curx = oldcurx;
				cury = oldcury;
			}
			else if (curxinc > 0)
			{
				curx = minx + 8;
			}
		}


	}

	curxinc = 0;
	curyinc = 0;



	if (curx < minx + 7)
		panx = -1;
	if (curx > (minx + maxx - 7))
		panx = 1;
	if (cury < 7)
		pany = 1;
	if (cury > (maxy - 7))
		pany = -1;
	if (panx | pany)
	{
		if (openflag)
		{
			dopan(panx, pany);
			return;
		}
		else
		{		/* if screen not open, reject offscreen move */
			if (panx == 1)
				curx = oldcurx;
		}
	}
	MoveCursor(curx, cury);
	MaybeHiliteSomething();
	if (curx < minx)
	{
		if (cursoron)
			ArrowCursor();
	}
	else
	{
		if (!cursoron)
			HideCursor();
		drawcursor();
	}
}





