#include <stdio.h>
#include <string.h>
#include <math.h>
#include <ctype.h>

#include "preview.h"
float bound_min_x,bound_min_y,bound_min_z=LARGE;
float bound_max_x,bound_max_y,bound_max_z= -LARGE;
#if defined(RADFILTER)
float cum_area=0.;
#endif
#define MIN(a,b) (((a)>(b))?(b):(a))
#define MAX(a,b) (((a)>(b))?(a):(b))
#ifdef notdef
#if defined(RADFILTER)
double
#else
int
#endif
      polygon_bound(),
      sphere_bound(),
      cylinder_bound(),
      cone_bound(),
      ring_bound(),
      no_bound();
#endif
#if defined(RADFILTER) 
void project_point(n,x,y,z,px,py)
int n;
double x,y,z,*px,*py;
{
	switch(n){
	case 0: *px = y; *py = z; break;
	case 1: *px = z; *py = x; break;
	case 2: *px = x; *py = y; break;
	}
}
double trapez_area(n,f,nx,ny,nz)
int n;
double f[],nx,ny,nz;
{
	double area=0.,lastx,lasty,curx,cury;
	int i,dominant_axis;
	if (fabs(nx) >= fabs(ny) && fabs(nx) >= fabs(nz))
		dominant_axis=0;
	else if (fabs(ny) >= fabs(nx) && fabs(ny) >= fabs(nz))
		dominant_axis=1;
	else dominant_axis=2;
	project_point(dominant_axis,
		f[(n-1)*3],f[(n-1)*3+1],f[(n-1)*3+2],&lastx,&lasty);
	for(i=0;i<n;i++,lastx=curx,lasty=cury){
		project_point(dominant_axis,
			f[i*3],f[i*3+1],f[i*3+2],&curx,&cury);
		area += (curx-lastx)*(cury+lasty);
	}
	area /= ((dominant_axis==0)?nx:((dominant_axis==1)?ny:nz));
	return(0.5*fabs(area));
}
double
#endif
polygon_bound(fargs,name)
char *name;
FUNARGS *fargs;
{
	int i,k,nv=fargs->nfargs/3;;
	if (fargs->nfargs < 9)
		error("wanted an > 3 for verticies",name);
        for (i = k = 0; i < nv; i++,k+=3) {
		double x,y,z;
		x = fargs->farg[k],
		y = fargs->farg[k+1]; 
		z = fargs->farg[k+2];
                bound_min_x = MIN(bound_min_x,x);
		bound_max_x = MAX(bound_max_x,x);
                bound_min_y = MIN(bound_min_y,y);
		bound_max_y = MAX(bound_max_y,y);
                bound_min_z = MIN(bound_min_z,z);
		bound_max_z = MAX(bound_max_z,z);
        }
#if defined(RADFILTER)
	{
	double area=0.,length;
	int i,j;
	double nx,ny,nz;
	nx = ny = nz = 0.;
        for(i=0,j=1;i<nv;i++,j++){
                if (j==nv)j=0;
                nx += (fargs->farg[i*3+1]-fargs->farg[j*3+1])*
			(fargs->farg[i*3+2]+fargs->farg[j*3+2]);
                ny += (fargs->farg[i*3+2]-fargs->farg[j*3+2])*
			(fargs->farg[i*3]+fargs->farg[j*3]);
                nz += (fargs->farg[i*3]-fargs->farg[j*3])*
			(fargs->farg[i*3+1]+fargs->farg[j*3+1]);
        }
        length = sqrt(nx*nx+ny*ny+nz*nz);
	if (length != 0.){
		nx/=length; ny/=length; nz/=length;
	}
	else error("Zero Vector Normal",name);
	area = trapez_area(nv,fargs->farg,nx,ny,nz);
	cum_area += area;
	return(area);
	}
#endif
}
#if defined(RADFILTER)
double
#endif 
sphere_bound(fargs,name)
char *name;
FUNARGS *fargs;
{
	double x,y,z,r;
        if (fargs->nfargs != 4)
                error("Expected to read 4 numbers",name);
	x = fargs->farg[0];
	y = fargs->farg[1];
	z = fargs->farg[2];
	r = fargs->farg[3];
        bound_min_x = MIN(bound_min_x,x-r);
		bound_max_x = MAX(bound_max_x,x+r);
        bound_min_y = MIN(bound_min_y,y-r);
		bound_max_y = MAX(bound_max_y,y+r);
        bound_min_z = MIN(bound_min_z,z-r);
		bound_max_z = MAX(bound_max_z,z+r);
#if defined(RADFILTER)
	{
	double area= 4.*PI*r*r;
	cum_area += area;
	return(area);
	}
#endif
}
static void disk_extents(r,cx,cy,cz,nx,ny,nz,name)
double r,cx,cy,cz,nx,ny,nz;
char *name;
{
	double extent;
	/* Normalize Normal */
	double length=sqrt(nx*nx+ny*ny+nz*nz);
	if (length != 0.){
		nx /= length;
		ny /= length;
		nz /= length;
	}
	else error("Zero Vector",name);
	extent = r*sqrt(1.-nx*nx);
        bound_min_x = MIN(bound_min_x,cx-extent);
	bound_max_x = MAX(bound_max_x,cx+extent);
	extent = r*sqrt(1.-ny*ny);
        bound_min_y = MIN(bound_min_y,cy-extent); 
	bound_max_y = MAX(bound_max_y,cy+extent);
	extent = r*sqrt(1.-nz*nz);
        bound_min_z = MIN(bound_min_z,cz-extent); 
	bound_max_z = MAX(bound_max_z,cz+extent);
}
#if defined(RADFILTER)
double
#endif
cylinder_bound(fargs,name)
char *name;
FUNARGS *fargs;
{
	double height,area;
	double hvector_x=fargs->farg[3]-fargs->farg[0],
		hvector_y=fargs->farg[4]-fargs->farg[1],
		hvector_z=fargs->farg[5]-fargs->farg[2];
        if (fargs->nfargs != 7)
                error("Expected to read 7 numbers",name);
	disk_extents(fargs->farg[6],
		fargs->farg[0],fargs->farg[1],fargs->farg[2],
		hvector_x,hvector_y,hvector_z,name);
	disk_extents(fargs->farg[6],
		fargs->farg[3],fargs->farg[4],fargs->farg[5],
		hvector_x,hvector_y,hvector_z,name);
	height = sqrt(hvector_x*hvector_x+hvector_y*hvector_y
			+hvector_z*hvector_z);
#if defined(RADFILTER)
	{
	float area= 2.*PI*fargs->farg[6]*height;
	cum_area += area;
	return(area);
	}
#endif
}
#if defined(RADFILTER) 
double
#endif
cone_bound(fargs,name)
char *name;
FUNARGS *fargs;
{
	double hvector_x=fargs->farg[3]-fargs->farg[0],
		hvector_y=fargs->farg[4]-fargs->farg[1],
		hvector_z=fargs->farg[5]-fargs->farg[2];
	double slant_height;
	double delta_r=fabs(fargs->farg[6]-fargs->farg[7]);
        if (fargs->nfargs != 8)
                error("Expected to read 8 numbers",name);
	disk_extents(fargs->farg[6],
		fargs->farg[0],fargs->farg[1],fargs->farg[2],
		hvector_x,hvector_y,hvector_z,name);
	disk_extents(fargs->farg[7],
		fargs->farg[3],fargs->farg[4],fargs->farg[5],
		hvector_x,hvector_y,hvector_z,name);
	slant_height = sqrt(hvector_x*hvector_x+hvector_y*hvector_y
			+hvector_z*hvector_z + delta_r*delta_r);
#if defined(RADFILTER)
	{
	float area= PI*(fargs->farg[6]+fargs->farg[7])*slant_height;
	cum_area += area;
	return(area);
	}
#endif
}
#if defined(RADFILTER) 
double
#endif
ring_bound(fargs,name)
char *name;
FUNARGS *fargs;
{
	double outer_radius,inner_radius;
        if (fargs->nfargs != 8)
                error("Expected to read 8 numbers",name);
	outer_radius = (fargs->farg[7]>fargs->farg[6])
			?fargs->farg[7]:fargs->farg[6];
	inner_radius = (fargs->farg[7]>fargs->farg[6])
			?fargs->farg[6]:fargs->farg[7];
	disk_extents(outer_radius,
		fargs->farg[0],fargs->farg[1],fargs->farg[2],
		fargs->farg[3],fargs->farg[4],fargs->farg[5],name);
#if defined(RADFILTER)
	{
	float area=PI*(outer_radius*outer_radius
			- inner_radius*inner_radius);
	cum_area += area;
	return(area);
	}
#endif
}
#if defined(RADFILTER) 
double
#endif
no_bound(fargs,name)
char *name;
FUNARGS *fargs;
{
#if defined(RADFILTER) 
	return (0.) ;
#endif
}
#if defined(RADFILTER) 
double
#else
int 
#endif
(*specific_object_bound[MAXPRIMITIVES])()={
	polygon_bound,
	sphere_bound,
	sphere_bound,
	cylinder_bound,
	cylinder_bound,
	cone_bound,
	cone_bound,
	ring_bound,
	no_bound,
	no_bound
};

fgetline(s, n, fp)
char  *s;
int  n;
FILE  *fp;
{
        register char  *cp = s;
        register int  c = EOF;

        while (--n > 0 && (c = getc(fp)) != EOF) {
                if (c == '\n' && (cp == s || cp[-1] != '\\'))
                        break;
                *cp++ = c;
        }
        *cp = '\0';
}

int readobj(input)
char  *input;
{
	FILE  *infp;
	char  buf[MAXSTR];
	register int  c;

	if (input == NULL) {
		infp = stdin;
		input = "standard input";
#ifndef __MSDOS__
	} else if (input[0] == '!') {
		if ((infp = popen(input+1, "r")) == NULL) {
			error("cannot execute",input);
		}
#endif
	} else if ((infp = fopen(input, "r")) == NULL) {
		return(0);
	}
	while ((c = getc(infp)) != EOF) {
		if (isspace(c))
			continue;
		if (c == '#') {				/* comment */
			fgets(buf, sizeof(buf), infp);
		} else if (c == '!') {			/* command */
			ungetc(c, infp);
			fgetline(buf, sizeof(buf), infp);
			readobj(buf);
		} else {				/* object */
			ungetc(c, infp);
			getobject(input, infp);
		}
	}
#ifndef __MSDOS__
	if (input[0] == '!')
		pclose(infp);
	else
#endif
		fclose(infp);
	return(1);
}

/* Primary Type */
#define ALIAS    -1
#define UNKNOWN  0
#define GEOMETRY 1
#define MATERIAL 2
#define TEXTURE  3
#define PATTERN  4
#define MIXTURE  5

int getindex(table,size,s)
char *table[];
int size;
char *s;
{
	if (table==NULL) return(-1);
	for(;size>0;) if (!strcmp(table[--size],s)) return(size);
	return(-1);
}
char *surface_table[MAXPRIMITIVES]={
	"polygon","sphere","bubble","cylinder","tube","cone","cup",
	"ring","source","instance"
};
char *material_table[MAXMATERIALS]={
	"light","illum","glow","spotlight","plastic","mirror",
	"metal","trans","dielectric","interface",
	"glass","plasfunc","metfunc","plasdata","metdata"
};
char *texture_table[MAXTEXTURES]={
	"texfunc","texdata"
};
char *pattern_table[MAXPATTERNS]={
	"colorfunc", "brightfunc", "colordata",
	"brightdata", "colorpict", "colortext",
	"brighttext"
};
char *mixture_table[MAXMIXTURES]={
	"mixfunc", "mixdata", "mixtext"
};
char *savqstr(s)
char *s;
{
	char *cp;
	if ((cp = (char *)malloc(strlen(s)+1)) == NULL)
		error("Cannot allocate memory for",s);
	strcpy(cp,s);
	return(cp);
}

MTable modifier_table;
int nmodifiers=0;
OBJ obj[MaxObjects];
int nobjects=0;

#define modifier(s) getindex(modifier_table.mnames,nmodifiers,s)
#define otype(s) getindex(surface_table,MAXPRIMITIVES,s)
#define mtype(s) getindex(material_table,MAXMATERIALS,s)
#define ttype(s) getindex(texture_table,MAXTEXTURES,s)
#define ptype(s) getindex(pattern_table,MAXPATTERNS,s)
#define xtype(s) getindex(mixture_table,MAXMIXTURES,s)

getobject(name, fp)		/* read the next object */
char  *name;
FILE  *fp;
{
	char  sbuf[MAXSTR];
	short omod, otyp=UNKNOWN, otindex;
	char *onam;
	FUNARGS fargs;

	fscanf(fp, "%s", sbuf); 	/* Get Modifier */
	if(!strcmp(sbuf,"void")) omod = -1;
	else if((omod=modifier(sbuf))== -1)
		error("Undefined Modeifier",sbuf);

	fscanf(fp, "%s", sbuf); 	/* Get Object Type */
	if (!strcmp(sbuf, "alias")) otyp = ALIAS;
	else if ((otindex=otype(sbuf))!= -1){ /* GEOMETRY */
		otyp = GEOMETRY;
	}
	else if ((otindex=mtype(sbuf))!= -1){ /* MATERIAL */
		otyp = MATERIAL;
	}
	else if ((otindex=ttype(sbuf))!= -1){ /* Textures */
		otyp = TEXTURE;
	}
	else if ((otindex=ptype(sbuf))!= -1){ /* Patterns */
		otyp = PATTERN;
	}
	else if ((otindex=xtype(sbuf))!= -1){ /* Mixtures */
		otyp = MIXTURE;
	}
	else error("Unknown material/surface",sbuf);

	fscanf(fp, "%s", sbuf);		/* Get identifier */
	onam = savqstr(sbuf);
					/* Get Arguments */
	if(otyp == ALIAS){
		short oldmod;
		fscanf(fp, "%s", sbuf);
		otyp = MATERIAL;
		if ((oldmod = modifier(sbuf))== -1)
			error("Bad alias reference",sbuf);
		if (nmodifiers < MAXMODIFIERS){
			modifier_table.mnames[nmodifiers]=onam;
			modifier_table.omod[nmodifiers] = omod;
			modifier_table.otyp[nmodifiers]
				= modifier_table.otyp[oldmod];
			modifier_table.margs[nmodifiers]
				= modifier_table.margs[oldmod];
			(nmodifiers)++;
		}
		else error("Many Modifiers",onam);
		return;
	}
	if (readfargs(&fargs,fp)== -1)
		error("Cannot read object arguments.","");
	if (otyp == GEOMETRY){
		if (nobjects < MaxObjects){
			obj[nobjects].onam = onam;
			obj[nobjects].omod = omod;
			obj[nobjects].otyp = otindex;
			obj[nobjects].oargs = fargs;
#if defined(RADFILTER)
			obj[nobjects].area=
#endif
			specific_object_bound[otindex](&fargs,onam);
			nobjects++;
		}
		else error("Many Objects",onam);
	}
	else if (otyp == MATERIAL){
		if (nmodifiers < MAXMODIFIERS){
		modifier_table.omod[nmodifiers] = omod;
		modifier_table.otyp[nmodifiers] = otindex;
		modifier_table.mnames[nmodifiers]=onam;
		modifier_table.margs[nmodifiers]=fargs;
		nmodifiers++;
		}
		else error("Many Modifiers",onam);
	}
	else if (otyp == TEXTURE
		 ||
		 otyp == PATTERN
		 ||
		 otyp == MIXTURE){
		if (omod != -1){
		  warn("Cannot really support texture. So ignores",
			onam);
		  if (nmodifiers < MAXMODIFIERS){
			modifier_table.mnames[nmodifiers]=onam;
			modifier_table.omod[nmodifiers] = omod;
			modifier_table.otyp[nmodifiers]
				= modifier_table.otyp[omod];
			modifier_table.margs[nmodifiers]
				= modifier_table.margs[omod];
			(nmodifiers)++;
		  }
		  else error("Many Modifiers",onam);
		}
		else error("Does not support this Modifier.",onam);
	}
}
#undef ALIAS   
#undef UNKNOWN
#undef GEOMETRY
#undef MATERIAL

int readfargs(fa,fp)
FUNARGS *fa;
FILE *fp;
{
        char  sbuf[MAXSTR];
        int  n;
        register int  i;

        if (fscanf(fp, "%d", &n) != 1 || n < 0)
                return(-1);
        if (fa->nsargs = n) {
                fa->sarg = (char **)malloc(n*sizeof(char *));
               if (fa->sarg == NULL)
                        goto memerr;
                for (i = 0; i < fa->nsargs; i++) {
                        if (fscanf(fp, "%s", sbuf) != 1)
                                return(-1);
                        fa->sarg[i] = savqstr(sbuf);
                }
        } else
                fa->sarg = NULL;
        if (fscanf(fp, "%d", &n) != 1 || n < 0)
                return(-1);
        if (fa->niargs = n) {
                fa->iarg = (long *)malloc(n*sizeof(long));
               if (fa->iarg == NULL)
                        goto memerr;
                for (i = 0; i < fa->niargs; i++)
                        if (fscanf(fp, "%ld", &fa->iarg[i]) != 1)
                                return(-1);
        } else
                fa->iarg = NULL;
	if (fscanf(fp, "%d", &n) != 1 || n < 0)return(-1);
        if (fa->nfargs = n) {
                fa->farg = (double *)malloc(n*sizeof(double));
                if (fa->farg == NULL)
                        goto memerr;
                for (i = 0; i < n; i++)
                        if (fscanf(fp, "%lf", &fa->farg[i]) != 1)
                                return(-1);
        } else
                fa->farg = NULL;
        return(0);
memerr:
        error("out of memory in readfargs","");
}
