/* ==== xjulia.c ============================================================
 *	
 * 	Created : 91/10/13 CAP
 *	Editted : 91/10/13 CAP
 *
 */

#include <math.h>
#include <errno.h>
#include <pthread.h>
#include "pxwidgets.h"

#define MAX_COMMAND_SIZE 256
#define EOF -1

char *version = "1.00";

int iterations	= 8;
double xcl 	= -2.0;
double ycl 	= -2.0;
double xch 	=  2.0;
double ych 	=  2.0;

char *colors[] = {
"red", "orange", "yellow", "green", "light blue", "blue", "purple", "black", NULL 
};
pthread_mutex_t xlock = PTHREAD_MUTEX_INITIALIZER;

struct poly {
	struct poly 	    * next;
	double 				  magnit;
	int					  x_exp;
	int					  y_exp;
} *x_poly, *y_poly;

struct point {
	double	xn;
	double	yn;
};

struct line {
	xwindow* name;
	int x;
};

/* ========================================================================
 * iterate()
 */
int iterate(struct point * pt)
{
	struct poly * current;
	double xnn, ynn, tmp;
	int rval = 0;

    for (xnn = 0.0, current = x_poly; current; current = current->next) {
		tmp = current->magnit;
		switch(current->x_exp) {
		default:
			tmp = pow(pt->xn, (double)(current->x_exp - 2));
		case 2:
			tmp *= pt->xn;
		case 1:
			tmp *= pt->xn;
		case 0:
			break;
		}
		switch(current->y_exp) {
		default:
			tmp *= pow(pt->yn, (double)(current->y_exp - 2));
		case 2:
			tmp *= pt->yn;
		case 1:
			tmp *= pt->yn;
		case 0:
			xnn += tmp;
			break;
		}
	}
	/* xnn = pt->xn*pt->xn - pt->yn*pt->yn + .3333; */
	/* xnn = pt->xn*pt->xn - pt->yn*pt->yn + .25; */
	/* xnn = pt->xn*pt->xn - pt->yn*pt->yn - .5; */
	ynn = 2*pt->xn*pt->yn;
	if ((xnn*xnn > 4) || (ynn*ynn > 4)) { 
		rval = 1;
	}
	pt->xn = xnn;
	pt->yn = ynn;
	return(rval);
}

/* ========================================================================
 * determine_poly()
 */
int determine_poly(char * eqn, struct poly ** list)
{
	struct poly * current;
	double neg;
	int retval, elem_done, exp, *var;
	int i = 0;

	while(eqn[i]) {
		if ((current = (struct poly *)malloc(sizeof(struct poly))) == NULL) {
			retval = ENOMEM;
			goto poly_error;
		}
		elem_done = 0;
		current->x_exp = 0;
		current->y_exp = 0;
		current->magnit = 0.0;
		current->next = *list;
		*list = current;

		/* Get leading +/- sign */
		while (eqn[i] && isspace(eqn[i])) i++;
		if ((eqn[i] == '-') || (eqn[i] == '+')) {
		   (eqn[i] == '-') ? (neg = -1.0) : (neg = 1.0);
			while (eqn[++i] && isspace(eqn[i]));
		} else {
			neg = 1.0;
		}

		if (!isdigit(eqn[i])) {
			switch (eqn[i]) {
			case '.':
				goto poly_get_dec;
			case 'x':
				current->magnit = neg;
				var = &current->x_exp;
				goto poly_get_exp;
			case 'y':
				current->magnit = neg;
				var = &current->y_exp;
				goto poly_get_exp;
			case 'E':
			case 'e':
			case '*':
			case '-':
			case '+':
			case '^':
			default:
				retval = EINVAL;
				goto poly_error;
			}
		} else {
			exp = eqn[i] - '0';
		}

		while (eqn[++i] && isdigit(eqn[i])) {
			exp = exp * 10 + eqn[i] - '0';
		}
		current->magnit = (double)exp;

poly_get_dec:;
		if (eqn[i] == '.') {
			int count = 1;
			if (eqn[++i] && isdigit(eqn[i])) {
				exp = eqn[i] - '0';
				while (eqn[++i] && isdigit(eqn[i])) {
					exp = exp * 10 + eqn[i] - '0';
					count++;
				}
			} else {
				retval = EINVAL;
				goto poly_error;
			}
			current->magnit += (double)exp * pow(0.1, (double)count);
		}
		current->magnit *= neg;

		/* Get 'e #' */
		/* Clear out spaces */
		while (eqn[i] && isspace(eqn[i])) i++;
		if ((eqn[i] == 'e') || (eqn[i] == 'E')) {
			while (eqn[++i] && isspace(eqn[i]));
			if (isdigit(eqn[i])) {
				exp = eqn[i] - '0';
			} else {
				retval = EINVAL;
				goto poly_error;
			}
			while (eqn[++i] && isdigit(eqn[i])) {
				exp = exp * 10 + eqn[i] - '0';
			}
			current->magnit *= pow(10.0, (double)exp);
		}

		while (!elem_done) {

			/* Clear out spaces */
			while (eqn[i] && isspace(eqn[i])) i++;

poly_get_var:;
			/* Get 'x' or 'y' */
			switch (eqn[i]) {
			case '*':
				/* Clear out spaces */
				while (eqn[i] && isspace(eqn[i])) i++;
				goto poly_get_var;
				break;
			case 'x':
				var = &current->x_exp;
				goto poly_get_exp;
			case 'y':
				var = &current->y_exp;
				goto poly_get_exp;
			default:
				elem_done++;
				continue;
			}

poly_get_exp:;
			/* Get '^ #' */
			/* Clear out spaces */
			while (eqn[++i] && isspace(eqn[i]));
			if (eqn[i] == '^') {
				while (eqn[++i] && isspace(eqn[i]));
				if (isdigit(eqn[i])) {
					exp = eqn[i] - '0';
				} else {
					retval = EINVAL;
					goto poly_error;
				}
				while (eqn[++i] && isdigit(eqn[i])) {
					exp = exp * 10 + eqn[i] - '0';
				}
				*var += exp;
			} else {
				*var++;
			}
		}
	}
	return(0);

poly_error:;
	for (current = *list; current; current = *list) {
		*list = current->next;
		free(current);
	}
	return(retval);
}

/* ========================================================================
 * compute_line()
 */
void * compute_line(void * arg)
{
	struct point pt;
	int next, i, y;

	struct line * line = (struct line *)arg;

	pthread_mutex_lock(&xlock);
	draw_point(line->name, line->x, 0);
	pthread_mutex_unlock(&xlock);

	for (y = 0; y < 400; y++) {
		pt.xn = (xch - xcl) * ((double)line->x)/400 + xcl;
		pt.yn = (ych - ycl) * ((double)y)/400 + ycl;
		for (i = 0; i < iterations; i++) {
			if (iterate(&pt)) {
				pthread_mutex_lock(&xlock);
				draw_point(line->name, line->x, y);
				pthread_mutex_unlock(&xlock);
				break;
			}
		}
	}
	
} 

/* ========================================================================
 * compute_window()
 */
void	compute_window(xwindow* name)
{
	pthread_t thread;
	struct line * line;
	int x;

xwindow_color_fg_set(name, 4);
printf("%f,%f,%f,%f.\n", xcl, ycl, xch, ych);

	for (x = 0; x < 400; x++) {
		line = (struct line *)malloc(sizeof(struct line));
		line->name = name;
		line->x = x;

		pthread_create(&thread, NULL, compute_line, (void *)line);
	}
	pthread_join(thread, NULL);
			
}

int line_last;
pthread_mutex_t line_mutex = PTHREAD_MUTEX_INITIALIZER;

int point_last;
pthread_mutex_t point_mutex = PTHREAD_MUTEX_INITIALIZER;

/* ========================================================================
 * compute_line_2()
 */
void * compute_line_2(void * arg)
{
	struct point point[400];
	int x, y, prevy, doney, i;
	char done[400];
struct sched_param maxparam, defparam;
pthread_t thread = pthread_self();

	xwindow * name = (xwindow *)arg;
maxparam.sched_priority = PTHREAD_MAX_PRIORITY;
defparam.sched_priority = PTHREAD_DEFAULT_PRIORITY;

	for (pthread_mutex_lock(&line_mutex); x = line_last++,
	  line_last < 400; pthread_mutex_lock(&line_mutex)) {
	  	pthread_mutex_unlock(&line_mutex);

		for (y = 0; y < 400; y++) {
			point[y].xn = (xch - xcl) * ((double)x)/400 + xcl;
			point[y].yn = (ych - ycl) * ((double)y)/400 + ycl;
			done[y] = 0;
		}

		for (doney = -1, i = 0; i < iterations; i++) {
			for (prevy = -1, y = doney; y < 400; y++) {
				if (done[y]) {
					if (prevy != -1) {
pthread_setschedparam(thread, SCHED_RR, &maxparam);
                        pthread_mutex_lock(&xlock);
                        xwindow_color_fg_set(name, i % 8);
                        xwindow_line_draw(name, x, prevy, x, y - 1);
                        pthread_mutex_unlock(&xlock);
pthread_setschedparam(thread, SCHED_RR, &defparam);
                        prevy = -1;
                    }
					if (doney == y - 1)
						doney++;
					continue;
				}
				if (done[y] = iterate(&point[y])) {
					if (prevy == -1) prevy = y;
					if (doney == y - 1)
						doney++;
				} else {
					if (prevy != -1) {
pthread_setschedparam(thread, SCHED_RR, &maxparam);
						pthread_mutex_lock(&xlock);
						xwindow_color_fg_set(name, i % 8);
						xwindow_line_draw(name, x, prevy, x, y - 1);
						pthread_mutex_unlock(&xlock);
pthread_setschedparam(thread, SCHED_RR, &defparam);
						prevy = -1;
					}
				}
			}
			if (done[399]) {
				if (prevy != -1) {
pthread_setschedparam(thread, SCHED_RR, &maxparam);
					pthread_mutex_lock(&xlock);
					xwindow_color_fg_set(name, i % 8);
					xwindow_line_draw(name, x, prevy, x, 399);
					pthread_mutex_unlock(&xlock);
pthread_setschedparam(thread, SCHED_RR, &defparam);
				}
				if (doney == y - 1)
					doney++;
			}
		}
	}
	pthread_mutex_unlock(&line_mutex);
}

/* ========================================================================
 * compute_line_4()
 */
void * compute_line_4(void * arg)
{
	struct point point;
	int prevy, previ;
	int x, y, i;
struct sched_param maxparam, defparam;
pthread_t thread = pthread_self();

	xwindow * name = (xwindow *)arg;
maxparam.sched_priority = PTHREAD_MAX_PRIORITY;
defparam.sched_priority = PTHREAD_DEFAULT_PRIORITY;

	for (pthread_mutex_lock(&line_mutex); x = line_last++,
	  line_last < 400; pthread_mutex_lock(&line_mutex)) {
	  	pthread_mutex_unlock(&line_mutex);

		for (prevy = -1, y = 0; y < 400; y++) {

			point.xn = (xch - xcl) * ((double)x)/400 + xcl;
			point.yn = (ych - ycl) * ((double)y)/400 + ycl;

			for (i = 0; i < iterations; i++) {
				if (iterate(&point)) {
					if (prevy == -1) {
						prevy = y;
						previ = i;
						break;
					}
					if (previ == i)
						break;
pthread_setschedparam(thread, SCHED_RR, &maxparam);
                    pthread_mutex_lock(&xlock);
                    xwindow_color_fg_set(name, previ % 8);
                    xwindow_line_draw(name, x, prevy, x, y - 1);
                    pthread_mutex_unlock(&xlock);
pthread_setschedparam(thread, SCHED_RR, &defparam);
					prevy = y;
					previ = i;
					break;
				}
			}
			if ((i == iterations) && (prevy != -1)) {
pthread_setschedparam(thread, SCHED_RR, &maxparam);
	            pthread_mutex_lock(&xlock);
	            xwindow_color_fg_set(name, previ % 8);
	            xwindow_line_draw(name, x, prevy, x, y - 1);
	            pthread_mutex_unlock(&xlock);
pthread_setschedparam(thread, SCHED_RR, &defparam);
				prevy = -1;
			}
		}
		if (prevy != -1) {
pthread_setschedparam(thread, SCHED_RR, &maxparam);
            pthread_mutex_lock(&xlock);
            xwindow_color_fg_set(name, previ % 8);
            xwindow_line_draw(name, x, prevy, x, 399);
            pthread_mutex_unlock(&xlock);
pthread_setschedparam(thread, SCHED_RR, &defparam);
		}
	}
	pthread_mutex_unlock(&line_mutex);
}

/* ========================================================================
 * compute_window_2()
 */
void compute_window_2(xwindow* name)
{
	pthread_t thread;
	int thread_count;
	int i, count;

	line_last = 0;
	thread_count = 0;
	while (pthread_mutex_lock(&line_mutex), line_last < 400) {
		if ((line_last + thread_count + 1) < 400) {
			count = thread_count + 1;
		} else {
			count = 400 - line_last;
		}
		for (i = 0; i < count; i++) {
			/*pthread_create(&thread, NULL, compute_line_2, (void *)name); */
			pthread_create(&thread, NULL, compute_line_4, (void *)name); 
		}
		pthread_mutex_unlock(&line_mutex);
		thread_count += count;
		pthread_yield();
	}
	pthread_mutex_unlock(&line_mutex);
printf("created %d threads\n", thread_count);
}

/* ========================================================================
 * compute_point_3()
 */
void * compute_point_3(void * arg)
{
	xwindow * name = (xwindow *)arg;
	struct point point;
	int point_current;
	int i;
struct sched_param maxparam, defparam;
pthread_t thread = pthread_self();

maxparam.sched_priority = PTHREAD_MAX_PRIORITY;
defparam.sched_priority = PTHREAD_DEFAULT_PRIORITY;

	for (pthread_mutex_lock(&point_mutex); point_current = point_last++,
	  point_last < 160000; 
pthread_setschedparam(thread, SCHED_RR, &maxparam),
	  pthread_mutex_lock(&point_mutex)) {
	  	pthread_mutex_unlock(&point_mutex);
pthread_setschedparam(thread, SCHED_RR, &defparam); 

		point.xn = (xch - xcl) * ((double)(point_current / 400))/400 + xcl;
		point.yn = (ych - ycl) * ((double)(point_current % 400))/400 + ycl;

		for (i = 0; i < iterations; i++) {
			if (iterate(&point)) {
pthread_setschedparam(thread, SCHED_RR, &maxparam); 
				pthread_mutex_lock(&xlock);
				xwindow_color_fg_set(name, i % 8);
				draw_point(name, point_current / 400, point_current % 400);
				pthread_mutex_unlock(&xlock);
pthread_setschedparam(thread, SCHED_RR, &defparam); 
				break;
			}
		}
	}
	pthread_mutex_unlock(&point_mutex);
}

/* ========================================================================
 * compute_window_3()
 */
void compute_window_3(xwindow* name)
{
	pthread_t thread;
	int thread_count;

	point_last = 0;
	thread_count = 0;
	while (pthread_mutex_lock(&point_mutex), point_last < 160000) {
		pthread_create(&thread, NULL, compute_point_3, (void *)name);
		pthread_mutex_unlock(&point_mutex);
		pthread_yield();
		thread_count++;
	}
	pthread_mutex_unlock(&point_mutex);
printf("created %d threads\n", thread_count);
}

/* ========================================================================
 * display_window()
 */
void	display_window() 
{
	xwindow *root, *name;

	if ((root = init_xwindow()) == NULL) {
		printf("Error can't initialize root window.\n");
		exit(1);
	}
	name = open_xwindow(root);
	xwindow_colormap_set(name, colors);
/*	draw_xwindow(name);  */
/*	compute_window(name); */
	compute_window_2(name); 
/*	compute_window_3(name); */ /* Points don't work */
	sleep(60);
}

/* ========================================================================
 * usage()
 */
void usage (int status)
{
	printf("xjuliaset [-dv]\n");
	exit(status);
}

main(int argc, char **argv)
{
	int debug;

	char word[MAX_COMMAND_SIZE];

	/* Getopt variables. */
	extern int optind, opterr;
	extern char *optarg;

printf("Testing\n");
	while ((word[0] = getopt(argc, argv, "dvcx:i:?")) != (char)EOF) {
		switch (word[0]) {
		case 'd':
			debug++;
			break;
		case 'v':
			printf("%s\n", version);
			break;
		case 'i':
			iterations = atol(optarg);
			break;
		case 'c':
			if ((argc - optind) < 3) {
				usage(1);
			}
			xcl = atof(argv[optind++]);
			ycl = atof(argv[optind++]);
			xch = atof(argv[optind++]);
			ych = atof(argv[optind++]);
			break;
		case 'x':
			if (determine_poly(optarg, &x_poly))
				return(2);
			break;
		case '?':
			usage(0);
			break;
		default:
			usage(1);
			break;
		}
	}


	display_window();
}

