#include <stdlib.h>
#include <setjmp.h>
#include <intuition/intuition.h>
#include <intuition/screens.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/exec.h>
#include <proto/dos.h>

#define width 320
#define height 256
#define depth 5


#define double float

struct IntuitionBase * IntuitionBase=0;
struct GfxBase * GfxBase=0;
struct Screen * scrn=0;
struct Window * win=0;
struct RastPort * rp;
struct MsgPort * mp;
struct IntuiMessage * msg;
void Cleanup(void);
void HandleMsg(void);
ULONG secs,msecs,olds=0,oldms=0;
void zoomin(WORD,WORD);
void zoomout(WORD,WORD);
void drawmandel(void);
double x1,x2,y1,y2;
int maxcount;
jmp_buf jb;
int oldpri=0;


UBYTE ct[][3]=
	{
	{ 11, 2, 2 },
	{ 11, 11, 11},
	{ 0, 10, 2 },
	{ 8, 8, 8 },
	{ 2, 2, 12 },
	{ 13, 13, 13 },
	{ 11, 0, 12 },
	{ 11, 11, 1 },
	{ 2, 10, 12 },
	{ 1, 5, 14 },
	{ 7, 8, 10 },
	{ 14, 14, 13 }
	};




int main(int argc,char *argv[])
	{
	int i;
	
	if(argc!=6 && argc!=1)
		{
		Printf("%s: [x1 x2 y1 y2 maxcount]\n",argv[0]);
		exit(EXIT_FAILURE);
		}
		
	if(argc==6)	
		{
		x1=atof(argv[1]);
		x2=atof(argv[2]);	
		y1=atof(argv[3]);
		y2=atof(argv[4]);
		maxcount=atoi(argv[5]);
		}
	else	/* no arguments given */
		{
		x1=-2.2;
		x2=1.1;
		y1=-1.4;
		y2=1.4;
		maxcount=50;
		}
		
		
		
		
	if(atexit(Cleanup)) return EXIT_FAILURE;
	if(!(IntuitionBase=(struct IntuitionBase *) OpenLibrary("intuition.library",37))) exit(EXIT_FAILURE);
	if(!(GfxBase=(struct GfxBase *) OpenLibrary("graphics.library",37))) exit(EXIT_FAILURE);
	
	scrn=OpenScreenTags(NULL,
						SA_Width,width,
						SA_Height,height,
						SA_Depth,depth,
						SA_ShowTitle,FALSE,
						SA_Type,CUSTOMSCREEN,
			/*			SA_DisplayID,HIRES_KEY, */
						TAG_DONE);
	if(!scrn) exit(EXIT_FAILURE);
	win=OpenWindowTags(NULL,
						WA_Left,0,
						WA_Top,0,
						WA_Width,width,
						WA_Height,height,
						WA_CustomScreen,(ULONG) scrn,
						WA_Borderless,TRUE,
						WA_Backdrop,TRUE,
						WA_Activate,TRUE,
						WA_SmartRefresh,TRUE,
						WA_NoCareRefresh,TRUE,
						WA_RMBTrap,TRUE,
						WA_IDCMP,IDCMP_VANILLAKEY|IDCMP_MOUSEBUTTONS,
						TAG_DONE);
	if(!win) exit(EXIT_FAILURE);
	
	
	rp=win->RPort;
	mp=win->UserPort;
	oldpri=SetTaskPri(FindTask(0),-1);
	
	for(i=0;i<=11;i++)
		{
		SetRGB4(&(scrn->ViewPort),i+20,ct[i][0],ct[i][1],ct[i][2]);
		}
	
	setjmp(jb);
	SetRast(rp,0);
	drawmandel();
	
	
	while(1)
		{
		WaitPort(mp);
		HandleMsg();
		}
	
	
	
	return 0;
	}
	
	
	
	
void Cleanup(void)	
	{
	if(win!=NULL) CloseWindow(win);
	if(scrn!=NULL) CloseScreen(scrn);
	if(GfxBase!=NULL) CloseLibrary((struct Library *)GfxBase);
	if(IntuitionBase!=NULL) CloseLibrary((struct Library *)IntuitionBase);
	SetTaskPri(FindTask(NULL),oldpri);
	}
	
	
void HandleMsg(void)
	{
	if(msg=(struct IntuiMessage *)GetMsg(mp))
		{
		switch(msg->Class)
			{
			case IDCMP_MOUSEBUTTONS:
				if((msg->Code) == SELECTDOWN)			
					{
					secs=msg->Seconds;	
					msecs=msg->Micros;
					if(DoubleClick(olds,oldms,secs,msecs))
						{
						olds=secs;	
						oldms=msecs;
						zoomin(msg->MouseX,msg->MouseY);
						ReplyMsg((struct Message *)msg);
						longjmp(jb,1);
						}
					else
						{
						olds=secs;
						oldms=msecs;
						}
					}
				else if((msg->Code) == MENUDOWN )
					{
					secs=msg->Seconds;	
					msecs=msg->Micros;
					if(DoubleClick(olds,oldms,secs,msecs))
						{
						olds=secs;	
						oldms=msecs;
						zoomout(msg->MouseX,msg->MouseY);
						ReplyMsg((struct Message *)msg);
						longjmp(jb,1);
						}
					else
						{
						olds=secs;
						oldms=msecs;
						}
					}
				break;
			case IDCMP_VANILLAKEY:
				switch(msg->Code)
					{	
					case 'q':
					case 27:  /* Escape key */
						ReplyMsg((struct Message *)msg);
						exit(0);
					case 'z':
						zoomin(width/2,height/2);
						ReplyMsg((struct Message *)msg);
						longjmp(jb,1);
						exit(EXIT_FAILURE); /* should never reach here */
					case 'Z':
						zoomout(width/2,height/2);
						ReplyMsg((struct Message *)msg);
						longjmp(jb,1);
						exit(EXIT_FAILURE); /* should never reach here */
					}
			}				
		ReplyMsg((struct Message *)msg);
		}
	}

	
void zoomin(WORD x,WORD y)	
	{
	double nx,ny;
	double dx,dy;
	maxcount+=25;
	dx=(x2-x1);	
	dy=(y2-y1);
	
	nx=(dx*x)/width+x1;
	ny=(dy*(height-y))/height+y1;
	x1=nx-(dx/2)/2;
	x2=nx+(dx/2)/2;
	y1=ny-(dy/2)/2;
	y2=ny+(dy/2)/2;
	}
	
	
void zoomout(WORD x,WORD y)
	{
	double nx,ny;
	double dx,dy;
	maxcount-=25;
	if(maxcount<10) maxcount=10;
	dx=(x2-x1);	
	dy=(y2-y1);
	
	nx=(dx*x)/width+x1;
	ny=(dy*(height-y))/height+y1;
	x1=nx-(dx/2)*2;
	x2=nx+(dx/2)*2;
	y1=ny-(dy/2)*2;
	y2=ny+(dy/2)*2;	
	}

void drawmandel(void)
	{
	int i,j,j1;
	double a,b,am,bm;
	double u,v,w,x,y;

	int k;

	am=(x2-x1)/width;
	bm=(y1-y2)/height;
	
	for(i=0,a=x1;i<width;i++)	
		{
		for(j=0,b=y2;j<height;)
			{
			HandleMsg();
			for(j1=0;j1<8 && j<height;j1++,j++)
				{
				x=a;y=b;
				
				for(k=0;k<maxcount;k++)
					{
					u=x*x;
					v=y*y;
					if(u+v>16) break;
					w=2*x*y;
					x=u-v+a;
					y=w+b;
					}
				if(k==maxcount)
					{
					SetAPen(rp,1);
					}
				else
					{
					SetAPen(rp,k % (1<<depth));
					}
				WritePixel(rp,i,j);
				b+=bm;
				}
			}
		a+=am;
		}
	}


	