#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>
#include "preview.h"
/*
	Cylinder, Sphere, and Cone drawing routines have been 
	adapted from VOGLE.
----
sumant
*/
extern int circle_precision;

read_n_drawsphere(oid,mid,name,fargs)
short oid,mid;
char *name;
FUNARGS *fargs;
{
	float	i, x, y, z, r, a;
	float	delta = 180.0 / circle_precision;
	
        x = fargs->farg[0];
        y = fargs->farg[1];
        z = fargs->farg[2];
        r = fargs->farg[3];
	pushmatrix();
		translate(x, y, z);
		/*
		 * create the longitudinal rings
		 */
		for (i = 0; i < 180; i += delta) {
			pushmatrix();
				rotate((float)i, 'y');
				circle(r);
			popmatrix();
		}
		/*
		 * create the latitudinal rings
		 */
		pushmatrix();
			rotate(90.0, 'x');
			for (i = -90.0; i < 90.0; i += delta) {
				a = r * cos((double)i * DTOR);
				z = r * sin((double)i * DTOR);
				pushmatrix();
					translate(0.0, 0.0, -z);
					circle(a);
				popmatrix();	
			}
		popmatrix();
	popmatrix();

}

read_n_drawcylinder(oid,mid,name,fargs)
short oid,mid;
char *name;
FUNARGS *fargs;
{
	float	apex_x, apex_y, apex_z;
	float	base_x, base_y, base_z;
	float	radius;

        base_x = fargs->farg[0];
        base_y = fargs->farg[1];
        base_z = fargs->farg[2];
        apex_x = fargs->farg[3];
        apex_y = fargs->farg[4];
        apex_z = fargs->farg[5];
        radius = fargs->farg[6];

	draw_cone_shaped_objects(apex_x, apex_y, apex_z, radius,
			base_x, base_y, base_z, radius,name);
}

read_n_drawcone(oid,mid,name,fargs)
short oid,mid;
char *name;
FUNARGS *fargs;
{
	float	apex_x, apex_y, apex_z, apex_r;
	float	base_x, base_y, base_z, base_r;
	
        base_x = fargs->farg[0];
        base_y = fargs->farg[1];
        base_z = fargs->farg[2];
        apex_x = fargs->farg[3];
        apex_y = fargs->farg[4];
        apex_z = fargs->farg[5];
        base_r = fargs->farg[6];
        apex_r = fargs->farg[7];

	draw_cone_shaped_objects(apex_x, apex_y, apex_z, apex_r,
			base_x, base_y, base_z, base_r,name);
}

draw_cone_shaped_objects(
	apex_x, apex_y, apex_z, apex_r,
	base_x, base_y, base_z, base_r,name)
float apex_x, apex_y, apex_z, apex_r,
	base_x, base_y, base_z, base_r;
char *name;
{
	float	len0, len1,  len_x, len_y, len_z;
	float	cosine, sine, dx1, dy1, dx2, dy2, delta;
	float	cx1, cy1, cx2, cy2;
	int	i;
	Matrix	m;

	len_x = apex_x - base_x;
	len_y = apex_y - base_y;
	len_z = apex_z - base_z;

	if ((len0 = sqrt(len_x * len_x + len_y * len_y + len_z * len_z)) < 1.0e-20)
		warn("c: zero length cone or cylinder",name);

	len1 = sqrt(len_x * len_x + len_z * len_z);

	move(base_x, base_y, base_z);
	draw(apex_x, apex_y, apex_z);

	pushmatrix();

		translate(base_x, base_y, base_z);

		identmatrix(m);

		if (len1 == 0.0) {
			cosine = 0.0;
			sine = 1.0;
		} else {
			cosine = len_z / len1;
			sine = len_x / len1;
		}

		/* rotate about y */
		m[0][0] = cosine;
		m[0][2] = -sine;
		m[2][0] = sine;
		m[2][2] = cosine;
		multmatrix(m);

		identmatrix(m);

		if (len0 == 0.0) {
			cosine = 0.0;
			sine = 1.0;
		} else {
			cosine = len1 / len0;
			sine = -len_y / len0;
		}

		/* rotate about x */
		m[1][1] = cosine;
		m[1][2] = sine;
		m[2][1] = -sine;
		m[2][2] = cosine;
		multmatrix(m);

		/*
		 * Draw the end circles...
		 */
		circle (base_r);
		pushmatrix();
			translate(0.0, 0.0, len0);
			circle (apex_r);
		popmatrix();



		/*
		 * Draw the logitudinal lines...
		 */
		delta = 2 * PI / circle_precision;

		cosine = cos(delta);
		sine = sin(delta);

		cx1 = base_r;
		cy1 = 0.0;
		cx2 = apex_r;
		cy2 = 0.0;

		move(cx1, cy1, 0.0);
		draw(cx2, cy2, len0);

		for (i = 0; i < circle_precision; i++) {
			dx1 = cx1;
			dy1 = cy1;
			cx1 = dx1 * cosine - dy1 * sine;
			cy1 = dx1 * sine + dy1 * cosine;
			dx2 = cx2;
			dy2 = cy2;
			cx2 = dx2 * cosine - dy2 * sine;
			cy2 = dx2 * sine + dy2 * cosine;
			move(cx1, cy1, 0.0);
			draw(cx2, cy2, len0);
		}
	popmatrix();
}

read_n_drawring(oid,mid,name,fargs)
short oid,mid;
char *name;
FUNARGS *fargs;
{
	float 	center_x, center_y, center_z,
		normal_x, normal_y, normal_z;
	float	r,r0,r1,delta_r;
	float   len0,len1;
	float	cosine, sine;
	int	i;
	Matrix	m;

        center_x = fargs->farg[0];
        center_y = fargs->farg[1];
        center_z = fargs->farg[2];
        normal_x = fargs->farg[3];
        normal_y = fargs->farg[4];
        normal_z = fargs->farg[5];
        r0 = fargs->farg[6];
        r1 = fargs->farg[7];

	len0 = sqrt(normal_x*normal_x + normal_y*normal_y + normal_z*normal_z);
	if (len0 < Small) error("Zero normal",name);
	len1 = sqrt(normal_x*normal_x+normal_z*normal_z);

	/* 
	 *	Assert r1 is the outer radius and r0 inner radius.
	 */
	if (r1 < r0) {
		r = r0; r0 = r1; r1 = r;
	}

	pushmatrix();

		translate(center_x,center_y,center_z);

		identmatrix(m);

		if (len1 == 0.0){
			cosine = 0.0;
			sine = 1.0;
		}
		else {
			cosine = normal_z/len1;
			sine = normal_x/len1;
		}

		/* rotate about y */
		m[0][0] = cosine;
		m[0][2] = -sine;
		m[2][0] = sine;
		m[2][2] = cosine;
		multmatrix(m);

		identmatrix(m);

		if (len0 == 0.0) {
			cosine = 0.0;
			sine = 1.0;
		} else {
			cosine = len1 / len0;
			sine = -normal_y / len0;
		}

		/* rotate about x */
		m[1][1] = cosine;
		m[1][2] = sine;
		m[2][1] = -sine;
		m[2][2] = cosine;
		multmatrix(m);

		/*
		 * Draw  circles...
		 */
		delta_r = (r1-r0)/circle_precision;
		for(i=0,r=r0;i<circle_precision;i++,r+=delta_r) circle (r);
	popmatrix();
}
/*
 * read_poly
 *
 * 	Read in and draw a polygon.
 */
read_n_drawpolygon(oid,mid,name,fargs)
short oid,mid;
char *name;
FUNARGS *fargs;
{
	float	x, y, z, xp, yp, zp, x0, y0, z0;
	int	i, k, nv;

	nv = fargs->nfargs/3;
	x0 = fargs->farg[0];
	y0 = fargs->farg[1];
	z0 = fargs->farg[2];
	k = 3;
	move(x0, y0, z0);

	xp = x0;
	yp = y0;
	zp = z0;

	for (i = 1; i < nv; i++) {
		x = fargs->farg[k++];
		y = fargs->farg[k++];
		z = fargs->farg[k++];

		draw(x, y, z);
		if (xp == x && yp == y && zp == z) {
			char buf[128];
			warn("p: point same as previous point",name);
			sprintf(buf, "%g %g %g = %g %g %g", xp, yp, zp, x, y, z);
			warn(buf,name);
		}

		xp = x;
		yp = y;
		zp = z;
	}
	/*
	 * Connect last to first...
	 */
	draw(x0, y0, z0);
}
int unsupported(oid,mid,name,fargs)
short oid,mid;
char *name;
FUNARGS *fargs;
{
        warn("MC does not support this surface type",name);
}

int (*read_n_drawobject[MAXPRIMITIVES])()={
        read_n_drawpolygon,
        read_n_drawsphere,
        read_n_drawsphere,
        read_n_drawcylinder,
        read_n_drawcylinder,
        read_n_drawcone,
        read_n_drawcone,
        read_n_drawring,
	unsupported,
	unsupported
};

set_modifier_color()
{
	double r,g,b;
	int i;
	for(i=0;i<nmodifiers;i++){
		r = modifier_table.margs[i].farg[0];
		g = modifier_table.margs[i].farg[1];
		b = modifier_table.margs[i].farg[2];
                switch(modifier_table.otyp[i]){
                case LIGHT:
                case ILLUM:
                case GLOW :
                case SPOTLIGHT: {
				double max;
				r /= 255.; g /= 255.; b /=255.;
				max=r;
				if (g>max) max = g;
				if (b>max) max = b;
				r /= max; g /= max; b /= max;
				}
				break;
                case GLASS:
                case INTERFACE:
                case TRANS:
                        r = (1.-r);
                        g = (1.-g);
                        b = (1.-b);
                        break;
                default :
                        break;
                }
		if (r>1.) r=1.; if (g>1.) g=1.; if (b>1.) b=1.;
                setcolor(8+i,r,g,b);
	}
}

drawobj()
{
extern int window_x_size,window_y_size,view_type;
	int i;
	set_modifier_color();
	for(i=0;i<nobjects;i++){
		color(8+obj[i].omod);
		read_n_drawobject[obj[i].otyp](
			obj[i].otyp,
			obj[i].omod,
			obj[i].onam,
			&(obj[i].oargs));
		if (window_exposed()){
		        color(BLACK);
        		clear(0,0,window_x_size,window_y_size);
        		if (view_type == FOUR) draw_partition();
			i = -1;
		}
	}
}
