#include "fractal.h"

#include<exec/tasks.h>

#include<stdlib.h>
#include<stdio.h>

#include "gadgets.h"

#include<clib/alib_protos.h>
#include<clib/exec_protos.h>
#include<clib/graphics_protos.h>

static int calc(double, double, int);
static void fractal(void);

/* The mechanism by which we will control our task: */
/* One Semaphore for drawing, the other for running */
static SignalSemaphore drawing;
static SignalSemaphore running;

/* Initialisation done when program is started */
void initSemaphores()
{
	InitSemaphore(&drawing);
	InitSemaphore(&running);
}

/* To stop the fractal we stop it drawing then stop our */
/* task from running */
void stopFractal()
{
	ObtainSemaphore(&drawing);
	ObtainSemaphore(&running);
	ReleaseSemaphore(&running);
	ReleaseSemaphore(&drawing);
}

/* A local copy of the window pointer for our task */
static struct Window* win;

/* Draw a fractal in the window */
void drawFractal(struct Window* w)
{
	/* If the semaphore is available a task is not already running */
	if(AttemptSemaphore(&running))
	{
		struct Task* task;
		ReleaseSemaphore(&running);
		/* Make a copy of the window pointer for the task to use */
		win = w;
		/* Create a new task which will draw the fractal */
		task = CreateTask("HelloPainter-Fractal",-1,&fractal,4096);
		if(task == NULL)
			printf("Error: could not create task\n");
	}
	else
		/* The semaphore was already taken, so stop the task */
		stopFractal();
}

/* The starting point of our task */
static void __saveds fractal()
{
	/* If the semaphore is available we can start running */
  if(AttemptSemaphore(&running))
  {
		/* The width, height and number of colours for drawing */
		int w=win->Width, h=win->Height, d=1<<win->WScreen->BitMap.Depth;
		/* The snapshot of the mandelbrot set to draw */
		/* (Adjust these numbers to draw different fractals) */
		double width=4.0, height=4.0, top=-2.0, left=-2.5;
		int x;
		/* Check whether we can continue drawing on each x */
		for(x=0; x<w && AttemptSemaphore(&drawing); x++)
		{
			int y;
			for(y=0; y<h; y++)
			{
				/* Set the current colour */
				setFgPen(win, calc(x*width/w+left,y*height/h+top,d));
				/* Draw the pixel */
				WritePixel(win->RPort,x,y);
			}
			/* Release the semaphore before checking again */
			ReleaseSemaphore(&drawing);
		}
		/* Release the semaphore to indicate the task has finished */
		ReleaseSemaphore(&running);
	}
}

/* Calculate the colour of a particular point */
/* (This is the number of iterations of the equation */
/* needed to exceed the bound value) */
static int calc(double x, double y, int d)
{
	double xc=x, yc=y;
	int it;
	/* Adjust the 16.0 to give different colour spreads */
	for(it=0; it<d && (xc*xc)+(yc*yc)<16.0; it++)
	{
		double oldx = xc;
		xc = oldx*oldx-yc*yc+x;
		yc = 2.0*oldx*yc+y;
	}
	return it;
}
