/************************************************************************
*  Version 3.00  MANDELBROT - Self-Squared Dragon Generator  29-May-86  *
*  Commodore Amiga           Control Routines                  MAND3.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"

int lines;

struct SpriteImage
{
	UWORD posctl[2];
	UWORD sprdata[2][15];
	UWORD reserved[2];
} newpointer = {
	{	0, 0 },
	{	0x0100, 0,
		0, 0,
		0x0100, 0,
		0, 0,
		0x0100, 0,
		0, 0,
		0, 0,
		0xa82a, 0,
		0, 0,
		0, 0,
		0x0100, 0,
		0, 0,
		0x0100, 0,
		0, 0,
		0x0100, 0 },
	{	0, 0 }
};


open_winds()
{
	int i,color;

	nw.Width = max_x;
	nw.Height = max_y+STARTY;

	if (color_mode & NOT_HOLDANDMODIFY) {
		ns.ViewModes = NULL;
		ns.Depth = 5;
	}
	else {
		ns.ViewModes = HAM;
		ns.Depth = 6;
	}

	if (color_mode & INTERLACE_MODE) {
		ns.Height = 400;
		ns.ViewModes |= INTERLACE;
	}
	else
		ns.Height = 200;

	if (color_mode & HIRES_MODE) {
		ns.Width = 640;
		ns.ViewModes |= HIRES;
		ns.Depth = 4;
	}
	else
		ns.Width = 320;

	screen = (struct Screen *)OpenScreen(&ns);
	if (screen == NULL) {
		printf("Can't open new screen!\n");
		return (1);
	}

	ShowTitle(screen,FALSE);
	title_displayed = 0;

	nw.Screen = screen;
	w = (struct Window *)OpenWindow(&nw);
	if (w == NULL) {
		CloseScreen(screen);
		printf("Can't open new window!\n");
		return (1);
	}

	init_menus();
	enable_all();
	reset_draw();

	SetMenuStrip(w,MainMenu);

	vp = &screen->ViewPort;
	rp = w->RPort;

	SetDrMd(rp,JAM1);
	SetBPen(rp, 0);

	/* leave registers 0 and 1 alone, set two to black, set the others
	* according to the user's design
	*/
	SetRGB4(vp, 0, (UserPalette[0] >> 8) & 0xf, (UserPalette[0] >> 4) & 0xf, UserPalette[0] & 0xf);
	SetRGB4(vp, 1, (UserPalette[1] >> 8) & 0xf, (UserPalette[1] >> 4) & 0xf, UserPalette[1] & 0xf);
	SetRGB4(vp, 2, (UserPalette[2] >> 8) & 0xf, (UserPalette[2] >> 4) & 0xf, UserPalette[2] & 0xf);
	for (i = 3; i <= 31; i++) {
		UserPalette[i] =
		color = *(color_table + color_offset + (i - 2) * color_inc);
		SetRGB4(vp, i, (color >> 8) & 0xf, (color >> 4) & 0xf, color & 0xf);
	}

	SetPointer(w,&newpointer,15,15,-8,-7);

	return (0);
}

void wait_close()
{
	ULONG class;
	USHORT code;

	if (redir_fp) {
		Delay(600);
		CloseDisplay();
		return;
	}

	SettingCenter = SettingBoxSize = FALSE;
	threed_stat = 0;

	reset_menus();

	for (EVER) {
		Wait(1 << w->UserPort->mp_SigBit);
		while (message = (struct IntuiMessage *)GetMsg(w->UserPort)) {
			class = message->Class;
			code  = message->Code;
			ReplyMsg(message);

			switch (class) {
				case MENUPICK:
					switch MENUNUM(code) {
						case MENU_PROJECT:
							if (ProjectMenu(code))
								return;
							break;
						case MENU_OPTIONS:
							if (DisplayMenu(code))
								return;
							break;
						case MENU_ZOOM:
							if (ZoomMenu(code))
								return;
							break;
					}
					break; /* breaks MENUPICK switch statement */
				case INTUITICKS:
					code = NULL;
				case MOUSEBUTTONS:
					if (SettingCenter) {
						DrawZoomCenter();
						if (code == SELECTDOWN) {
							SettingCenter = FALSE;
							DrawDisplayCenter();
						}
						else {
							ZoomCenterX = w->MouseX;
							ZoomCenterY = w->MouseY;
							DrawZoomCenter();
						}
					}
					else
						if (SettingBoxSize) {
							DrawZoomBox();
							if (code == SELECTDOWN)
								SettingBoxSize = FALSE;
							else {
								RecalcZoomBox();
								DrawZoomBox();
							}
						}
						break;
			}
		}
	}
}


anal_mand()
{
	FLOATSTRUCT x_gap,y_gap,z_r,z_i,u_r,u_i,const0,const1,const4;
	FLOATSTRUCT temp;
	int count,x,y,width,height,last_x,last_y,select,refresh;
	ULONG class;
	USHORT code;

	disable_some(~0,~0,~0);

	MAKEFP(x_gap) = DIV(FLT(max_x),SUB(MAKEFP(start_r),MAKEFP(end_r)));
	MAKEFP(y_gap) = DIV(FLT(max_y),SUB(MAKEFP(start_i),MAKEFP(end_i)));

	MAKEFP(const0) = FLT(0);
	MAKEFP(const1) = FLT(1);
	MAKEFP(const4) = FLT(4);

	last_x = -1;
	last_y = -1;
	select = 0;

	aw.Screen = screen;
	w2 = (struct Window *)OpenWindow(&aw);
	if (w2 == NULL) {
		CloseDisplay();
		printf("Can't open analyzing window\n");
		return (1);
	}

	rp2 = w2->RPort;

	SetDrMd(rp2,JAM1);
	SetAPen(rp2,0);
	SetBPen(rp2,0);
	RectFill(rp2,LEFTW2+1,TOPW2+1,RIGHTW2-1,BOTTOMW2-1);

	for (EVER) {
		refresh = 0;
		if (message = (struct IntuiMessage *)GetMsg(w2->UserPort)) {
			class = message->Class;
			code  = message->Code;
			ReplyMsg(message);

			if (class == CLOSEWINDOW) {
				CloseWindow(w2);
				w2 = NULL;
				reset_menus();
				return (0);
			}
			if (class == NEWSIZE)
				refresh = 1;
		}

		if (message = (struct IntuiMessage *)GetMsg(w->UserPort)) {
			class = message->Class;
			code  = message->Code;
			ReplyMsg(message);

			if (class == MOUSEBUTTONS)
				if (code == SELECTDOWN)
					select = TRUE;
				else
					if (code == SELECTUP)
						select = FALSE;
		}
		if (((last_x != w->MouseX || last_y != w->MouseY) && select &&
		w->MouseX < max_x && w->MouseY >= STARTY && w->MouseY < max_y+STARTY) || refresh) {
			if (!refresh) {
				last_x = w->MouseX;
				last_y = w->MouseY;
			}
			SetAPen(rp2,0);
			RectFill(rp2,LEFTW2+1,TOPW2+1,RIGHTW2-1,BOTTOMW2-1);
			width = RIGHTW2 - LEFTW2 - 6;
			height = BOTTOMW2 - TOPW2 - 4;
			SetAPen(rp2,2);
			Move(rp2,LEFTW2+2,TOPW2+height/2+2);
			Draw(rp2,RIGHTW2-2,TOPW2+height/2+2);
			Move(rp2,LEFTW2+width/2+3,TOPW2+2);
			Draw(rp2,LEFTW2+width/2+3,BOTTOMW2-2);
			SetAPen(rp2,1);
			z_r = const0;
			z_i = const0;
			MAKEFP(u_r) = ADD(MAKEFP(start_r),MUL(FLT(last_x),MAKEFP(x_gap)));
			MAKEFP(u_i) = ADD(MAKEFP(start_i),MUL(FLT(max_y-(last_y-STARTY)-1),MAKEFP(y_gap)));
			count = 0;
			for (count=0;FIX(ADD(MUL(MAKEFP(z_r),MAKEFP(z_r)),MUL(MAKEFP(z_i),MAKEFP(z_i))))<4&&(count
			<max_count);count++) {

				MAKEFP(temp) = DIV(MAKEFP(const4),MUL(MAKEFP(z_r),FLT(width)));
				x = FIX(MAKEFP(temp));
				MAKEFP(temp) = DIV(MAKEFP(const4),MUL(MAKEFP(z_i),FLT(height)));
				y = FIX(MAKEFP(temp));

				if (!lines)
					if (x != 0 || y != 0)
						WritePixel(rp2,x+width/2+LEFTW2+3,height/2-y+TOPW2+2);
					else
						;
				else
					if (count == 0)
						Move(rp2,x+width/2+LEFTW2+3,height/2-y+TOPW2+2);
					else
						Draw(rp2,x+width/2+LEFTW2+3,height/2-y+TOPW2+2);

				if (func_num == 0) {  /* z = z^2-u */
					MAKEFP(temp) = SUB(MAKEFP(u_r),SUB(MUL(MAKEFP(z_i),MAKEFP(z_i)),MUL(MAKEFP(z_r),MAKEFP(z_r))));
					MAKEFP(z_i) = SUB(MAKEFP(u_i),MUL(ADD(MAKEFP(z_r),MAKEFP(z_r)),MAKEFP(z_i)));
					z_r = temp;
				}
				else
					if (func_num == 1) {  /* z = z^2+u */
						MAKEFP(temp) = ADD(MAKEFP(u_r),SUB(MUL(MAKEFP(z_i),MAKEFP(z_i)),MUL(MAKEFP(z_r),MAKEFP(z_r))));
						MAKEFP(z_i) = ADD(MAKEFP(u_i),MUL(ADD(MAKEFP(z_r),MAKEFP(z_r)),MAKEFP(z_i)));
						z_r = temp;
					}
			}
		}
	}
}

ZoomAlongDarling(rzoom, izoom)
#ifdef DOUBLEPREC
double rzoom,izoom;
#else
int rzoom, izoom;
#endif
{
	FLOATSTRUCT center,distance,scale;

	MAKEFP(scale) = rzoom;
	MAKEFP(distance) = DIV(FLT(2),SUB(MAKEFP(start_r),MAKEFP(end_r)));
	MAKEFP(center) = ADD(MAKEFP(start_r),MAKEFP(distance));
	MAKEFP(scale) = MUL(MAKEFP(scale),MAKEFP(distance));
	MAKEFP(start_r) = SUB(MAKEFP(scale),MAKEFP(center));
	MAKEFP(end_r) = ADD(MAKEFP(scale),MAKEFP(center));

	MAKEFP(scale) = izoom;
	MAKEFP(distance) = DIV(FLT(2),SUB(MAKEFP(start_i),MAKEFP(end_i)));
	MAKEFP(center) = ADD(MAKEFP(start_i),MAKEFP(distance));
	MAKEFP(scale) = MUL(MAKEFP(scale),MAKEFP(distance));
	MAKEFP(start_i) = SUB(MAKEFP(scale),MAKEFP(center));
	MAKEFP(end_i) = ADD(MAKEFP(scale),MAKEFP(center));
}

int ProjectMenu(code)
ULONG code;
/* if this function calls gen_mand() and it returns non-zero, return a
* non-zero to the caller, else return zero
*/
{
	char namebfr[31];

	switch ITEMNUM(code) {
		case PROJECT_SAVEPICTURE:
			ScreenToBack(screen);
			printf("Click in window and enter save filename: ");
			rewind(stdout);
			fgets(namebfr,30,stdout);
			rewind(stdout);
			printf("\nDon't forget to click on the graphics display when saving is complete!\n");
			namebfr[strlen(namebfr)-1] = '\0';
			if (SavePicture(namebfr))
				ScreenToFront(screen);
			else
				printf("LeftAMIGA-M will redisplay your picture\n");
			break;
		case PROJECT_TITLE:
			title_displayed = !title_displayed;
			ShowTitle(screen,title_displayed);
			reset_title();
			break;
		case PROJECT_PRINT:
			dump_screen();
			break;
	}
	return(0);
}

int DisplayMenu(code)
ULONG code;
/* if this function calls gen_mand() and it returns non-zero, return a
* non-zero to the caller, else return zero
*/
{
	int threed_scale;

	switch ITEMNUM(code) {
		case OPTIONS_QUARTER:
			if (color_mode & HIRES_MODE)
				max_x = 640 / 4;
			else
				max_x = 320 / 4;
			if (color_mode & INTERLACE_MODE)
				max_y = 400 / 4;
			else
				max_y = 200 / 4;
			max_mem = max_mem_y * MAXX;
			max_mem /= max_x;
			if (gen_mand())
				return(1);
			break;
		case OPTIONS_FULL:
			if (color_mode & HIRES_MODE)
				max_x = 640;
			else
				max_x = 320;
			if (color_mode & INTERLACE_MODE)
				max_y = 400;
			else
				max_y = 200;
			max_mem = max_mem_y * MAXX;
			max_mem /= max_x;
			/* intentionally fall into GENERATE */
		case OPTIONS_GENERATE:
			if (gen_mand())
				return(1);
			break;
		case OPTIONS_REDRAW:
			if (disp_mand())
				return(1);
			break;
		case OPTIONS_3D:
			switch (SUBNUM(code)) {
				case OPTIONS_3D_NONE: threed_scale = 1; break;
				case OPTIONS_3D_2: threed_scale = 2; break;
				case OPTIONS_3D_5: threed_scale = 5; break;
				case OPTIONS_3D_10: threed_scale = 10; break;
				case OPTIONS_3D_15: threed_scale = 15; break;
			}
			if (draw_threed(threed_scale))
				return(1);
			break;
		case OPTIONS_ANAL:
			switch (SUBNUM(code)) {
				case OPTIONS_ANAL_LINES: lines = 1; break;
				case OPTIONS_ANAL_DOTS: lines = 0; break;
			}
			anal_mand();
			break;
		case OPTIONS_CLOSE:
			CloseDisplay();
			return(1);
		case OPTIONS_COLORS:
			DoColorWindow();
			break;
	}
	return(0);
}

int ZoomMenu(code)
ULONG code;
/* if this function calls gen_mand() and it returns non-zero, return a
* non-zero to the caller, else return zero
*/
{
	FLOATSTRUCT center, distance, scale;

	switch ITEMNUM(code) {
		case ZOOM_SETCENTER:
			if (NOT SettingCenter) {
				ZoomCenterX = w->MouseX;
				ZoomCenterY = w->MouseY;
				DrawDisplayCenter();
				DrawZoomCenter();
				SettingCenter = TRUE;
			}
			break;
		case ZOOM_SIZEBOX:
		case ZOOM_SIZEPROP:
			if (NOT SettingBoxSize) {
				if (ITEMNUM(code) == ZOOM_SIZEPROP)
					SetBoxProportional = TRUE;
				else
					SetBoxProportional = FALSE;
				SettingBoxSize = TRUE;
				RecalcZoomBox();
				DrawZoomBox();
			}
			break;
		case ZOOM_ZOOMIN:
		case ZOOM_ZOOMIN10:
			/* first, get distance equal to the current size
			* of a single pixel width
			*/
			MAKEFP(distance) = SUB(MAKEFP(start_r),MAKEFP(end_r));
			MAKEFP(distance) = DIV(FLT(max_x), MAKEFP(distance));
			/* center equals the number of pixels from the
			* center of the display to the zoom center
			*/
			MAKEFP(center) = SUB(FLT(max_x >> 1), FLT(ZoomCenterX));
			/* scale equals the real displacement from the
			* center of the display to the zoom center
			*/
			MAKEFP(scale) = MUL(MAKEFP(distance), MAKEFP(center));
			/* move the real origin to here */
			MAKEFP(start_r) = ADD(MAKEFP(scale),MAKEFP(start_r));
			MAKEFP(end_r) = ADD(MAKEFP(scale),MAKEFP(end_r));
			/* now do it all again for the imaginary axis */
			MAKEFP(distance) = SUB(MAKEFP(start_i),MAKEFP(end_i));
			MAKEFP(distance) = DIV(FLT(max_y), MAKEFP(distance));
			/* the signs are reversed to swap the sign */
			MAKEFP(center) = SUB(FLT(ZoomCenterY),FLT(max_y >> 1));
			MAKEFP(scale) = MUL(MAKEFP(distance), MAKEFP(center));
			/* move the real origin to here */
			MAKEFP(start_i) = ADD(MAKEFP(scale),MAKEFP(start_i));
			MAKEFP(end_i) = ADD(MAKEFP(scale),MAKEFP(end_i));
			/* next, get the zoom-in scale
			* if we're using the frame, then get the
			* proportion of the box to the display;
			* else if we're zooming in by 10, get that scale
			*/
			if (ITEMNUM(code) == ZOOM_ZOOMIN)
				MAKEFP(scale) = DIV(FLT(max_x), FLT(ZoomBoxSizeX));
			else
				MAKEFP(scale) = DIV(FLT(10), FLT(1));
			ZoomAlongDarling(MAKEFP(scale), FLT(1));
			if (ITEMNUM(code) == ZOOM_ZOOMIN)
				MAKEFP(scale) = DIV(FLT(max_y), FLT(ZoomBoxSizeY));
			else
				MAKEFP(scale) = DIV(FLT(10), FLT(1));
			ZoomAlongDarling(FLT(1), MAKEFP(scale));
				if (gen_mand())
				return(1);
			break;
		case ZOOM_ZOOMOUT2:
			ZoomAlongDarling(FLT(2), FLT(2));
			if (gen_mand())
				return(1);
			break;
		case ZOOM_ZOOMOUT10:
			ZoomAlongDarling(FLT(10), FLT(10));
			if (gen_mand())
				return(1);
			break;
	}
	return(0);
}
