#include <stdio.h>
#include <math.h>
#include "grf4.h"

#include "../h/cdefs.h"
#include "chull.h"
#include "../rci/link.h"

void *malloc();

typedef struct _event {
	struct _event *next;
/*	struct _event *prev;*/
#define HIDE_EVENT	1
#define EXPOSE_EVENT	2
	int type;
	float r;
} EVENT;

typedef struct vector {
	struct vector *next;
	struct vector *prev;
	float x1, y1, z1;
	float x2, y2, z2;
	int visible;

	int intersect;
	float rInto;
	float rOut;
	EVENT *events;
} VECTOR;

typedef struct _pedge {
	struct _pedge *next;
	struct _pedge *prev;
	float x, y;
	int visible;

	int intersect;
#define INTO	1
#define OUT	2
	float rInto;
	float rOut;

	EVENT *events;

	  } PEDGE;

typedef struct _polygon {
	struct _polygon *next;
	PEDGE *e;
	int n;
	  } SCR_POLYGON;

SCR_POLYGON *specialPolygon();
SCR_POLYGON *fixedPoly2();
SCR_POLYGON *fixedPoly3();
SCR_POLYGON *fixedPoly4();

char **argvec;

#define EPSILON	(1e-7)

PEDGE *edgePool = NULL;

static PEDGE *newEdge();
static drawPolygon();
static insidePolygon ();

static PEDGE *newEdge()
{
	PEDGE *pe;
#define EBLKSIZE	512
	if (!edgePool)
	 { int k;
	   edgePool = (PEDGE*)malloc(EBLKSIZE*sizeof(PEDGE));
	   for (k=0; k<EBLKSIZE-1; k++)
	    { edgePool[k].next = &edgePool[k+1];
	    }
	 }
	pe = edgePool;
	pe->intersect = 0;
	edgePool = edgePool->next;
	return (pe);
}

freeEdge(pe)
PEDGE *pe;
{
	pe->next = edgePool;
	edgePool = pe;
}

EVENT* newEvent(type,r)
int type;
float r;
{
	EVENT *ev;

	ev = (EVENT*)calloc(1,sizeof(EVENT));
	ev->type = type;
	ev->r = r;
	ev->next = NULL;
	return (ev);
}

freeEdges(ps,pf)
PEDGE *ps, *pf;
{
	/* assumes that "pe" begins a NULL terminated list of edges */
	pf->next = edgePool;
	edgePool = ps;
}

PEDGE *newEdgeRing(n)
int n;
{
	PEDGE *ph, *pt, *pn;
	int k;

	if (n <= 0)
	 { return(NULL);
	 }
	ph = newEdge();
	ph->visible = 1;
	ph->events = 0;
	pt = ph;
	for (k=1; k<n; k++)
	 { pn = newEdge();
	   pt->next = pn;
	   pn->prev = pt;
	   pn->visible = 1;
	   pt = pn;
	 }
	ph->prev = pt;
	pt->next = ph;
	return (ph);
}

VECTOR mainVect;
SCR_POLYGON *polygons;

drawIt()
{
	SCR_POLYGON *p;

	mainVect.events = NULL;
	mainVect.visible = 1;
	grf4Clear();
#if 0
	grf4Move2 (mainVect.x1, mainVect.y1);
	grf4Draw2 (mainVect.x2, mainVect.y2);
#endif
	for (p=polygons; p; p=p->next)
	 { drawPolygon(p);
	   if (vectorIntersect(p,&mainVect))
	    { float x1,x2,y1,y2;

#if 0
	      x1 = mainVect.x1;
	      x2 = mainVect.x2;
	      y1 = mainVect.y1;
	      y2 = mainVect.y2;
	      drawBox (x1+mainVect.rInto*(x2-x1), y1+mainVect.rInto*(y2-y1));
	      drawBox (x1+mainVect.rOut*(x2-x1), y1+mainVect.rOut*(y2-y1));
#endif
	      vectorClip(&mainVect);
	    }
	 }
	vectorDraw(&mainVect);
	grf4Flush();
}

main(argc, argv)
int argc;
char **argv;
{
	int winw, winh, n;
	int cnt;
	int status;
	SCR_POLYGON *p;
	PEDGE *fp, *fq, *ee, *ne;
	char *fileName = NULL;

	argvec = argv;

	argc--; argv++;
	while (argc > 0)
	 { if (fileName == NULL)
	    { fileName = *argv;
	    }
	   else
	    { goto usage;
	    }
	   argc--; argv++;
	   continue;
usage:
	   fprintf (stderr, "Usage: %s <pointFile>\n", argvec[0]);
	   exit(-1);
	 }

	if (readPointFile(fileName) < 0)
	 { fprintf (stderr, "%s: can't read point file %s\n",
			     argvec[0], fileName);
	 }

	if (grf4Init (argvec[0], GRF_WHITE, GRF_BLACK, 0) < 0)
	 { fprintf (stderr, "couldn't init graphics\n");
	   exit (1);
	 }
	grf4SetDrawFxn (drawIt, 0);
	grf4Ortho (0.0, 100.0, 0.0, 100.0, -1.0, 1.0, /*aspectSet=*/1.0);
	drawIt();
	grf4Wait (-1);
}

drawX(x,y)
float x, y;
{
	grf4Move2(x-5,y-5);
	grf4Draw2(x+5,y+5);
	grf4Move2(x-5,y+5);
	grf4Draw2(x+5,y-5);
}

drawBox(x,y,size)
float x, y;
float size;
{
	grf4Move2(x-size/2,y-size/2);
	grf4Draw2(x-size/2,y+size/2);
	grf4Draw2(x+size/2,y+size/2);
	grf4Draw2(x+size/2,y-size/2);
	grf4Draw2(x-size/2,y-size/2);
}

freePolygon(p)
SCR_POLYGON *p;
{
	p->e->prev->next = 0;
	freeEdges(p->e,p->e->prev);
	FREE(p);
}

static drawPolygon(p)
SCR_POLYGON *p;
{
	PEDGE *e;

	grf4Move2 (p->e->x, p->e->y);
	for (e = p->e->next; e != p->e; e = e->next)
	 { if (e->visible)
	    { grf4Draw2 (e->x, e->y);
	    }
	   else
	    { grf4Move2 (e->x, e->y);
	    }
	 }
	if (e->visible) grf4Draw2 (e->x, e->y);
}

SCR_POLYGON *fixedPoly3()
{
	SCR_POLYGON *p;
	PEDGE *e, *last_e;

	p = ALLOC(SCR_POLYGON);

	p->n = 3;

	p->e = newEdgeRing(p->n);

	p->e[2].x = 50.0;
	p->e[2].y = 50.0;

	p->e[1].x = 150.0;
	p->e[1].y = 50.0;

	p->e[0].x = 150.0;
	p->e[0].y = 100.0;

	return (p);
}

SCR_POLYGON *fixedPoly4()
{
	SCR_POLYGON *p;
	PEDGE *e, *last_e;

	p = ALLOC(SCR_POLYGON);

	p->n = 4;

	p->e = newEdgeRing(p->n);

	p->e[3].x = 75.0;
	p->e[3].y = 25.0;

	p->e[2].x = 125.0;
	p->e[2].y = 25.0;

	p->e[1].x = 125.0;
	p->e[1].y = 100.0;

	p->e[0].x = 75.0;
	p->e[0].y = 100.0;

	return (p);
}


SCR_POLYGON *fixedPoly2()
{
	SCR_POLYGON *p;
	PEDGE *e, *last_e;

	p = ALLOC(SCR_POLYGON);

	p->n = 2;

	p->e = newEdgeRing(p->n);

	p->e[1].x = 75.0;
	p->e[1].y = 25.0;

	p->e[0].x = 75.0;
	p->e[0].y = 100.0;

	return (p);
}


#define MAXSPC	256

typedef struct _pspec {
	struct _pspec *next;
	int size;
	struct _pnts {
	   float x, y } pnts[256];
	  } PSPEC;

readPointFile(name)
char *name;
{
	FILE *fp;
	SCR_POLYGON *p;
	int k, size;
	PSPEC ps;

	if ((fp = fopen(name,"r")) == NULL)
	 { fprintf(stderr, "Can't open file\n");
	   return(-1);
	 }

	/* File starts with a description of the line, followed 
	   by the polygons to intersect it with. */

	if (fscanf(fp, "%f %f %f %f", &mainVect.x1, &mainVect.y1,
				      &mainVect.x2, &mainVect.y2) != 4)
	 { fprintf(stderr, "Can't read line desc\n");
	   return (-1);
	 }

	while (fscanf(fp,"%d", &size) != EOF)
	 { if (size > MAXSPC)
	    { fprintf(stderr, "Polygon size %d is too large\n", size);
	      return(-1);
	    }
	   for (k=0; k<size; k++)
	    { if (fscanf(fp,"%f %f", &ps.pnts[k].x, &ps.pnts[k].y) != 2)
	       { fprintf(stderr, "Error reading points\n");
		 return(-1);
	       }
	    }
	   ps.size = size;
	   p = specialPolygon(&ps);
	   p->next = polygons;
	   polygons = p;
	 }	
	return(1);
}

SCR_POLYGON *specialPolygon(pspec)
PSPEC *pspec;
{
	SCR_POLYGON *p;
	PEDGE *e, *last_e;
	int k;

	p = ALLOC(SCR_POLYGON);
	p->n = pspec->size;

	/* Build the polygon from the specified points. The defined
	   points are given in clockwise order, so we have to reverse
	   this. */

	p->e = newEdgeRing(p->n);
	k = 0;
	e = p->e;
	do
	 { e->x = pspec->pnts[k].x;
	   e->y = pspec->pnts[k].y;
	   k++;
	   e = e->prev;
	 }
	while (e != p->e);
	return (p);
}

static insidePolygon (p3, p)
PEDGE *p3;
SCR_POLYGON *p;
{
	/* returns true if p3 is strictly inside polygon p */

	PEDGE *p1, *p2;

	p2 = p->e;
	do
	 { 
	   p1 = p2->prev;
	   /* check for left turn */
	   if (((p2->x-p1->x)*(p3->y-p2->y) - 
		(p3->x-p2->x)*(p2->y-p1->y)) <= 0.0)
	    { return(0);
	    }
	   p2 = p2->next;
	 }
	while (p2 != p->e);
	return(1);
}

/* Cross product between two vectors v1 and v2 is v1x*v2y-v2x*v1y */

/* SIDE(pt,e1,e2) computes (e2-e1)X(pt-e1).  

   This also means that:

   SIDE(pt,e1,e2) > 0 if e1,e2,pt form a LEFT turn;
   SIDE(pt,e1,e2) < 0 if e1,e2,pt form a RIGHT turn;
   SIDE(pt,e1,e2) = 0 if e1,e2,pt are colinear,

   and also that

   SIDE(pt,e1,e2) > 0 if pt is LEFT of (e2-e1);
   SIDE(pt,e1,e2) < 0 if pt is RIGHT of (e2-e1);
   SIDE(pt,e1,e2) = 0 if pt is colinear with (e2-e1)

*/   

#define SIDE(pt,e1,e2) \
	( ((e2)->x-(e1)->x)*((pt)->y-(e1)->y) - \
	  ((pt)->x-(e1)->x)*((e2)->y-(e1)->y) )	

vectorIntersect(poly,vect)
SCR_POLYGON *poly;
VECTOR *vect;
{
	PEDGE *p1, *p2;
	PEDGE *q1, *q2;
	PEDGE q1e, q2e;
	float r;
	float sideP1, sideP2, sideQ1, sideQ2;
	float sideP3;
	int intersect = 0;
	int found;

	q1 = &q1e;
	q2 = &q2e;

	q1->x = vect->x1;
	q1->y = vect->y1;
	q2->x = vect->x2;
	q2->y = vect->y2;

	p2 = poly->e;
	p1 = p2->prev;
	do
	 { 
	   sideP1 = SIDE(p1,q1,q2);
	   sideP2 = SIDE(p2,q1,q2);
	   sideQ1 = SIDE(q1,p1,p2);
	   sideQ2 = SIDE(q2,p1,p2);
	   found = 0;

	   /* As a first condition, the line of edge p must split q, 
	      and the back of p must be EPSILON from q. */

	   if (sideQ1*sideQ2 < 0 && ABS(sideP1) > EPSILON)
	    { 
	      if (ABS(sideP2) < EPSILON)
	       { /* head of p just touches q. */
		 sideP3 = SIDE(p2->next,q1,q2);
		 if (ABS(sideP3) < EPSILON || sideP1*sideP3 > 0)
		  { return (0);
		  }
		 else
		  { found = 1;
		  }
	       }
	      else if (sideP2*sideP1 < 0)
	       { /* p crosses over q */
		 found = 1;
	       }
	      if (found)
	       { r = sideQ1/(sideQ1-sideQ2);
		 r = MAX(0.0,MIN(r,1.0));
		 if (sideQ2 <= 0)
		  { intersect |= OUT;
		    vect->rOut = r;
		  }
		 else
		  { intersect |= INTO;
		    vect->rInto = r;
		  }
	       }
	    }
	   p1 = p2;
	   p2 = p2->next;
	 }
	while (p2 != poly->e);

	if (!intersect)
	 { q1->x = (q1->x+q2->x)/2;
	   q1->y = (q1->y+q2->y)/2;
	   if (!insidePolygon (q1, poly))
	    { return (0);
	    }
	   vect->rInto = 0.0;
	   vect->rOut = 1.0;
	 }
	else if (intersect == INTO)
	 { vect->rOut = 1.0;
	 }
	else if (intersect == OUT)
	 { vect->rInto = 0.0;
	 }
	return (1);
}

vectorClip(v)
VECTOR *v;
{
	/* Here is the basic paradigm for adding a hidding region delimited 
	   by the interval [h,e]:

	   1. If the region at h is exposed, then add HIDE event at h
	   2. If the region at e is exposed, then add EXPOSE event at e
	   3. Delete all other events in the interval [h,e].

	   h corresponds to the value of rInto, and e corresponds to the value
	   of rOut. 
	 */
	EVENT *hide;
	EVENT *expose;
	EVENT *evBak, *evFwd;
	float e, h;

	e = v->rOut;
	h = v->rInto;
	
	/* Start by either locating a hide event previous to h, or
	   creating one at h. */

	evBak = NULL;
	for (evFwd=v->events; evFwd && evFwd->r<h; evFwd=evFwd->next)
	 { evBak = evFwd;
	 }
	if (evBak && evBak->type == HIDE_EVENT)
	 { hide = evBak;
	 }
	else
	 { hide = newEvent(HIDE_EVENT,h);
	   if (evBak)
	    { evBak->next = hide;
	    }
	   else
	    { v->events = hide;
	    }
	   evBak = hide;
	 }

	/* Now locate an expose event after e, or create one at e. */

	for ( ; evFwd && evFwd->r <= e; evFwd=evFwd->next)
	 { evBak = evFwd;
	 }
	if (evFwd && evFwd->type == EXPOSE_EVENT)
	 { expose = evFwd;
	 }
	else
	 { expose = newEvent(EXPOSE_EVENT,e);
	   expose->next = evFwd;
	 }

	/* Link the hide and expose events together, effectively deleting
	   all events in between. */

	hide->next = expose;

	/* Mark the entire vector invisible if necessary. */

	if (hide->r == 0.0 && expose->r == 1.0)
	 { v->events = NULL;
	   v->visible = 0;
	 }
}

vectorDraw(v)
VECTOR *v;
{
	if (v->events == NULL)
	 { if (v->visible)
	    { grf4Move (v->x1,v->y1);
	      grf4Draw (v->x2,v->y2);
	    }
	 }
	else
	 { float dx, dy;
	   EVENT *ev, *ep;

	   dx = v->x2 - v->x1;
	   dy = v->y2 - v->y1;
	   ep = NULL;
	   for (ev=v->events; ev; ev=ev->next)
	    { if (ev->type == HIDE_EVENT && ev->r != 0.0)
	       { if (ep)
		  { grf4Move2 (v->x1 + ep->r*dx, v->y1 + ep->r*dy);
		  }
		 else
		  { grf4Move2 (v->x1, v->y1);
		  }
		 grf4Draw2 (v->x1 + ev->r*dx, v->y1 + ev->r*dy);
		 if (ep)
		  { drawBox (v->x1 + ep->r*dx, v->y1 + ep->r*dy, 4.0);
		  }
		 drawBox (v->x1 + ev->r*dx, v->y1 + ev->r*dy, 4.0);
	       }
	      else if (ev->type == EXPOSE_EVENT && ev->next == NULL &&
		       ev->r != 1.0)
	       { 
		 grf4Move2 (v->x1 + ev->r*dx, v->y1 + ev->r*dy);
		 grf4Draw2 (v->x2, v->y2);
		 drawBox (v->x1 + ev->r*dx, v->y1 + ev->r*dy, 4.0);
	       }
	      printf ("event=%d r=%g\n", ev->type, ev->r);
	      ep = ev;
	    }
	 }
}
