#include "view.h"

#define SMALLNUM 		(-999999)
#define BIGNUM	 		(999999)
#define MIN(a,b)		((a)<(b)?(a):(b))
#define MAX(a,b)		((a)>(b)?(a):(b))
#define INDEXInvalid	(-1)

#define ET(surface,i)		(*(surface->edgeTable + (i)))
#define ADDToET(surface,i,etv)	{ ET(surface,i).et[ET(surface,i).n++] = etv;}

						
typedef struct{
	int x,y;
} Point2D;

void initialiseETOfSurface(surface)
Surface *surface;
{
	register int i;
	int n;
	
	n = HEIGHT(surface);
	surface->edgeTable = Malloc(ETEntry,n);
	for(i=0;i<n;++i) ET(surface,i).n = 0;
	surface->ymin = BIGNUM;
	surface->ymax = SMALLNUM;
}

void reInitialiseETOfSurface(surface)
Surface *surface;
{
	register int i;
	int n;
	
	n = HEIGHT(surface);
	for(i=0;i<n;++i) ET(surface,i).n = 0;
	surface->ymin = BIGNUM;
	surface->ymax = SMALLNUM;
}

static void updateAET(aet,i)
ETEntry *aet;
int i;
{
	register int j;
	
	if(aet->n==0) return;
	
	for(j=0;j<aet->n;++j){
		if(aet->et[j].polyPointer != INDEXInvalid){
			if(aet->et[j].y2 == i) {
				aet->et[j].polyPointer = INDEXInvalid;
				--aet->n;
			}
			else aet->et[j].x1 += aet->et[j].delta;
		}
	}
}

static void combineAET(aet,et)
ETEntry *aet,*et;
{
	register int i;
	ETEntry newAET;
	
	newAET.n = 0;
	if(aet->n > 0)
		for(i=0;i<aet->n;++i)
			if(aet->et[i].polyPointer != INDEXInvalid)
				newAET.et[newAET.n++] = aet->et[i];
				
	for(i=0;i<et->n;++i)
		if(et->et[i].polyPointer != INDEXInvalid)
			newAET.et[newAET.n++] = et->et[i];
			
	*aet = newAET;
}
	
void horizontalLineOnSurface(surface,i,aet)
Surface *surface;
int i;
ETEntry *aet;
{
	horizontalLineOnDrawable(ROUND(aet->et[0].x1),ROUND(aet->et[1].x1),i);
}

void processETOfSurface(surface)
Surface *surface;
{
	ETEntry aet;
	register int i;
	
	aet.n = 0;
	for(i=surface->ymin; i<=surface->ymax; ++i){
		updateAET(&aet,i);
		if(ET(surface,i).n) combineAET(&aet,&ET(surface,i));
		horizontalLineOnSurface(surface,i,&aet);
	}
}

void addToETOfSurface(surface,p1,p2,index)
Surface *surface;
Point2D *p1;
Point2D *p2;
int index;
{
	Point2D q1,q2,q;
	ETValue etv;
	ETEntry et;
	
	q1 = *p1;
	q2 = *p2;
	if(q1.y > q2.y){
		q = q1;
		q1 = q2;
		q2 = q;
	}
	if(q1.y < q2.y){	/*ignore horizontal lines*/
		etv.y2 = q2.y;
		etv.x1 = (REAL)(q1.x);
		etv.delta = (REAL)(q2.x - q1.x)/(REAL)(q2.y - q1.y);
		etv.polyPointer = index;
		ADDToET(surface,q1.y,etv);
		surface->ymin = MIN(surface->ymin,q1.y);
		surface->ymax = MAX(surface->ymax,q2.y);
	}
}


void displayFilledPolygonOnSurface(surface,shading)
Surface *surface;
Shading *shading;
{
	int x,y,i;
	Point2D p0,p1,p2;
	
	/*set the current drawable*/
	setDrawable(surface->drawable);
	
	/*set the shading*/
	setColourOnDrawable(REDShading(shading),
						GREENShading(shading),
						BLUEShading(shading));
	
	/*set the first point*/
	p1.x = ROUND(SCREEN_X(surface,surface->polygon[0].x));
	p1.y = ROUND(SCREEN_Y(surface,surface->polygon[0].y));

	p0 = p1;
	
	/*and the subsequent points*/
	for(i=1;i<surface->polygonSize;++i){
		p2.x = ROUND(SCREEN_X(surface,surface->polygon[i].x));
		p2.y = ROUND(SCREEN_Y(surface,surface->polygon[i].y));
		addToETOfSurface(surface,&p1,&p2,0);
		p1 = p2;
	}
	/*closure*/
	addToETOfSurface(surface,&p1,&p0,0);
	
	processETOfSurface(surface);
}
	