#include "view.h"

static Face *TheTargetFace;
static FaceList *TheFaceList;
static LiteFaceList *TheLiteFaceList;
static Light *TheLight;
static int TheLightNo;


#define POLYGONSIZE 20

#define SXS(poly,i) (poly.polygon[(i) % poly.n].x)
#define SYS(poly,i) (poly.polygon[(i) % poly.n].y)
#define SZS(poly,i) (poly.polygon[(i) % poly.n].z)

#define XS(poly,i) (poly->polygon[(i) % poly->n].x)
#define YS(poly,i) (poly->polygon[(i) % poly->n].y)
#define ZS(poly,i) (poly->polygon[(i) % poly->n].z)

#define AT(p,i)         (p->polygon[(i) % p->n])
#define PUT(p,x)        {p.polygon[p.n++] = x;}

typedef struct{
	int n;
	Point3D polygon[POLYGONSIZE];
} MyPolygon;


static void splitPolygon(poly,plane,c,i,t,frontP,backP)
/*
  split poly into two between indices i-1 and i.
  t is the t value for the intersection.
*/
MyPolygon *poly;
PlaneEq *plane;
Classification c;
int i;
REAL t;
MyPolygon *frontP,*backP;
{
	Point3D p1,p2,p,pnew;
	int j,k,k1,k2,i2;
	Classification c1,c2,cn;
	REAL x,y,z,d1,d2;
	MyPolygon theFront,theBack;
	BOOLEAN tZero;

	/*initialise polygons*/
	theFront.n = theBack.n = 0;

	/*get the end points of the split edge*/
	p1 = AT(poly,i-1);
	p2 = AT(poly,i);

	if(ABS(t) > TOL){
		/*and compute the new point*/
		pnew.x = X(p1) + (X(p2) - X(p1))*t;
		pnew.y = Y(p1) + (Y(p2) - Y(p1))*t;
		pnew.z = Z(p1) + (Z(p2) - Z(p1))*t;
		tZero = FALSE;
	}
	else{
		tZero = TRUE;
		pnew = AT(poly,i-1);
	}

	if(c==FRONT_BACK) c1 = BACK; else c1 = FRONT;
	c2 = c1;
	/*
	classifyPoint3D(plane,&p2,&d2);
	*/
	CLASSIFYPoint3D(plane,p2,d2,cn);

	/*this is worked out as if going from FRONT to BACK*/
	/*start off the back face with this point and p2*/
	PUT(theBack,pnew);
	j = i;
	while(c2 == c1){
		PUT(theBack,AT(poly,j++));
		d1 = d2;
		p2 = AT(poly,j);
		/*
		c2 = addClassification(c2,classifyPoint3D(plane,&p2,&d2));
		*/
		CLASSIFYPoint3D(plane,p2,d2,cn);
		ADDClassification(c2,cn,c2);
	}

	/*here c2 must be BACK_FRONT and the split is between j-1, j*/
	p1 = AT(poly,j-1);
	p2 = AT(poly,j);
	t = d1/(d1-d2);

	if(ABS(t) > TOL){
		p.x = X(p1) + (X(p2) - X(p1))*t;
		p.y = Y(p1) + (Y(p2) - Y(p1))*t;
		p.z = Z(p1) + (Z(p2) - Z(p1))*t;

		PUT(theBack,p);

		PUT(theFront,p);
	}
	else PUT(theFront,AT(poly,j-1));

	i2 = j;
	while((i2%poly->n) != i){
		PUT(theFront,AT(poly,i2++));
	}
	if(tZero==FALSE) PUT(theFront,pnew);

	if(c == FRONT_BACK){
		*frontP = theFront;
		*backP = theBack;
	}
	else{
		*frontP = theBack;
		*backP = theFront;
	}
}


static Classification classifyPolygon(poly,plane,frontP,backP)
/*
  returns the classification of the face with respect
  to the plane. If the classification is CUT then the front split-off polygon
  is stored in the global Shadow
*/
MyPolygon *poly;
PlaneEq *plane;
MyPolygon *frontP,*backP;
{
	Classification c,c1,c2,cn;
	REAL d1,d2;
	register int i,j;
	REAL t;

	/*
	c = classifyPoint3D(plane,&AT(poly,0),&d1);
	*/
	CLASSIFYPoint3D(plane,AT(poly,0),d1,c);

	for(i=1;i<poly->n;++i){
		/*
		c2 = addClassification(c,classifyPoint3D(plane,&AT(poly,i),&d2));
		*/
		CLASSIFYPoint3D(plane,AT(poly,i),d2,cn);
		ADDClassification(c,cn,c2);
		if(c2 == FRONT_BACK || c2 == BACK_FRONT) {
			t = d1/(d1-d2); /*split from i-1 to i*/
			splitPolygon(poly,plane,c2,i,t,frontP,backP);
			return(c2);
		}
		else{
			c = c2;
			d1 = d2;
		}
	}

	/*closure - note if the plane intersected this last edge
				then this would have already happened*/
	return(c);
}


void samePolygon(poly,shadowFace)
/*
  In this case the shadow polygon is the same as the original.
  The point of all this is to avoid duplicating the same vertices
*/
Face *poly;
FacePtr *shadowFace;
{
	Vertex *v;
	Index *index;
	PlaneEq *pe;
	Shading *sh;

	v = VERTEXOfFace(poly);
	*shadowFace = newFace(v,INDEX(FIRSTOfFace(poly)));

	index = FIRSTOfFace(poly);
	while(index != LASTOfFace(poly)){
		index = NEXT(index);
		addToBackFace(*shadowFace,INDEX(index));
	}

	/*give same plane equation and shading as poly*/
	pe = copyPlaneEqOfFace(poly);
	sh = copyShadingOfFace(poly);
	(*shadowFace)->coeff = pe;
	(*shadowFace)->shading = sh;

	/*give the new shadowFace the same lightsAway set as poly*/
	(*shadowFace)->lightsAway = copyIndex(poly->lightsAway);
}

static void faceToPolygon(poly,face)
MyPolygon *poly;
Face *face;
{
	Index *ind;

	poly->n = 0;

	ind = face->first;
	while(ind != face->last)
	{
		poly->polygon[poly->n++] = *atVertex(face->vertex,INDEX(ind));
		ind = NEXT(ind);
	}
	poly->polygon[poly->n++] = *atVertex(face->vertex,INDEX(ind));
}

static void faceFromPolygon(poly,shadowFace)
/*constructs a new face from a polygon*/
MyPolygon *poly;
FacePtr *shadowFace;
{
	Vertex *v;
	register int i,k;
	PlaneEq *pe;
	Shading *sh;

	v = VERTEXOfFace(TheTargetFace);

	/*put the first point of the shadow into the vertex list*/
	k = NEXTFreeVertex(v);
	atPutCoordVertex(v,k,XS(poly,0),YS(poly,0),ZS(poly,0));
	*shadowFace = newFace(v,k);

	for(i=1;i<poly->n;++i){
		k = NEXTFreeVertex(v);
		atPutCoordVertex(v,k,XS(poly,i),YS(poly,i),ZS(poly,i));
		addToBackFace(*shadowFace,k);
	}

	/*give same plane equation and shading as targetFace*/
	pe = copyPlaneEqOfFace(TheTargetFace);
	sh = copyShadingOfFace(TheTargetFace);
	(*shadowFace)->coeff = pe;
	(*shadowFace)->shading = sh;

	/*give the new shadowFace the same lightsAway set as target*/
	(*shadowFace)->lightsAway = copyIndex(TheTargetFace->lightsAway);
}


void setShadowOnFace(shadowFace)
/*puts shadowFace as a shadow to the face*/
Face *shadowFace;
{
	Face *shadowOnFace,*targetFace;
	Index *ind;

	shadowFace->shading->noOfLights--;
	REDOfFace(shadowFace) -= REDLightOfFace(TheTargetFace,TheLightNo);
	GREENOfFace(shadowFace) -= GREENLightOfFace(TheTargetFace,TheLightNo);
	BLUEOfFace(shadowFace) -= BLUELightOfFace(TheTargetFace,TheLightNo);
	REDLightOfFace(shadowFace,TheLightNo) = 0.0;
	GREENLightOfFace(shadowFace,TheLightNo) = 0.0;
	BLUELightOfFace(shadowFace,TheLightNo) = 0.0;

	shadowFace->lightsAway = addToFrontIndex(shadowFace->lightsAway,TheLightNo);

	appendToIndex(&TheTargetFace->shadow,NEXTFreeFaceList(TheFaceList),&TheTargetFace->lastShadow);
	atPutFaceList(TheFaceList,NEXTFreeFaceList(TheFaceList),shadowFace);
	++TheTargetFace->noOfShadows;
}


extern Vertex *liteVertex;

void addPolygonShadowPlanesToTree(tree,poly)
Tree **tree;
MyPolygon *poly;
{
	Point3D p1,p2,q[3];
	register int i,n,k;
	PlaneEq *plane;
	LiteFace *shadowPlane;


	p1 = AT(poly,0);

	for(i=1;i<=poly->n;++i){
		/*make new shadow plane - the light source pos is always between these two*/
		k = NEXTFreeVertex(liteVertex);
		atPutCoordVertex(liteVertex,k,XS(poly,i-1),YS(poly,i-1),ZS(poly,i-1));
		shadowPlane = newLiteFace(liteVertex,k);
		k = NEXTFreeVertex(liteVertex);
		atPutCoordVertex(liteVertex,k,XS(poly,i),YS(poly,i),ZS(poly,i));
		addToBackLiteFace(shadowPlane,k);

		/*add this face to the facelist*/
		atPutLiteFaceList(TheLiteFaceList,(n=NEXTFreeFaceList(TheLiteFaceList)),shadowPlane);

		/*find the plane equation*/
		p2 = AT(poly,i);
		q[0] = p1;
		q[1] = POSITIONOfLight(TheLight);
		q[2] = p2;
		plane = Malloc(PlaneEq,1);
		normalPoint3D(q,plane);
		plane->d = detFor3Point3D(q);

		/*set the plane equation*/
		shadowPlane->coeff = plane;
		if(*tree == EMPTYTREE)
			*tree = makeRoot(n);
		else
			addLiteFaceToTree(*tree,TheLiteFaceList,n);

		/*onto next edge*/
		p1 = p2;
	}
}




void filterPolygonOnSVBSPTree();

static void onFrontPolygon(tree,poly,fromShadow)
Tree *tree;
MyPolygon *poly;
BOOLEAN fromShadow;
{
	Face *shadowFace;

	if(tree->frontNode == EMPTYTREE) {
		/*construct a new shadowFace from the poly*/
		faceFromPolygon(poly,&shadowFace);
		setShadowOnFace(shadowFace);
	}
	else
	filterPolygonOnSVBSPTree(tree->frontNode,poly,fromShadow);
}

static void onBackPolygon(tree,poly,fromShadow)
Tree *tree;
MyPolygon *poly;
BOOLEAN fromShadow;
{
	if(tree->backNode == EMPTYTREE){
		/*this face is visible to the light*/
		if(!fromShadow) addPolygonShadowPlanesToTree(&tree,poly);
	}
	else
	filterPolygonOnSVBSPTree(tree->backNode,poly,fromShadow);
}


void filterPolygonOnSVBSPTree(tree,poly,fromShadow)
/*filters the new face down the tree - shadows will be added to this face*/
Tree *tree;
MyPolygon *poly;
BOOLEAN fromShadow;
{
	Index *faceSet;
	Tree *frontNode,*backNode;
	PlaneEq *plane;
	Classification c;
	MyPolygon frontP,backP;
	int n;
	int i;

	faceSet = tree->faceSet;
	frontNode = tree->frontNode;
	backNode = tree->backNode;

	plane = PLANEQOfFace(atLiteFaceList(TheLiteFaceList,INDEX(faceSet)));
	c = classifyPolygon(poly,plane,&frontP,&backP);

	if(c==FRONT) onFrontPolygon(tree,poly,fromShadow);
	else
	if(c==BACK) onBackPolygon(tree,poly,fromShadow);
	else
	if(c == FRONT_BACK || c == BACK_FRONT){
		onFrontPolygon(tree,&frontP,fromShadow);
		onBackPolygon(tree,&backP,fromShadow);
	}
	else /*on*/{
		/*ignore this one*/
	       /*
		printf("reached on!!!!!!!\n");
	       */
		return;
	}
}

#define NEWSTUFF

#ifdef NEWSTUFF

void filterPFaceOnSVBSPTree(tree,targetFace,face,facelist,liteFaceList,light,lightNo,fromShadow)
/*filters the new face down the tree - shadows will be added to this face*/
Tree *tree;
Face *targetFace,*face;
FaceList *facelist;
LiteFaceList *liteFaceList;
Light *light;
int lightNo;
BOOLEAN fromShadow;
{
	MyPolygon poly;

	/*construct a polygon from the face*/
	faceToPolygon(&poly,face);

	TheTargetFace = targetFace;
	TheFaceList = facelist;
	TheLiteFaceList = liteFaceList;
	TheLight = light;
	TheLightNo = lightNo;

	filterPolygonOnSVBSPTree(tree,&poly,fromShadow);
}


#endif
