#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include "group.h"
#include "implemented.h"

#include <vector>
using namespace std ;

/* These are functions associated to the ShapeListElement class */
ShapeListElement::ShapeListElement(Shape* s){
  next=NULL;
  shape=s;
}
void ShapeListElement::addShape(Shape* s){
  ShapeListElement* temp;
  temp=next;
  next=new ShapeListElement(s);
  assert(next);
  next->next=temp;
}

/* These are functions associated to the Group class */
Group::Group(FILE* fp){
  if(fscanf(fp," %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg %lg", 
	    &(localTransform(0,0)),&(localTransform(1,0)),
	    &(localTransform(2,0)),&(localTransform(3,0)),
	    &(localTransform(0,1)),&(localTransform(1,1)),
	    &(localTransform(2,1)),&(localTransform(3,1)),
	    &(localTransform(0,2)),&(localTransform(1,2)),
	    &(localTransform(2,2)),&(localTransform(3,2)),
	    &(localTransform(0,3)),&(localTransform(1,3)),
	    &(localTransform(2,3)),&(localTransform(3,3))) != 16){
    fprintf(stderr, "Failed to parse group_begin for Group\n"); 
    exit(EXIT_FAILURE);
  }
  inverseTransform=localTransform.invert();
  shapeList=NULL;
}
Group::Group(Matrix m){
  localTransform=m;
  inverseTransform=localTransform.invert();
  shapeList=NULL;
}
void Group::addShape(Shape* s){
  if(shapeList==NULL){shapeList=new ShapeListElement(s);}
  else{shapeList->addShape(s);}
}
char* Group::name(void){return "group";}
void Group::write(int indent,FILE* fp){
  ShapeListElement* temp;
  int i;

  for(i=0;i<indent;i++){fprintf(fp," ");}
  fprintf(fp,"#group_begin\n");
  for(i=0;i<indent;i++){fprintf(fp," ");}
  fprintf(fp,"  %lg %lg %lg %lg\n",
	  localTransform(0,0),localTransform(1,0),
	  localTransform(2,0),localTransform(3,0));
  for(i=0;i<indent;i++){fprintf(fp," ");}
  fprintf(fp,"  %lg %lg %lg %lg\n",
	  localTransform(0,1),localTransform(1,1),
	  localTransform(2,1),localTransform(3,1));
  for(i=0;i<indent;i++){fprintf(fp," ");}
  fprintf(fp,"  %lg %lg %lg %lg\n",
	  localTransform(0,2),localTransform(1,2),
	  localTransform(2,2),localTransform(3,2));
  for(i=0;i<indent;i++){fprintf(fp," ");}
  fprintf(fp,"  %lg %lg %lg %lg\n",
	  localTransform(0,3),localTransform(1,3),
	  localTransform(2,3),localTransform(3,3));
  temp=shapeList;
  while(temp!=NULL){
    temp->shape->write(indent+2,fp);
    temp=temp->next;
  }
  for(i=0;i<indent;i++){fprintf(fp," ");}
  fprintf(fp,"#group_end\n");
}
//This does NOT do BV generation based on cost estimation
//but based on groups in the .ray file . This needs to change.
Flt Group::intersect(Ray ray,IntersectionInfo& iInfo){
	
	//inverse transform the ray so that it is in the object coordinate system
	Ray transformedRay=inverseTransform.mult(ray);
	
	//check bounding box intersection first : return if failed
	if(bBox.intersect(transformedRay)==-1)
		return -1;

	//list of all possible intersections for given ray
	vector<IntersectionInfo> isectList;
	double tnearest=INT_MAX;
	
	//iterate through primitives
	ShapeListElement * temp=shapeList;
	ShapeListElement* pClosest=NULL;
	while(temp!=NULL)
	{
		IntersectionInfo tempinfo;
		BoundingBox tempbox=temp->shape->getBoundingBox();
		Flt t=tempbox.intersect(transformedRay);
		//intersection did occur with the box..
		if(t!=-1)
		{	//check intersection with primitive in box
			t=temp->shape->intersect(transformedRay,tempinfo);
			if(t!=-1)
			{
				if(t<tnearest)
				{	tnearest=t;
					pClosest=temp;
				}
			}
		}
		temp=temp->next;
	}
	//box for group was intersected but no object inside was intersected
	if(tnearest==INT_MAX)
		return -1;
	IntersectionInfo tempinfo;
	Flt t=pClosest->shape->intersect(transformedRay,tempinfo);
	//intersection did occur
	if(t!=-1)
	{
		//transform the point of intersection to get world coordinates
		tempinfo.iCoordinate=localTransform.multPosition(tempinfo.iCoordinate);
		tempinfo.normal=localTransform.multNormal(tempinfo.normal);
		tempinfo.normal.Normalize();
		tnearest=t;
		iInfo=tempinfo;
		return t;
	}
	else
	  return -1.0;
}

//This does NOT do BV generation based on cost estimation
//but based on groups in the .ray file . This needs to change.
Flt Group::intersect(Ray ray){
	
	//inverse transform the ray so that it is in the object coordinate system
	Ray transformedRay=inverseTransform.mult(ray);
	
	//check bounding box intersection first : return if failed
	if(bBox.intersect(transformedRay)==-1)
		return -1;

	double tnearest=INT_MAX;
	
	//iterate through primitives
	ShapeListElement * temp=shapeList;
	ShapeListElement* pClosest=NULL;
	while(temp!=NULL)
	{
		BoundingBox tempbox=temp->shape->getBoundingBox();
		Flt t=tempbox.intersect(transformedRay);
		//intersection did occur with the box..
		if((t!=-1)&&(t>RAYEPS))
		{	//check intersection with primitive in box
			t=temp->shape->intersect(transformedRay);
			if(t!=-1)
			{
				if(t<tnearest)
				{	tnearest=t;
					pClosest=temp;
				}
			}
		}
		temp=temp->next;
	}
	//box for group was intersected but no object inside was intersected
	if(tnearest==INT_MAX)
		return -1;
	IntersectionInfo tempinfo;
	Flt t=pClosest->shape->intersect(transformedRay);
	//intersection did occur
	if(t!=-1)
	{
		tnearest=t;
		return t;
	}
	else
	  return -1.0;
}

BoundingBox Group::getBoundingBox(void){
	if(bCalculated)
		return bBox;
	//iterate through primitives
	ShapeListElement * temp=shapeList;
	BoundingBox box;
	while(temp!=NULL)
	{
		BoundingBox tempbox=temp->shape->getBoundingBox();
		//union
		box=box+tempbox;
		temp=temp->next;
	}
	bBox=box.transform(localTransform);
	bCalculated=true;
	return bBox;
}


void Group::free(void){
  ShapeListElement* temp;
  temp=shapeList;
  while(temp!=NULL){
    temp->shape->free();
    temp=temp->next;
  }
  delete this;
}
