/*
		Author: S.N.pattanaik
*/

#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <math.h>

#include "GraphicsGems.h"
#include "data_structure.h"
#include "render.h"
#include "vox.h"

#include "raddecl.h"

#define Hemicube_Reso 50

#ifdef __BORLANDC__
extern unsigned int _stklen=50*1024;
#endif

int matherr(struct exception *erre) {

	char errorstr[][10]={"DOMAIN","SING","OVERFLOW","UNDERFLOW","TLOSS","PLOSS"};

	printf("MATH ERROR: Function \n",erre->name);
	printf("Type %s \n",errorstr[erre->type-1]);
	printf("Press a key to continue\n");
	getche();

	return(0);
	}


#include <signal.h>

typedef void (*fptr)();

void Catcher(int *reglist) {
   printf("Caught error!\n"); /* make return AX = 3 */
}


/* Begin Global Variables */
	int color_channels=0;
	int number_objects=0;
	int number_volumes=0;
	int light_sources=0;
	Obj *object;
	Source source[MAXSOURCES];
	Volume_grid volume_grid;
	ViewParameter view;
	double brightness_mult_factor=1.;
	int total_surface_grid_points;
	long total_rays=1;
	int intensity_out_flag=For_Grids_Only;
	int verbose_output_flag=0;
	int hemicube_resolution=Hemicube_Reso;
	int verbose_flag=0;
	int output_format;
/* End Global Variables */

static int progressive_distribution_flag = 0;

int error(s)
char *s;
{
	fprintf(stderr,"ERROR : %s\n",s);
	exit(0);
}
int show_usage()
{
	fprintf(stderr,"USAGE : rad [options] image_file < scene data\n");
	fprintf(stderr,"Options :\n");
	fprintf(stderr,"\t-v : for verbose output to STDERR.\n\t\tDefault : Silent.\n");
	fprintf(stderr,"\t-s shading type (Flat/Gouraud).\n\t\tDefault : %s.\n",
		(view.shading_type==FLAT)?"Flat":
			(view.shading_type==GOURAUD)?"Gouraud":
			"Gouraud+TrilinearInterpolation");
	fprintf(stderr,"\t-i intensity file (precomputed intensity input).\n\t\tDefault : compute.\n");
	fprintf(stderr,"\t-O intensity file (for intensity output for main surfaces).\n");
	fprintf(stderr,"\t-o intensity file (for intensity output for grid) points.\n");
	fprintf(stderr,"\t-b <Brightness Multiplication Factor>.\n\t\tDefault : %g.\n",
		brightness_mult_factor);
	fprintf(stderr,"\t-p <no. of passes>. Carry out analytical(Radiosity) solution\n\t\tby Progressive Distribution.\n");
	fprintf(stderr,"\t-P <RayTracing/Scan>. Carry out hemicube projection by ray tracing or scan conversion.\n");
	fprintf(stderr,"\t-H <Hemicube Resolution>.\n\t\tDefault Resolution : %d.\n",hemicube_resolution);
#if defined(__BORLANDC__) || defined(__WATCOMC__)
	fprintf(stderr,"\t-f RLE/RADIANCE/RAW/TEXT.\n\t\tDefault Output Format : %s.\n",
		(output_format==Rle)?"RLE":
		(output_format==Radiance)?"RADIANCE":
		(output_format==Raw)?"RAW":"TEXT"
	);
#else
	fprintf(stderr,"\t-f RLE/RADIANCE/SUNRASTER/RAW/TEXT.\n\t\tDefault Output Format : %s.\n",
		(output_format==Rle)?"RLE":
		(output_format==Radiance)?"RADIANCE":
		(output_format==Sunraster)?"SUNRASTER":
		(output_format==Raw)?"RAW":"TEXT"
	);
#endif
/*
	fprintf(stderr,"NOTE : If number of frames>1\n\tthen the image files are named with the frame number appended\n\tto the specified file name.\n");
*/
	fprintf(stderr,"HARDCODED OPTIONS : Maybe changed by editing data_structure.h.\n");
	fprintf(stderr,"\tMAXCHANNELS : %d.\n",MAXCHANNELS);
	fprintf(stderr,"\tMAXSOURCES : %d.\n",MAXSOURCES);
	exit(0);
}
void init_view()
{
		view.shading_type=GOURAUD;
#if defined (RADIANCE)
	output_format = Radiance;
#else
#if defined (RLE)
	output_format = Rle;
#else
#if defined (SUNRASTER) && !defined(__BORLANDC__) && !defined(__WATCOMC__)
	output_format = Sunraster;
#else
#if defined (RAW)
	output_format = Raw;
#else
	output_format = Text;
#endif
#endif
#endif
#endif
}
static int progressive_passes= -1;
int main(argc,argv)
int argc;
char **argv;
{
	int input();
	long render();
	long solve_analytically();
	int parse_parameters();
	int precomputed_intensity;
	FILE *in_intfile=NULL,*out_intfile=NULL;
	char *imagefl;


#ifdef __BORLANDC__
   signal(SIGFPE, (fptr)Catcher);  /* cast Catcher to appropriate type */
#endif

//   signal(SIGFPE, SIG_IGN);  /* cast Catcher to appropriate type */

	init_view();
	precomputed_intensity=
		parse_parameters(argc,argv,
			&imagefl,&in_intfile,&out_intfile);
	total_surface_grid_points=input();
	if (verbose_flag)fprintf(stderr,"Input Complete.\n");
	preprocess();
	if (verbose_flag)fprintf(stderr,"Preprocessing Complete.\n");
	if (precomputed_intensity) read_in_intensity(in_intfile);
	else {
		long progressive_radiosity();
		long fullmatrix_radiosity();
		total_rays+=(progressive_distribution_flag)
			?progressive_radiosity(progressive_passes,
				0,out_intfile)
			:fullmatrix_radiosity(0,out_intfile);
	}
	if (out_intfile!=NULL) fclose(out_intfile);
	total_rays+=render(imagefl);
}
 
int parse_parameters(argc,argv,imagefl,in_intfile,out_intfile)
int argc;
char **argv;
char **imagefl;
FILE **in_intfile,**out_intfile;
{
#define invalid_filename(s) (!isalnum(s[0]))

	int index=1;
	int precomputed_intensity=0;
	extern int ProjectionByRaytracing;

	if (argc < 2) show_usage();
	while((index < argc) && (argv[index][0] == '-')){
		switch(argv[index][1]){
		case 'p' : progressive_distribution_flag = 1;
			   index++; if (index >= argc) show_usage();
			   sscanf(argv[index],"%d",&progressive_passes);
			   break;
		case 'H' : index++; if (index >= argc) show_usage();
			   sscanf(argv[index],"%d",
				&hemicube_resolution);
			   break;
		case 's' : /* Shading Type : Flat/Gouraud.
			      Default is Flat. */
			   index++;  if (index >= argc) show_usage();
			   if (strcmp(argv[index],"Gouraud")==0)
				view.shading_type=GOURAUD;
			   else if(strcmp(argv[index],"GouraudPlus")==0)
				view.shading_type=GOURAUD_PLUS;
			   else if (strcmp(argv[index],"Flat")==0)
				view.shading_type=FLAT;
			   else show_usage();
			   break;
		case 'f' : /* Output Format */
			   index++;  if (index >= argc) show_usage();
			   if (strcmp(argv[index],"RLE")==0)
				output_format=Rle;
			   else if(strcmp(argv[index],"RADIANCE")==0)
				output_format=Radiance;
#if !defined(__BORLANDC__) && !defined(__WATCOMC__)
			   else if (strcmp(argv[index],"SUNRASTER")==0)
				output_format=Sunraster;
#endif
			   else if (strcmp(argv[index],"RAW")==0)
				output_format=Raw;
			   else if (strcmp(argv[index],"TEXT")==0)
				output_format=Text;
			   else show_usage();
			   break;
		case 'P' : /* Selecting projection method */
			   index++;  if (index >= argc) show_usage();
			   if (strcmp(argv[index],"RayTracing")==0)
				ProjectionByRaytracing=1;
			   else if (strcmp(argv[index],"Scan")==0)
				ProjectionByRaytracing=0;
			   else show_usage();
			   break;
		case 'i' : /* Pre computed intensity */
			   index++; if (index >= argc) show_usage();
			   if (invalid_filename(argv[index]))
				error("Invalid intensity filename.");
			   *in_intfile=fopen(argv[index],"r");
			   if (*in_intfile == NULL) 
				error("Precomputed Intensity File Does Not Exist.");
			   else{
				precomputed_intensity=1;
			   }
			   break;
		case 'O' : intensity_out_flag=For_Main_Surfaces_Only;
		case 'o' :
			   if (argv[index][2]=='v') verbose_output_flag = 1;
			   index++; if (index >= argc) show_usage();
			   if (invalid_filename(argv[index]))
				error("Invalid Intensity file name.");
#if defined(__MSDOS__) || defined(__WATCOMC__)
			   if ((*out_intfile=fopen(argv[index],"wb"))==NULL)
				error("Cannot write to intensity file.");
#else
			   if ((*out_intfile=fopen-(argv[index],"w"))==NULL)
				error("Cannot write to intensity file.");
#endif
			   break;
		case 'b' : /* Brightness multiplication factor. */
			   index++; if (index >= argc) show_usage();
			   sscanf(argv[index],"%lf",&brightness_mult_factor);
			   break;
		case 'v' : verbose_flag = 1; break;
		default  : fprintf(stderr,"Option %s not supported.\n",
					argv[index]);
			   break;
		}
		index++;
	}
		if (argc > index) {
				if (invalid_filename(argv[index]))
                                error("Invalid Image filename.");
                *imagefl=argv[index];
	}
	if (verbose_flag){
	fprintf(stderr,"%s.\n",
		(view.shading_type==GOURAUD)
		?"Gouraud Shading"
		:(view.shading_type==FLAT)?"Flat Shading"
			:"Gouraud+TrilinearInterpolation");
	fprintf(stderr,"Image Format : %s.\n",
		(output_format==Rle)?"RLE":
		(output_format==Radiance)?"RADIANCE":
#if !defined(__BORLANDC__) && !defined(__WATCOMC__)
		(output_format==Sunraster)?"SUNRASTER":
#endif
		(output_format==Raw)?"RAW":"TEXT"
	);
	if (precomputed_intensity)
		fprintf(stderr,"Uses Precomputed Intensity.\n");
	}
	return(precomputed_intensity);
}

Vector3 transform_vector(v,m)
Vector3 *v;
Matrix4 *m;
{
	Vector3 result;
	result.x= v->x * m->element[0][0]
		+ v->y * m->element[1][0]
		+ v->z * m->element[2][0];
	result.y= v->x * m->element[0][1]
		+ v->y * m->element[1][1]
		+ v->z * m->element[2][1];
	result.z= v->x * m->element[0][2]
		+ v->y * m->element[1][2]
		+ v->z * m->element[2][2];
	return(result);
}

void align_axis_to_Z(v,m)
Vector3 *v;
Matrix4 *m;
{
	m->element[0][3]= m->element[1][3]= m->element[2][3]=
		m->element[3][0]=m->element[3][1]=m->element[3][2]=0.0;
	m->element[3][3]=1.0;
	if (fabs(v->y) < EPSILON && fabs(v->z) < EPSILON){
		m->element[0][0]=m->element[0][1]=
			m->element[1][0]=m->element[1][2]=
			m->element[2][1]=m->element[2][2]=0.;
		m->element[1][1] = 1.;
		m->element[0][2] = v->x;
		m->element[2][0] = -v->x;
	}
	else{
		double len = sqrt(v->y*v->y+v->z*v->z);
		m->element[0][0] = len; m->element[0][1] = 0.; m->element[0][2] = v->x;
		m->element[1][0] = -v->x*v->y/len; m->element[1][1] = v->z/len; m->element[1][2] = v->y;
		m->element[2][0] = -v->x*v->z/len; m->element[2][1] = -v->y/len; m->element[2][2] = v->z;
	}
}

#define exchange(a,b) {temp=b; b = a; a = temp;}
void align_Z_to_axis(v,m)
Vector3 *v;
Matrix4 *m;
{
	double temp;
	align_axis_to_Z(v,m);
	/* Transpose */
	exchange(m->element[1][0],m->element[0][1])
	exchange(m->element[2][0],m->element[0][2])
	exchange(m->element[2][1],m->element[1][2])
}
#undef exchange

void mirror_reflection(N,dir,o)
/*
	IMPORTANT
	---------
	Modifies the vector dir.
*/
Vector3 *N;		/* Input */
Vector3 *dir;		/* Input and Output */
Obj *o;			/* Input */
{
	double     N_dot_V_mul_2=2.0*V3Dot (N,dir);

	dir->x -= N_dot_V_mul_2 * N->x;
	dir->y -= N_dot_V_mul_2 * N->y;
	dir->z -= N_dot_V_mul_2 * N->z;
}

static int check_overlap(min1,max1,min2,max2)
double min1,max1,min2,max2;
{
        if (min1 > min2){
                if (min1 > max2) return(0);
        }
        else if (min2 > max1) return(0);
        return(1);
}

int bounds_overlap(b1,b2)
/*
        3D Bounds overlap iff
                X extents overlap and
                Y extents overlap and
                Z extents overlap.
*/
Box3 *b1,*b2;
{
        int check_overlap();
        /* Check X extent */
        if (check_overlap(b1->min.x,b1->max.x,b2->min.x,b2->max.x))
                /* Check Y extent */
                if (check_overlap(b1->min.y,b1->max.y,b2->min.y,b2->max.y))
                        /* Check Z extent */
						if (check_overlap(b1->min.z,b1->max.z,b2->min.z,b2->max.z))
                                return(1);
        return(0);
}

Point3 get_bilinear(q,u,v)
Quadrilateral *q;
double u,v;
{
        Point3 p;
        p.x = q->P00.x*(1.0-u)*(1.0-v)
              + q->P10.x*u*(1.0-v)
              + q->P01.x*(1.0-u)*v
              + q->P11.x*u*v;
        p.y = q->P00.y*(1.0-u)*(1.0-v)
              + q->P10.y*u*(1.0-v)
              + q->P01.y*(1.0-u)*v
              + q->P11.y*u*v;
        p.z = q->P00.z*(1.0-u)*(1.0-v)
              + q->P10.z*u*(1.0-v)
              + q->P01.z*(1.0-u)*v
              + q->P11.z*u*v;
        return(p);
}

