// mandel.c v. 1.0
// 2009 - Seth Just
// Licensed under Creative Commons Attribution-Noncommercial-Share Alike 3.0 License
// This software is provided with no warranty of any kind.
// The author is not liable for any consequences arising from the use of this software.

#include <stdio.h> 
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <unistd.h>

#define GRADIENT	" .:oO8@"

// Initialize Variables
int verbose_flag = 0;

double width = 3;
double x = -1;
double y = 0;

int max_iterations = 100;

int s_width = 80;
int s_height = 24;

double height;
double x_offset;
double y_offset;

//
// Subroutines
//

long mandel( double real, double complex, int max_iterations ) {	
	long i;
	double z[ max_iterations ][ 2 ];
				
	for (i = 0; i < max_iterations; i++){
	
		z[ i ][ 0 ] =
			pow( z[ i - 1 ][ 0 ], 2 )
			- pow( z[ i - 1 ][ 1 ], 2 )
			+ real;
		
		z[ i ][ 1 ] =
			2
			* z[ i - 1 ][ 0 ]
			* z[ i - 1][ 1 ]
			+ complex;
		if ( pow( z[i][0], 2 ) + pow( z[i][1], 2 ) >= 4 ) {break;}
	}
	//fprintf(stderr,"%d ",i);
	return i;
}

static void usage( char *prog)
{
	(void)fprintf(stderr,
		"Usage: %s [-x x-coord] [-y y-coord] [-w width] [-i iterations] [-v]\n"
		"  x, y, width and iterations default to -1, 0, 3 and 100 respectively\n"
		"  -v prints details of the settings you've chosen\n\n"
		"  For an example try:\n"
		"    %s -w 0.01 -x -1.1887204 -y -0.3032472 -i 300\n"
		, prog, prog);
	exit(1);
}

int get_args(int argc, char *argv[])
{
	int option;
	extern char *optarg;
	
	/* Process the options.  Print help if requested. */
	while ((option = getopt(argc, argv, "x:y:w:i:vh")) != -1) {
		switch (option) {
			case 'x':
				x = atof(optarg);
				break;
			case 'y':
				y = atof(optarg);
				break;
			case 'w':
				width = atof(optarg);
				break;
			case 'i':
				max_iterations = atoi(optarg);
				break;
			case 'v':
				verbose_flag = 1;
				break;
			case 'h':
				usage(argv[0]);
				break;
		}
	}
	
	if (verbose_flag == 1){
		fprintf(stderr,
			"Proceeding with\n"
			"  x = %f\n"
			"  y = %f\n"
			"  width = %f\n"
			"  %d max iterations\n",
			x,y,width,max_iterations);
	}
	
	return 0;
}

int main(int argc, char *argv[])
{
	get_args(argc, argv);
	
	// Calculate window values
	height = s_height * ( width / s_width ) * 2;
	x_offset = ( width / 2 ) - x;
	y_offset = ( height / 2 ) + y;
	
	// Declare counters
	int i; int j;
	
	// Set ascii gradient
	char gradient[ strlen(GRADIENT) ];
	strcpy( gradient, GRADIENT );
	
	char ascii_table[ max_iterations ];
	
	for ( i = 0; i <= max_iterations; i++ ) {
		ascii_table[ i ] = gradient[ (int) ( ( strlen( GRADIENT ) ) * i / ( max_iterations + 1 ) ) ];
		//printf( "%c", ascii_table[ i ]);
	}
		
	// Main program starts here
	for ( i = 1; i <= s_height; i++ ) {										// Iterate rows
		double complex = -1 * ( ( ( (float) (s_height-i) / s_height ) * height ) - y_offset );
		for ( j = 1; j <= s_width; j++ ) {									// Iterate columns
			double real = ( ( (float) j / s_width ) * width ) - x_offset;
			long iterations = mandel( real, complex, max_iterations );		// Call mandel() function
			printf( "%c", ascii_table[ iterations ] ) ;
		}
		printf( "\n" );
	}
	return 1;
}

