/* INCLUDE FILES */
#include "pv.h"

int reset_c(char *);
int step_c(char *);
int twist_c(char *);
int write_c(char *);

parse_t commands[] = {
	"from", from_c,
	"at", at_c,

	"step", step_c,
	"twist", twist_c,
	"write", write_c,
	"reset", reset_c,
	
	NULL, NULL
};


void
init_menus()
{
	void exit(int);


	/* Define the window pop-up menus */
	quit_m = defpup("Confirm%t|Yes, really! %f|No, not really.", exit);
	vrender_m = defpup("points%f|lines%f|polygons%f",
			  point_mode, lines_mode, polygon_mode);
	vshade_m = defpup("constant%f|gouraud%f", constant_mode, gouraud_mode);
	vproject_m = defpup("perspective%f|orthographic%f", persp_mode,
		 ortho_mode);
	vpalette_m = defpup("from file %F|gray|rainbow|rgb",
			    palette_pick);
	voverlay_m = defpup("at point%f|axes%f|box%f|labels%f|outlines%f",
		at_point_tgl, axes_tgl, box_tgl, labels_tgl, outlines_tgl);
	vanimate_m = defpup("reverse%f|reverse 1%f|stop%f|forward 1%f|forward%f",
			    reverse, reverse1, stop, forward1, forward);
	view_m = defpup("View %t|project %m|draw %m|shade %m|palette %m|overlay %m%l|Animate %m|Fly|Pop|Redraw|Reset|Write HDF %l|quit %m",
			vproject_m, vrender_m, vshade_m, vpalette_m, voverlay_m,
			vanimate_m, quit_m);
}


int
parse_and_exec(command, line)
	parse_t command[];
	char * line;
{
	char word[MAXLINELEN];
	char subline[MAXLINELEN];
	int i;
	int (*parse_fn)();

	/* Parse the command word and the rest of the line */
	word[0] = '\0';
	subline[0] = '\0';
	sscanf(line, "%s %[^\n]", word, subline);

	/* Search for any matches of the command word */
	parse_fn = NULL;
	for (i = 0; command[i].word != NULL; i++) {
		if (strcasecmp(command[i].word, word) == 0) {
			if (parse_fn == NULL) {
				parse_fn = command[i].parse_fn;
			}
			else {
				return TRUE;	/* ERROR:  not unique */
			}
		}
	}


	/* Call the sub-line parsing function if one was found. */
	if (parse_fn != NULL) {
		return ((*parse_fn)(subline));
	}
	else {
		return TRUE;
	}
}


int
from_c(line)
	char * line;
{
	char word[MAXLINELEN];
	windat_t * window;

	float dx, dy, dz, dt;
	double fx, fy, fz, ft;
	double twistf;

	double dist;
	float steps;
	int num_args;


	/* Get the type of move and coordinate vector */
	word[0] = '\0';
	num_args = sscanf(line, "%s %f %f %f %f", word, &dx, &dy, &dz, &dt);
	if ((num_args != 4) && (num_args != 5)) {
		printf("syntax error:  from type x y z [twist]\n");
		return TRUE;
	}


	/* The default value for twist is the current value */
	if (num_args == 4) {
		dt = (float) twist;
	}
	else {
		dt *= 10;
	}


	/* Adjust the new vector based on the type (absolute or relative) */
	/* and save their final values */
	if (strncasecmp("abs", word, strlen(word)) == 0) {
		fx = dx;
		fy = dy;
		fz = dz;
		ft = dt;

		dx -= from[X];
		dy -= from[Y];
		dz -= from[Z];
		dt -= twist;
	}
	else {
		fx = dx + from[X];
		fy = dy + from[Y];
		fz = dz + from[Z];
		ft = dt + twist;

		if (strncasecmp("rel", word, strlen(word)) != 0) {
			printf("from requires 'abs' or 'rel' as ");
			printf("a second argument.\n");
		}
		return TRUE;
	}


	/* Determine the number of steps necessary to move from the initial */
	/* from point to the new from point.  If there is no change in */
	/* location, use the twist value to determine the twist rate.  If */
	/* there is no change in twist, quit the function. */
	dist = fhypot((double) dx, fhypot((double) dy, (double) dz));

	if (dist == 0.0) {
		if (dt == 0.0) {
			return FALSE;
		}
		else {
			steps = fabs(dt / (step * 500));
		}
	}
	else {
		steps = dist / step;
	}


	/* Calculate the steps sizes for x, y, z, and twist parameters. */
	dx /= steps;
	dy /= steps;
	dz /= steps;
	dt /= steps;

	/* Move the view, updating after each step */
	twistf = (float) twist;
	while ((steps >= 1.0) && current.fly_mode) {
		/* Adjust the viewing parameters */
		from[X] += (float) dx;
		from[Y] += (float) dy;
		from[Z] += (float) dz;
		twistf += dt;
		twist = (Angle) twistf;
		steps -= 1.0;

		cart_to_sphere(from[X]-at[X], from[Y]-at[Y], from[Z]-at[Z],
			       &rho, &theta, &phi);

		/* Redraw the window and check for any other pending events */
		if (current.window != NULL) {
			qenter(REDRAW, (short) current.active_id);
		}
		handle_event(windows);

		/* If the user terminated the fly-by, get out of here */
		/* without any further changes. */
		if (!current.fly_mode) return FALSE;
	}

	/* If there was part of a step left over, adjust the final position */
	/* and regenerate the image */
	if (steps != 0.0) {
		from[X] = fx;
		from[Y] = fy;
		from[Z] = fz;
		twist = ft;
		cart_to_sphere(from[X]-at[X], from[Y]-at[Y], from[Z]-at[Z],
			       &rho, &theta, &phi);

		if (current.window != NULL) {
			qenter(REDRAW, (short) current.active_id);
		}
		handle_event(windows);
	}
	
	return FALSE;
}


int
at_c(line)
	char * line;
{
	char word[MAXLINELEN];
	windat_t * window;
	double nx, ny, nz;
	float dx, dy, dz;
	double dist;
	int steps;
	
	/* Get the type of move and coordinate vector */
	word[0] = '\0';
	if (sscanf(line, "%s %f %f %f", word, &dx, &dy, &dz) != 4) {
		printf("at requires four arguments:  type, x, y, z.\n");
		return TRUE;
	}

	/* Adjust the new vector based on the word...*/
	if (strncasecmp("rel", word, strlen(word)) == 0) {
		dx += at[X];
		dy += at[Y];
		dz += at[Z];
	}
	else if (strncasecmp("abs", word, strlen(word)) != 0) {
		printf("at requires 'abs' or 'rel' as second argument.\n");
		return TRUE;
	}

	/* Calculate how to move */
	dist = fhypot((double)(dx-at[X]), fhypot((double)(dy-at[Y]),
						(double)(dz-at[Z])));
	nx = (dx - at[X]) / dist * step;
	ny = (dy - at[Y]) / dist * step;
	nz = (dz - at[Z]) / dist * step;

	/* Move the view, updating after each step */
	for (steps = dist / step; (steps > 0.0) && current.fly_mode;
	     steps -= 1.0) {
		at[X] += (float) nx;
		at[Y] += (float) ny;
		at[Z] += (float) nz;
		cart_to_sphere(from[X]-at[X], from[Y]-at[Y], from[Z]-at[Z],
			       &rho, &theta, &phi);


		if (current.window != NULL) {
			qenter(REDRAW, (short) current.active_id);
		}
		handle_event(windows);
		if (!current.fly_mode) return FALSE;
	}
	at[X] = dx;
	at[Y] = dy;
	at[Z] = dz;
	cart_to_sphere(from[X]-at[X], from[Y]-at[Y], from[Z]-at[Z],
		       &rho, &theta, &phi);

	if (current.window != NULL) {
		qenter(REDRAW, (short) current.active_id);
	}
	handle_event(windows);
	
	return FALSE;
}


int
step_c(line)
	char * line;
{
	/* Get the new step size value */
	if (sscanf(line, "%f", &step) != 1) {
		printf("step requires one argument:  stepsize.\n");
		return TRUE;
	}

	return FALSE;
}
	

int
twist_c(line)
	char * line;
{
	float angle;
	float twist_dist;
	Angle new_twist;
	int steps;
	float nt;

	
	/* Get the new twist angle value */
	if (sscanf(line, "%f", &angle) != 1) {
		printf("twist requires one argument:  angle.\n");
		return TRUE;
	}


	/* Calculate the amount of change in twist required */
	new_twist = (Angle) angle * 10;
	twist_dist = new_twist - twist;
	nt = (step * 500);
	if (twist_dist < 0.0) nt = -nt;


	/* Change the twist, updating after each step */
	for (steps = (int) (twist_dist / nt);
	     (steps > 0) && current.fly_mode; steps--) {
		twist += nt;

		if (current.window != NULL) {
			qenter(REDRAW, (short) current.active_id);
		}
		handle_event(windows);
		if (!current.fly_mode) return FALSE;
	}

	if (twist != new_twist) {
		twist = new_twist;
		if (current.window != NULL) {
			qenter(REDRAW, (short) current.active_id);
		}
		handle_event(windows);
	}

	return FALSE;
}


int
write_c(line)
	char * line;
{
	/* Should provide single and auto options, file name, and optional */
	/* count between frames */
	return FALSE;
}
	

int
reset_c(line)
	char * line;
{
	do_reset(10);
	return FALSE;
}
	

int
point_mode(val)
	int val;
{
	current.points = TRUE;
	current.lines = FALSE;
	current.polygons = FALSE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
lines_mode(val)
	int val;
{
	current.points = FALSE;
	current.lines = TRUE;
	current.polygons = FALSE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
polygon_mode(val)
	int val;
{
	current.points = FALSE;
	current.lines = FALSE;
	current.polygons = TRUE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
depth_mode(val)
	int val;
{
	current.depth = !current.depth;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
constant_mode(val)
	int val;
{
	current.constant = TRUE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
gouraud_mode(val)
	int val;
{
	current.constant = FALSE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
ortho_mode(val)
	int val;
{
	current.ortho = TRUE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
persp_mode(val)
	int val;
{
	current.ortho = FALSE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
at_point_tgl(val)
	int val;
{
	current.at_point = !current.at_point;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
palette_pick(val)
	int val;
{
	switch (val) {
	case 1:	/* from file */
		build_colormap(LOAD_SPEC);
		break;
	case 2: /* gray */
		build_colormap(GRAY_SPEC);
		break;
	case 3:	/* rainbow */
		build_colormap(RBOW_SPEC);
		break;
	case 4: /* rgb */
		build_colormap(RGB_SPEC);
		break;
	}
}


int
axes_tgl(val)
	int val;
{
	current.axes = !current.axes;
	current.box = FALSE;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}



int
box_tgl(val)
	int val;
{
	current.axes = FALSE;
	current.box = !current.box;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
labels_tgl(val)
	int val;
{
	current.labels = !current.labels;
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
outlines_tgl(val)
	int val;
{
	if (current.polygons) {
		current.outlines = !current.outlines;
		qenter(REDRAW, (short) current.active_id);
	}
	else
		if (current.outlines)
			current.outlines = !current.outlines;
	return 1;
}


int
forward(val)
	int val;
{
	image_t ** image;

	animation.forward = -1;
	animation.reverse = 0;

	return 1;
}


int
forward1(val)
	int val;
{
	animation.forward = 1;
	animation.reverse = 0;

	return 1;
}


int
stop(val)
	int val;
{
	animation.forward = 0;
	animation.reverse = 0;

	return 1;
}


int
reverse1(val)
	int val;
{
	image_t * image;

	animation.forward = 0;
	animation.reverse = 1;

	return 1;
}


int
reverse(val)
	int val;
{
	image_t * image;

	animation.forward = 0;
	animation.reverse = -1;

	return 1;
}


int
do_fly(val)
	int val;
{
	if (current.fly_mode) {
		current.fly_mode = FALSE;
	}
	else {
		current.fly_mode = TRUE;
		if (args_found_arg(args, "fly")) {
			if (read_fly_file(flyfile)) {
				printf("\nERROR:  Problem reading flyfile '%s'.\n",
					flyfile);
				exit(-1);
			}
			else {
			}
		}
		else {
			printf("\nPlease specify flyfile on command line\n");
			printf("using the '-fly' switch for this option.\n");
		}
	}

	/* Turn off the flying now that we are done */
	current.fly_mode = FALSE;
	return 1;
}


int
do_redraw(val)
	int val;
{
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
do_reset(val)
	int val;
{
	initialize(TRUE);
	qenter(REDRAW, (short) current.active_id);
	return 1;
}


int
do_pop(val)
	int val;
{
	winpop();
	return 1;
}


int
write_hdf(val)
	int val;
{
	unsigned char *buff;
	int depth, ret, num_bytes;
	long xsize, ysize;
	long x, y;
	Screencoord sx1,sy1,sx2,sy2;
	int i, j;
	char local_palette[256*3];

	/* pop the current window to the top of the screen */
	winset(current.active_id); 
	winpop();
 	getsize(&x, &y);

	/* Calculate the number of bytes required for the screen */
	getviewport(&sx1,&sx2,&sy1,&sy2);
	xsize = (sx2 - sx1) + 1;
	ysize = (sy2 - sy1) + 1;

	num_bytes = xsize * ysize * sizeof(char);
	buff = (unsigned char *)malloc(num_bytes);
	if (!buff) {
		printf("%s: Not enough memory\n",main_image.ris8out);
		exit(1);
	}

	/* Copy the palette to the proper format for DFR8setpalette */
	for (i = 0; i < 256; i++) {
		for (j = 0; j < 3; j++) {
			local_palette[i*3+j] = palette[i][j];
		}
	}

	/* Read the screen and write it to the hdf file */
	read_scr(&xsize,&ysize,buff);
	DFR8setpalette(local_palette); 
	ret = DFR8putimage(main_image.ris8out,buff,xsize,ysize,11);
	if (ret) {
		printf("%s: Error with DFR8putimage\n",main_image.ris8out);
		exit(1);
	}
}


int
read_scr(xsiz,ysiz,ibuff)
	long *xsiz,*ysiz;
	unsigned char *ibuff;
{
	Screencoord sx1,sy1,sx2,sy2;
	Coord rx1,ry1,rx2,ry2;
	Colorindex *row_cols;
	int num_bytes;
	register int i,j;
	Icoord ix;
	short lx;
	long jrp;
	unsigned char *buf_ptr;
	static float idmatrix[4][4] = {
        	1.0, 0.0, 0.0, 0.0,
        	0.0, 1.0, 0.0, 0.0,
        	0.0, 0.0, 1.0, 0.0,
        	0.0, 0.0, 0.0, 1.0};


	getviewport(&sx1,&sx2,&sy1,&sy2);

	*xsiz = (sx2 - sx1) + 1;
	*ysiz = (sy2 - sy1) + 1;

	rx1 = (Coord)sx1 - 0.5;
	ry1 = (Coord)sy1 - 0.5;
	rx2 = (Coord)sx2 + 0.5;
	ry2 = (Coord)sy2 + 0.5;

	pushmatrix();
	pushviewport();
	loadmatrix(idmatrix);
	ortho2(rx1,rx2,ry1,ry2);

	lx = (short)(*xsiz);
	ix = sx1;
	buf_ptr = &ibuff[0];

	num_bytes = lx * sizeof(Colorindex);
	row_cols = (Colorindex *)malloc(num_bytes);
	if (!row_cols){
		puts("Not enough memory for row_cols");
		exit(1);
	}

	readsource(SRC_FRONT);
	for(j=sy2; j >= sy1; j--){
		cmov2i(ix,j);
		jrp = readpixels(lx,row_cols);
		for(i=0; i<lx; i++)
			*buf_ptr++ = (unsigned char)(row_cols[i] - SPEC_BASE);
	}

	free(row_cols);

	popviewport();
	popmatrix();

	return(0);
}
