#include <stdio.h>
#include <stdlib.h>
#include "data.h"

int finished=0;
float xmin, xmax, ymin, ymax;
int nsites, ndtedges, ntriples, sqrt_nsites, siteidx; 
Freelist wavefront_fl;
Site *sites;
int *main_ac; 
char **main_av;
float sweep_line;
VarArray dtedge_va, triple_va, rectangle_va;

int b_debug;

extern int screen_number;

int main(int ac, char **av)
{
	int i;
	char c;
	int *t;
	
	main_ac = &ac; main_av = av;
	x_init();
	x_parse_options();
	x_openwin();

	t = &screen_number;
	while ((c = getopt(ac, av, "d")) != EOF) {
		switch(c) {
		case 'd':
			b_debug = 1 ;
			break ;
		}
        }

	if (ac>1) nsites = atoi(av[1]);

	sites = myalloc(nsites * sizeof (Site));
	siteidx = 0;

	gen_sites (sites, nsites);
	warn("the original sites\n");
	dump_sites();
	qsort(sites, nsites, sizeof(Site), scomp) ;

	warn("the sorted sites\n");
	dump_sites();

	ymin = sites[0].coord[Y];
	ymin = sites[0].coord[Y];

	for (i=0; i<nsites; i++) {
		if (sites[i].coord[Y] < ymin) ymin = sites[i].coord[Y]; 
		if (sites[i].coord[Y] > ymax) ymax = sites[i].coord[Y]; 
	}

	geominit();
	memoryinit();
	EQinit();
	WFinit();
	x_mainloop();
	return 0;
}

/* generate n sites from the start location base */
void gen_sites(Site *base, int n)
{

	FILE *randdev;
	struct timeval tv;
	int i;

	warn("[%s] generating %d sites\n", __func__, nsites);

	/* seed the random number generator
	 * first try the random file /dev/random
	 * if there isn't such a file in the system
	 * use current time to seed the RNG.
	 */
	if ((randdev = fopen("/dev/random","r")) == NULL) {
		gettimeofday(&tv, NULL);
		i = tv.tv_usec;
	} else i = getc(randdev);
	srand(10);

	for (i=0; i<n; i++) {

		/* floating point version */
#ifdef RANDFLOAT
		sites[i].coord[X] = 1.0 + WIDTH *(rand()/(RAND_MAX+1.0));
		sites[i].coord[Y] = 1.0 + HEIGHT*(rand()/(RAND_MAX+1.0));

#else
		/* integer version */
		sites[i].coord[X] = 1 + (int)((float)WIDTH *rand()/(RAND_MAX+1.0));
		sites[i].coord[Y] = 1 + (int)((float)HEIGHT*rand()/(RAND_MAX+1.0));

#endif
		//warn("(%f, %f) ", sites[i].coord[X], sites[i].coord[Y]);
		sites[i].sitenbr = i;
	}
}

int scomp(const void *vs1, const void *vs2)
{
	Site *s1 = (Site *)vs1 ;
	Site *s2 = (Site *)vs2 ;

	/* compare X coordinate first */
	if (s1->coord[X] < s2->coord[X]) return -1;
	else if (s1->coord[X] > s2->coord[X]) return 1;

	/* if there are ties, use Y coordinate */
	if (s1->coord[Y] < s2->coord[Y]) return -1;
	else if (s1->coord[Y] > s2->coord[Y]) return 1;
	return 0;
}

float peek_next_site()
{
	if (siteidx >= nsites) return -1;
	return sites[siteidx].coord[X];
}

float trigger_next_event() 
{
	Wavefront *inact_event, *lowerbnd;
	Wavefront *new_wf;
	Site *newsite;
	float ret, site_loc, inact_loc;

	site_loc = peek_next_site();
	inact_loc = EQ_min();

	warn("site location %f, inact location %f\n", 
			site_loc, inact_loc);

	if (site_loc >= 0 && 
		(inact_loc < 0 || site_loc < inact_loc)) {

		/* new site is the next event */
		newsite = extractsite();
		warn("site event %d(%f,%f) \n", newsite->sitenbr, newsite->coord[X],newsite->coord[Y]);

		/* add the new wave front to the list */
		new_wf = WFcreate(newsite);
		lowerbnd = WFlowerneighbor(new_wf);

		if (lowerbnd->site->sitenbr >= 0) 
			report_edge(newsite, lowerbnd->site);
		if (lowerbnd->WFup->site->sitenbr >= 0)
			report_edge(newsite, lowerbnd->WFup->site);
		if (lowerbnd->site->sitenbr >= 0 
			&& lowerbnd->WFup->site->sitenbr >= 0)
			report_triple(lowerbnd->site, lowerbnd->WFup->site, new_wf->site);

		WFinsert(lowerbnd, new_wf);
		update_act_record(new_wf->WFup, UP);
		update_act_record(new_wf->WFdown, DOWN);

		ret = site_loc;

	} else if (inact_loc >= 0) { 
		/* an inactivation record should be processed */

		inact_event = EQextractmin();
		warn("[%s] inactivation event for site %d(%f,%f) occured at %f!\n",__func__, inact_event->site->sitenbr, inact_event->site->coord[X],inact_event->site->coord[Y], inact_event->inact);

		Wavefront *pre = inact_event->WFdown;

		report_triple(inact_event->site, pre->site, inact_event->WFup->site);
		WFdelete(inact_event);

		/* new edge created */
		report_edge(pre->site, pre->WFup->site);

		update_act_record(pre, DOWN);
		update_act_record(pre->WFup, UP);

		ret = inact_loc;
	} else if (finished) ret = NOEVENT;
	else finished=1,ret = LASTEVENT;		/* for the last event */
	warn("[%s] event at %f!\n", __func__, ret);
	return ret;
}

Site *extractsite()
{
	if (siteidx >= nsites) return NULL;
	return &sites[siteidx++];
}

void report_edge (Site *s1, Site *s2)
{
	DTedge *e;
	e = var_array_elem(&dtedge_va, ndtedges++);
	e->ends[0] = s1;
	e->ends[1] = s2;
	warn("edge %d between site %d(%f,%f) and %d(%f,%f)\n", ndtedges, s1->sitenbr, s1->coord[X], s1->coord[Y], s2->sitenbr, s2->coord[X], s2->coord[Y]);
}

void report_triple(Site *s1, Site *s2, Site *s3)
{
	Triple *t;
	bounding_rectangle(var_array_elem(&rectangle_va,ntriples), 
			   s1, s2, s3, 1);	
	t = var_array_elem(&triple_va, ntriples++);
	t->sites[0] = s1;
	t->sites[1] = s2;
	t->sites[2] = s3;
	warn("triple (%d, %d, %d)\n",
	     t->sites[0]->sitenbr,	     
	     t->sites[1]->sitenbr,
	     t->sites[2]->sitenbr);	
}

void update_act_record(Wavefront *w, int dir)
{
	float offset, inact_location, rightmost;
	Wavefront *upper = w->WFup;
	Wavefront *lower = w->WFdown;

	/* There aren't any inactivations that can occur */
	if (!upper || ! lower 
		|| upper->site->coord[X] < w->site->coord[X]
		|| lower->site->coord[X] < w->site->coord[X]) 
		return;

	EQdelete(w);

	offset = upper->site->coord[Y] - lower->site->coord[Y];
	inact_location = w->site->coord[X] + offset;

	rightmost = upper->site->coord[X] > lower->site->coord[X] ?
		upper->site->coord[X]: lower->site->coord[X];


	if (inact_location <= rightmost) {
		Wavefront *pre = w->WFdown;
		warn ("[%s] inactivaton record for site %d activated prematurely at %f\n", __func__, w->site->sitenbr, inact_location);
		report_triple(w->site,pre->site,w->WFup->site);
		WFdelete(w);
		report_edge(pre->site, pre->WFup->site);
		if (dir == UP) update_act_record(pre->WFup, UP);
		else update_act_record(pre, DOWN);
	} else {
		EQinsert(w, inact_location);
		warn("[%s] inactivation record for site %d at %f generated by %d and %d\n", __func__, w->site->sitenbr, inact_location, upper->site->sitenbr, lower->site->sitenbr);
	}
}

void dump_sites()
{
	int i;
	for (i=0; i<nsites; i++)
		warn("%d (%f,%f)\n", sites[i].sitenbr, sites[i].coord[X], sites[i].coord[Y]);
	warn ("\n");
}