// Ingemars rewrite of the julia demo, integrating the OpenGL parts.
// 100% load version with fps meter

// SOLUTION

#include <GLUT/glut.h>
#include <OpenGL/gl.h>
#include <stdio.h>
#include <stdlib.h>

// Image data
	unsigned char	*pixels;
	int	 gImageWidth, gImageHeight;

// Init image data
void initBitmap(int width, int height)
{
	pixels = (unsigned char *)malloc(width * height * 4);
	gImageWidth = width;
	gImageHeight = height;
}

#define DIM 1024
#define iterations 200

// Complex number class
struct cuComplex
{
	float   r;
	float   i;
	
	cuComplex( float a, float b ) : r(a), i(b)  {}
	
	float magnitude2( void )
	{
		return r * r + i * i;
	}
	
	cuComplex operator*(const cuComplex& a)
	{
		return cuComplex(r*a.r - i*a.i, i*a.r + r*a.i);
	}
	
	cuComplex operator+(const cuComplex& a)
	{
		return cuComplex(r+a.r, i+a.i);
	}
};

int julia( int x, int y, float r, float im)
{
	const float scale = 1.5;
	float jx = scale * (float)(DIM/2 - x)/(DIM/2);
	float jy = scale * (float)(DIM/2 - y)/(DIM/2);

	cuComplex c(r, im);
	cuComplex a(jx, jy);

	int i = 0;
	for (i=0; i<iterations; i++)
	{
		a = a * a + c;
		if (a.magnitude2() > 1000)
			return i;
	}

	return i;
}

void kernel( unsigned char *ptr, float r, float im)
{
	int x, y;
	for (x = 0; x < gImageWidth; x++)
		for (y = 0; y < gImageWidth; y++)
		{
				int offset = x + y * gImageWidth;

				// now calculate the value at that position
				int juliaValue = julia( x, y, r, im );
				ptr[offset*4 + 0] = 255 * juliaValue/iterations;
				ptr[offset*4 + 1] = 0;
				ptr[offset*4 + 2] = 0;
				ptr[offset*4 + 3] = 255;
		}
}

float theReal, theImag;
long frames;

// Compute kernel (on CPU) and display image
void Draw()
{
	frames += 1;
	
	kernel(pixels, theReal, theImag);
	
// Dump the whole picture onto the screen.	
	glClearColor( 0.0, 0.0, 0.0, 1.0 );
	glClear( GL_COLOR_BUFFER_BIT );
	glDrawPixels( gImageWidth, gImageHeight, GL_RGBA, GL_UNSIGNED_BYTE, pixels );
	glFlush();
}

void MouseMovedProc(int x, int y)
{
	theReal = -0.5 + (float)(x-400) / 500.0;
	theImag = -0.5 + (float)(y-400) / 500.0;
}

void idle()
{
	glutPostRedisplay();
}

void timer(int value)
{
	printf("%f fps\n", (float)frames/10);
	frames = 0;
	glutTimerFunc(10000, timer, 0);
}

// Main program, inits
int main( int argc, char** argv) 
{
	glutInit(&argc, argv);
	glutInitDisplayMode( GLUT_SINGLE | GLUT_RGBA );
	glutInitWindowSize( DIM, DIM );
	glutCreateWindow("CPU on live GL");
	glutDisplayFunc(Draw);
	glutPassiveMotionFunc(MouseMovedProc);
	glutTimerFunc(10000, timer, 0);
	frames = 0;
	glutIdleFunc(idle);
	
	initBitmap(DIM, DIM);
	
	glutMainLoop();
}
