#include <stdio.h>
#include <math.h>
#include <malloc.h>

#include "GraphicsGems.h"
#include "data_structure.h"
#include "objects.h"
extern int verbose_flag;
#define adjust_limits(o) {\
		xlo=x2voxel(volume_grid,(o).bbox.min.x);\
		ylo=y2voxel(volume_grid,(o).bbox.min.y);\
		zlo=z2voxel(volume_grid,(o).bbox.min.z);\
		xhi=x2voxel(volume_grid,(o).bbox.max.x);\
		yhi=y2voxel(volume_grid,(o).bbox.max.y);\
		zhi=z2voxel(volume_grid,(o).bbox.max.z);\
		\
		if (xlo >= volume_grid.x_subdivision) \
			xlo = volume_grid.x_subdivision-1;\
		if (ylo >= volume_grid.y_subdivision) \
			ylo = volume_grid.y_subdivision-1;\
		if (zlo >= volume_grid.z_subdivision) \
			zlo = volume_grid.z_subdivision-1;\
		if (xhi >= volume_grid.x_subdivision) \
			xhi = volume_grid.x_subdivision-1;\
		if (yhi >= volume_grid.y_subdivision) \
			yhi = volume_grid.y_subdivision-1;\
		if (zhi >= volume_grid.z_subdivision) \
			zhi = volume_grid.z_subdivision-1;\
}
static void spacially_sub_divide()
/* Compute the object list for each voxel. */
{
	int i;

	if (volume_grid.voxels==NULL)error("Null Voxel List.");

	for(i=0; i < number_objects; i++){
		int x,y,z,xlo,ylo,zlo,xhi,yhi,zhi;
		adjust_limits(object[i]);
		for (x = xlo; x <= xhi; x++)
		   for (y = ylo; y <= yhi; y++)
		      for (z = zlo; z <= zhi; z++){
		         Voxel *v=voxel_addr(volume_grid,x,y,z);
		         if (v->nobjects==0)
		            if ((v->intersecting_object_list=
		             (int *)malloc(sizeof(int)*number_objects))
						==NULL) error(
				"Cannot allocate Voxel object list");
		         v->intersecting_object_list[v->nobjects]=i;
		         (v->nobjects)++;
		      }
	}

#if defined(DEBUG)
	/* Check whether the object voxel classification is correct. */
	/* 
	   Step I :
		Check if the Bbox of the objects in the voxel list
		really intersects the Voxel extent.
	*/
	{
	int bounds_overlap();
	Voxel *vox=volume_grid.voxels;
	Box3 b1;
	int i,j,k,n,m;
        for (i=0,b1.min.x=volume_grid.extent.min.x;
        i < volume_grid.x_subdivision; i++, b1.min.x+=volume_grid.voxel_size.x)
        for (j=0, b1.min.y = volume_grid.extent.min.y;
        j<volume_grid.y_subdivision;j++,b1.min.y+=volume_grid.voxel_size.y)
        for (k=0, b1.min.z= volume_grid.extent.min.z;
        k<volume_grid.z_subdivision; k++,b1.min.z+=volume_grid.voxel_size.z,vox++){
		V3Add(&(b1.min),&(volume_grid.voxel_size),&(b1.max));
		for(n=0; n < vox->nobjects; n++)
		if (bounds_overlap(&b1,&(object[vox->intersecting_object_list[n]].bbox))==0)
			fprintf(stderr, "Step I Mismatch in Voxel[%d][%d][%d]\n", i,j,k);
	}
	/*
	   Step II :
		Check for each object Bbox intersection with each Voxel.
		For a valid intersection check if the Object belongs to
		the Voxel list. If it does not NOTIFY.
	*/
	for (n=0; n< number_objects;n++)
		for (i=0,b1.min.x=volume_grid.extent.min.x,vox=volume_grid.voxels;
		i < volume_grid.x_subdivision; i++,b1.min.x+=volume_grid.voxel_size.x)
                for (j=0, b1.min.y = volume_grid.extent.min.y;
                j<volume_grid.y_subdivision;j++,b1.min.y+=volume_grid.voxel_size.y)
                for (k=0, b1.min.z= volume_grid.extent.min.z;
                k<volume_grid.z_subdivision;k++,b1.min.z+=volume_grid.voxel_size.z,vox++){
			V3Add(&(b1.min),&(volume_grid.voxel_size),&(b1.max));
			/* Check if belongs to the current voxel */
			if (bounds_overlap(&b1,&(object[n].bbox))){
				int exists=0;
				/* Then check if it is in the list or has been missed out.*/
				for(m=0; m < vox->nobjects; m++)
					if (n == vox->intersecting_object_list[m]){
						exists=1;
						break;
					}
				if (!exists)
					fprintf(stderr,
				       "Step II Mismatch in object %d Voxel[%d][%d][%d].\n",
					n,i,j,k);
			}
		}
	}
#endif
}
#undef adjust_limits
preprocess()
/*
1. Initialising the grid data structure s.a.
		area of the grid element.

2. Parameterization and related work for all object surfaces.
	For Quadrilateral :
		1) Parameterize the quadrilateral from the given points.
		2) Compute Plane_Normal and Plane_Constant. 
		3) Compute a local co-ordinate system U,V,N and
			Compute a 4 x 4 Matrix for transformation to the
			local co-ordinate system.
		4) Compute the Constants for inverse Mapping of a point 
			on the quadrilateral to (u,v) parameter.
	For Sphere :
	For Other Objects :
		.
		.
		.
		.
3. Cover the whole scene with a bounding box with faces parallel to Major Planes.

4. Divide the Bounding Box into predefined number of voxels (XDiv * YDiv * ZDiv)
   and associate with each box a list of objects.

*/
{
	int i,j,k;
	Point3 min0;
	Voxel *vox;

	/* Initialise the Bounding Box */
	volume_grid.extent.min.x=
		volume_grid.extent.min.y=
			volume_grid.extent.min.z= LARGE;
	volume_grid.extent.max.x=
		volume_grid.extent.max.y=
			volume_grid.extent.max.z= -LARGE;
	for (i=0; i < number_objects; i++){
		ofunc[object[i].surface_geometry_type].object_specific_preprocess(
			object[i].grid,
			object[i].grid_h_reso,object[i].grid_v_reso,
			object[i].object_specific_structure,
			&(object[i].bbox),
			&(volume_grid.extent)
		);
		object[i].mail_box.ray_num= UNDEFINED;
	}
	/*
		Now a tight scene volume bound is ready.
		Loosen the volume extents a bit, to allow for the arithmatic errors.
	*/
	
	volume_grid.extent.min.x-=EPSILON;volume_grid.extent.max.x+=EPSILON;
	volume_grid.extent.min.y-=EPSILON;volume_grid.extent.max.y+=EPSILON;
	volume_grid.extent.min.z-=EPSILON;volume_grid.extent.max.z+=EPSILON;
	

	volume_grid.voxel_size.x=(volume_grid.extent.max.x-volume_grid.extent.min.x)/
			volume_grid.x_subdivision;
	volume_grid.voxel_size.y=(volume_grid.extent.max.y-volume_grid.extent.min.y)/
			volume_grid.y_subdivision;
	volume_grid.voxel_size.z=(volume_grid.extent.max.z-volume_grid.extent.min.z)/
			volume_grid.z_subdivision;
	volume_grid.voxel_density.x=1.0/volume_grid.voxel_size.x;
	volume_grid.voxel_density.y=1.0/volume_grid.voxel_size.y;
	volume_grid.voxel_density.z=1.0/volume_grid.voxel_size.z;

	min0=volume_grid.extent.min;
	for(i=0,vox=volume_grid.voxels;i<volume_grid.x_subdivision;
		i++,min0.x+=volume_grid.voxel_size.x){
		Point3 min1;
		min1=min0;
		for(j=0;j<volume_grid.y_subdivision;
			j++,min1.y+=volume_grid.voxel_size.y){
			Point3 min2;min2=min1;
			for(k=0;k<volume_grid.z_subdivision;
				k++,vox++,min2.z+=volume_grid.voxel_size.z){
				vox->extent.min=min2;
				V3Add(&(vox->extent.min),
					&(volume_grid.voxel_size),
					&(vox->extent.max));
			}
		}
	}

	spacially_sub_divide();
#if defined (DEBUG)
	{
	int j,n=volume_grid.x_subdivision*volume_grid.y_subdivision*volume_grid.z_subdivision;
	char *counter=(char *)malloc(number_objects);
	if (counter==NULL) error("Cannot allocate counter.");
	if(verbose_flag) fprintf(stderr,"Scene Bounding Box extents are <%g,%g,%g> to <%g,%g,%g>.\n",
	      volume_grid.extent.min.x,volume_grid.extent.min.y,volume_grid.extent.min.z,
	      volume_grid.extent.max.x,volume_grid.extent.max.y,volume_grid.extent.max.z);
	
	for(i=0; i<n;i++)
		for(j=0;j<volume_grid.voxels[i].nobjects;j++)
			counter[volume_grid.voxels[i].intersecting_object_list[j]]=1;
	if(verbose_flag) fprintf(stderr,"Objects Not accounted for in Voxel list are : <");
	for (i=0;i<number_objects;i++)
		if(counter[i]==0) fprintf(stderr,"%d ",i);
	if(verbose_flag) fprintf(stderr,">\n");
	}
#endif
}
