
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <math.h>

#include "glut.h"
#include "glui.h"

#include "dynamics.h"
#include "structures.h"


void Path::Print() {
	int i;
	for (i=0; i<length; i++) {
		printf("%d ", nodeArray[i]);
	}
	printf("\n");
}

int Path::FindNode(int n) {

	int i;
	for (i=0; i<length; i++) {
		if (nodeArray[i] == n) return 1;
	}
	return 0;

}


int Path::FindLink(int a, int b) {

	int i;
	for (i=1; i<length; i++) {
		if ((nodeArray[i-1] == a) && (nodeArray[i] == b)) return 1;
	}
	return 0;

}


int nodesSame(Path *p1, Path *p2) {

	int i;
	int same = 1;
	if (p1->length == p2->length) {
		for (i=0;i<p1->length;i++) {
			if (p1->nodeArray[i]!=p2->nodeArray[i]) same = 0;
		}
		return same;
	} else {
		return 0;
	}

}


DynamicsViz::DynamicsViz(int sx, int sy, int xs, int ys) : Visualization(sx,sy,xs,ys) , toprightx(sx+220) , toprighty(sy+220) , 
toprightxsize(500) , toprightysize(600) , H(sx,sy,500,100,200) , TT(sx+20,sy+220,200,600) , 
AC(toprightx,toprighty,toprightxsize,toprightysize) , MV(toprightx,sy+200,toprightxsize-100,toprightysize) , G(sx+220,sy+200,400,600) ,
EV(sx,sy+110,500,100) , LG(sx+610, sy+10, 100, 200) , sig_thres(0.0) {

	int i, j;
	npaths = 0;

	default_k = 3;
	default_t = 300;
	default_a = 1.0/90.0;
	num_sigan = 0;

	Sig.k[0] = default_k;
	Sig.k[1] = -1;
	Sig.k[2] = -1;
	Sig.t[0] = default_t;
	Sig.t[1] = -1;
	Sig.t[2] = -1;
	Sig.a[0] = default_a;
	Sig.a[1] = -1.0;
	Sig.a[2] = -1.0;

	TT.mintime = 0;
	TT.maxtime = timetoint(235959);
	AC.archeight = 0.6 * (float)AC.ysize/(float)TT.maxitems;

	MV.num_measures = 6;
	strcpy(MV.measure_names[0],"int-arr time");
	strcpy(MV.measure_names[1],"num new path");
	strcpy(MV.measure_names[2],"path frq");
	strcpy(MV.measure_names[3],"type");
	strcpy(MV.measure_names[4],"path diff");
	strcpy(MV.measure_names[5],"aggregate");
	for (i=0;i<5;i++) {
		MV.num_vals[i] = 2;
		strcpy(MV.val_names[i][0], "Q");
		strcpy(MV.val_names[i][1], "S");
		MV.val_colors[i][0][0] = 1.0;
		MV.val_colors[i][0][1] = 0.0;
		MV.val_colors[i][0][2] = 0.0;
		MV.val_colors[i][0][3] = 1.0;
		MV.val_colors[i][1][0] = 0.0;
		MV.val_colors[i][1][1] = 1.0;
		MV.val_colors[i][1][2] = 0.0;
		MV.val_colors[i][1][3] = 1.0;
	}
	MV.num_vals[5] = 1;
	strcpy(MV.val_names[5][0], "T");
	MV.val_colors[5][0][0] = 0.1;
	MV.val_colors[5][0][1] = 0.1;
	MV.val_colors[5][0][2] = 0.1;
	MV.val_colors[5][0][3] = 1.0;
	
	pcolors[0][0] = 1.0; pcolors[0][1] = 0.0; pcolors[0][2] = 0.0;
	pcolors[1][0] = 1.0; pcolors[1][1] = 0.5; pcolors[1][2] = 0.0;
	pcolors[2][0] = 0.5; pcolors[2][1] = 0.0; pcolors[2][2] = 1.0;
	pcolors[3][0] = 0.0; pcolors[3][1] = 1.0; pcolors[3][2] = 0.0;
	pcolors[4][0] = 0.0; pcolors[4][1] = 0.0; pcolors[4][2] = 1.0;
	pcolors[5][0] = 0.0; pcolors[5][1] = 0.5; pcolors[5][2] = 1.0;
	pcolors[6][0] = 0.5; pcolors[6][1] = 0.5; pcolors[6][2] = 0.5;
	pcolors[7][0] = 0.2; pcolors[7][1] = 0.8; pcolors[7][2] = 0.5;
	pcolors[8][0] = 0.5; pcolors[8][1] = 0.2; pcolors[8][2] = 0.8;
	pcolors[9][0] = 0.8; pcolors[9][1] = 0.5; pcolors[9][2] = 0.2;
	srand(5);
	for (i=10;i<128;i++) {
		pcolors[i][0] = (float)(rand()%256)/256.0;
		pcolors[i][1] = (float)(rand()%256)/256.0;
		pcolors[i][2] = (float)(rand()%256)/256.0;
	}

	LG.colors = new float[6*4];
	for (i=0;i<6;i++) {
		for (j=0;j<3;j++) {
			LG.colors[i*4+j] = EventShrub::colors[i][j];
		}
		LG.colors[i*4+3] = 1.0;
	}
	LG.numitems = 6;
	LG.names = new char*[6];
	for (i=0;i<6;i++) LG.names[i] = new char[8];
	strcpy(LG.names[0],"A");
	strcpy(LG.names[1],"B");
	strcpy(LG.names[2],"C");
	strcpy(LG.names[3],"D");
	strcpy(LG.names[4],"E");
	strcpy(LG.names[5],"F");
	strcpy(LG.title,"Signatures");

	EV.SetVerticalDimensions(toprightx+toprightxsize-100,toprighty,100,toprightysize);

}




void DynamicsViz::readPath(char *buf) {
	readPathPtr(buf, paths, npaths);
}


void DynamicsViz::readPathPtr(char *buf, Path **parray, int &p) {

	char str[512];
	char as[16];
	char temp[8];
	int i, j, k, a, fk;
	int asn, asn1;
	int incremented;
	int found, keepgoing;
	int curind, prevind;

	struct tm *timeobj;
	int itimestamp;
	unsigned long longtime;

	i = 0;
	parray[p] = new Path();
	// read header BGP4MP
	while (buf[i]!='|') {
		str[i] = buf[i];
		i++;
	}
	i++;
	// read time stamp
	for (j=0;buf[i]!='|';j++,i++) {
		temp[j] = buf[i];
	}
	temp[j] = '\0';
	sscanf(temp,"%d",&itimestamp);
	longtime = (unsigned long)itimestamp;
	timeobj = localtime((const time_t*)(&longtime));
	timeobj->tm_year+=1900;
	timeobj->tm_mon++;

	parray[p]->cdate = timeobj->tm_year * 10000;
	parray[p]->cdate += timeobj->tm_mon * 100;
	parray[p]->cdate += timeobj->tm_mday;

	parray[p]->ctime = timeobj->tm_hour * 10000;
	parray[p]->ctime += timeobj->tm_min * 100;
		
	i++;


	// read whether announcement or withdraw
	while (buf[i]!='|') {
		str[i] = buf[i];
		i++;
	}
	i++;

	// read peer ip
	j = 0;
	while (buf[i]!='|') {
		parray[p]->attributes[0][j] = buf[i];
		i++; j++;
	}
	parray[p]->attributes[0][j] = '\0';
	i++;

	// skip peer as
	while (buf[i]!='|') {
		str[i] = buf[i];
		i++;
	}
	i++;

	// skip prefix
	while (buf[i]!='|') {
		str[i] = buf[i];
		i++;
	}
	i++;

	fk = k = i;

	// read as path
	while (buf[i]!='|') {
		str[i] = buf[i];
		i++;
	}
	str[i] = '\0';
	i++;

	a = 0;
	while (k < fk + strlen(&(str[fk]))) {
		// read AS
		j = 0;
		while ((str[k]>='0') && (str[k]<='9')) {
			as[j] = buf[k];
			k++; j++;
		}
		as[j] = '\0';
		parray[p]->nodeArray[a] = atoi(as);
		a++;
		parray[p]->length = a;
		// read white space
		while ((buf[k]!='\0')&&((buf[k]<'0') || (buf[k]>'9'))) k++;
		if (a >= MAX_N_NODES) {
			printf("a exceeded\n");
		}
	}

	// read 4 other attributes
	for (k=1;k<5;k++) {
		j = 0;
		while (buf[i]!='|') {
			parray[p]->attributes[k][j] = buf[i];
			i++; j++;
		}
		parray[p]->attributes[k][j] = '\0';
		i++;
	}

	// read the Q,S,T values
	while (buf[i]!='\t') i++;
	i++;
	for (k=0;k<NUM_MEASURES;k++) {
		sscanf(&(buf[i]),"%f",&(parray[p]->Q[k]));
		while (buf[i]!='\t') i++;
		i++;
		sscanf(&(buf[i]),"%f",&(parray[p]->S[k]));
		while (buf[i]!='\t') i++;
		i++;
	}
	sscanf(&(buf[i]),"%f",&(parray[p]->T));

        //scan for flag, that is 'U', 'D', 'F' 'W'
        while ((buf[i]!='U') && (buf[i]!='D') && (buf[i]!='W') && (buf[i]!='F') && (buf[i]!='\n')) i++;
        if (buf[i]!='\n')
	  parray[p]->flag=buf[i];
	parray[p]->sig = 0;
	// finished reading this path, now do some accounting
	p++;
	if (p >= MAX_N_PATHS) {
		printf("p exceeded\n");
	}

}

void DynamicsViz::readOtherPath(char *buf, int o) {
	readPathPtr(buf, other_observation_paths[o], notherpaths[o]);
}

void DynamicsViz::UpdateUniquePaths(int p) {
	UpdateUniquePathPtr(paths[p]);
}

void DynamicsViz::UpdateOtherUniquePaths(int p, int o) {
	UpdateUniquePathPtr(other_observation_paths[o][p]);
}

void DynamicsViz::UpdateUniquePathPtr(Path *pptr) {
	int found, i, j;
	int keepgoing;
	found = 0;
	for (i=0;((i<ndistinctpaths)&(!found));i++) {
		if (distinctpaths[i].length==pptr->length) {
			keepgoing = 1;
			for (j=0;((j<distinctpaths[i].length)&&(keepgoing));j++) {
				if (distinctpaths[i].nodeArray[j] !=
					pptr->nodeArray[j]) {
					keepgoing = 0;
				}
			}
			if (keepgoing) {
				found = 1;
				pptr->dpid = i;
			}
		}
	}
	if (found==0) {
		// create new unique path
		for (i=0;i<pptr->length;i++) {
			distinctpaths[ndistinctpaths].nodeArray[i] = pptr->nodeArray[i];
		}
		distinctpaths[ndistinctpaths].length = pptr->length;
		pptr->dpid = ndistinctpaths;
		ndistinctpaths++;
	}
}


void DynamicsViz::SetAggregates() {

	int d_array[4096]; // max about 10 years' worth of data
	int l_array[4096]; // array for the labels
	int current_day;
	int i, j, p;
	float colors[4] = { 0.5, 0.5, 0.5, 1.0 };


	if (npaths>0) {
		for (i=0;i<4096;i++) d_array[i] = 0; // reset
		current_day = paths[0]->cdate;
		i = 0;
		d_array[i]++;
		l_array[i] = current_day;
		for (p=0;p<npaths;p++) {
			if (paths[p]->cdate == current_day) {
				d_array[i]++;
			} else {
				while (paths[p]->cdate > current_day) {
					current_day = nextday(current_day);
					i++;
					l_array[i] = current_day;
				}
				d_array[i]++;
			}
		}

		H.SetNumLayers(0);
		j = H.AddLayer(0.5, colors);
		H.SetResolution(i+1);
		H.AddData(j, d_array);
		H.AddLabels(l_array);
		
		first_date = current_date = paths[0]->cdate;
		last_date = paths[npaths-1]->cdate;
		SetDate(current_date);
	}


}


void DynamicsViz::ConstructGraph() {

	int p;
	int asn1, a, asn, incremented, i, curind, prevind;
	int *next;
	int tempx, tempy;

	// **************** PART II : CONSTRUCT THE GRAPH *******************
	G.Init();
	G.nodes[G.root] = new Node(G.root);
	for (p=0;p<ndistinctpaths;p++) {

		if ((distinctpaths[p].length)>0) {
			asn1 = distinctpaths[p].nodeArray[0];
			if ((G.nodes[asn1])==NULL) {
				G.nodes[asn1] = new Node(asn1);
			}
			G.nodes[G.root]->AddLink(G.nodes[asn1]);
		}
		
		for (a=0;a<distinctpaths[p].length-1;a++) {
			asn1 = distinctpaths[p].nodeArray[a];
			asn = distinctpaths[p].nodeArray[a+1];
			if ((G.nodes[asn])==NULL) {
				G.nodes[asn] = new Node(asn);
				G.nodes[asn1]->AddLink(G.nodes[asn]);
			} else {
				G.nodes[asn1]->AddTempLink(G.nodes[asn]);
			}
		}
		// detect cycle
		if (G.DetectCycle()) {
			for (a=0;a<distinctpaths[p].length-1;a++) {
				asn1 = distinctpaths[p].nodeArray[a];
				G.nodes[asn1]->RemoveTempLink();
			}
			distinctpaths[p].addlater = 1;
		} else {
			for (a=0;a<distinctpaths[p].length-1;a++) {
				asn1 = distinctpaths[p].nodeArray[a];
				G.nodes[asn1]->PermanizeLink();
			}
		}
	}
	

	// *************** PART III : SET LEVEL AND LINEARX OF NODES ********************
	G.y = G.nodes[G.root]->SetLevel(0);
	next = new int[G.y+3];
	for (i=-1; i<=G.y; i++) next[i+1] = G.nodes[G.root]->NumInLevel(i);
	for (i=0; i<=G.y; i++) next[i+1] += next[i];
	for (i=G.y+2;i>0;i--) next[i] = next[i-1];
	G.maxlinearx = next[G.y+2];
	G.nodes[G.root]->SetLinearX(next);
	delete [] next;
	for (p=0;p<ndistinctpaths;p++) {
		if (distinctpaths[p].addlater==1) {
			for (a=0;a<distinctpaths[p].length-1;a++) {
				asn1 = distinctpaths[p].nodeArray[a];
				asn = distinctpaths[p].nodeArray[a+1];
				if ((G.nodes[asn1])==NULL) G.nodes[asn1] = new Node(asn1);
				if ((G.nodes[asn])==NULL) G.nodes[asn] = new Node(asn);
				G.nodes[asn1]->AddLink(G.nodes[asn]);
			}
			distinctpaths[p].addlater = 0;
		}
	}


	// *************** PART IV : SET X COORDINATES OF NODES *************
	G.x = 0;
	for (p=0;p<ndistinctpaths;p++) {
		incremented = 0;
		for (a=0;a<distinctpaths[p].length;a++) {
			asn1 = distinctpaths[p].nodeArray[a];
			if (G.nodes[asn1]->xcoord<0) {
				G.nodes[asn1]->xcoord = (float)G.x;
				if (!incremented) {
					incremented = 1;
				}
			}
		}
		if (incremented) G.x++;
	}
	G.nodes[G.root]->xcoord = 0.5*(float)G.x;

}



void DynamicsViz::readStats(char *fname) {

	FILE *fptr;
	char buf[512];
	char str[512];
	char as[16];
	char temp[8];
	int i, j, k, p, a, fk;
	int asn, asn1;
	int incremented;

	fptr = fopen(fname,"rt");
	npaths = 0;
	ndistinctpaths = 0;
	G.root = 0;
	p = 0;

	// *************** PART I : READ IN ALL THE PATHS ******************

	while (!feof(fptr)) {
		fgets(buf,512,fptr);
		while ((!feof(fptr))&&(buf[0]=='-')) fgets(buf,256,fptr);
		readPath(buf);
		if (npaths>=MAX_N_PATHS) printf("exceeded ");
		UpdateUniquePaths(npaths-1);
	}

	// *************** PART II : READ FROM OTHER OBSERVATION POINTS *****

	readOtherObservations("../data_dynamics/comparison.txt");

	// *************** PART III : CONSTRUCT GRAPH, SIGNATURES ***********

	ConstructGraph();
	SetAggregates();
	CreateSignature(0, npaths-1, default_k, default_t, default_a);

}


void DynamicsViz::readOtherObservations(char *fname) {

	FILE *fptr;
	char buf[64];
	int i;
	char fullpath[64];
	char ofname[32];
	FILE *ofptr;
	char pathbuf[512];
		
	fptr = fopen(fname,"rt");
	fgets(buf,64,fptr);
	sscanf(buf,"%d", &n_observations);
	for (i=0;i<n_observations;i++) {
		fgets(buf,64,fptr);
		sscanf(buf,"%s", ofname);
		strcpy(fullpath,"../data_dynamics/");
		strcat(fullpath,ofname);
		ofptr = fopen(fullpath,"rt");
		notherpaths[i] = 0;
		while (!feof(ofptr)) {
			fgets(pathbuf,512,ofptr);
			while ((!feof(ofptr))&&(pathbuf[0]=='-')) fgets(pathbuf,256,ofptr);
			readOtherPath(pathbuf,i);
			if (notherpaths[i]>=MAX_N_PATHS) printf("exceeded ");
			UpdateOtherUniquePaths(notherpaths[i]-1,i);
		}
		fclose(ofptr);
		OTT[i] = new TimeText(startx+20+(i+1)*200,starty+220,200,600);
		OTT[i]->mintime = 0;
		OTT[i]->maxtime = timetoint(235959);
	}
	fclose(fptr);

}


////////////////////////////////////////////////////////////////////////////
// Create signatures for paths 
// The signatures are a set of patterns of BGP update burst
// BGP burst is a sequence of updates within a short time window.
// Three parameters define the patterns:
// 1. number of updates K ( stored in "limit")
// 2. time interval     T ( stored in "tInterval" )
// 3  frequency     alpha ( stored in "freq" )
// the burst is defined as "at least K updates space close togather. The time 
// interval between updates is less than T and the average update frequency is alpha"
// We examine the "UP,DOWN,FLAT,WITHDRAWAL" sequence in a BGP burst to define
// the patterns.
// Augument: start, end indicate which path is a start point and which is an
// end point
/////////////////////////////////////////////////////////////////////////////
void DynamicsViz::CreateSignature(int start, int end, int limit, int tInterval, float freq)
{
  int i;
  
  for ( i = start; i < end; i++)
    {
      int j=1;
      float rate=0.0;
      int time1, time2;
      time1 = paths[i]->cdate * 10000;
	  time1 += paths[i]->ctime / 100;
      time2 = paths[i+j]->cdate * 10000;
	  time2 += paths[i+j]->ctime / 100;

      while( (time2-time1) <= tInterval)
	{
          if (time2 != time1)
	    { 
              rate += 1.0/(time2-time1);
	    }
          else{
	    // time collision
	    rate += 1.0;
	  }

          j++;
          if ( i+j > end ) break; // reach end point
          time1 = time2;
		  time2 = paths[i+j]->cdate * 10000;
		  time2 += paths[i+j]->ctime / 100;

	}//end while
      // not satisfy the burst condition, continue
      if ( (j < limit) || (rate/(j-1) < freq))
	continue;
      
      // burst found
      int state = 0;
      int n = i;
      for (; n < i+j ; n++){
	if (state == 0) {
	  if (paths[n]->flag == 'U') { state = 1; continue;}
          if (paths[n]->flag == 'D') { state = 2; continue;}
          if (paths[n]->flag == 'F') { continue; }
          if (paths[n]->flag == 'W') { state = 8 ; break;}
	} else if (state == 1){
		if ((paths[n]->flag == 'U') || (paths[n]->flag == 'F')) continue;
                if ( paths[n]->flag == 'D') { state = 3; continue; }
                if ( paths[n]->flag == 'W') { state = 8; break; }
	      } else if (state == 2) {
		if ((paths[n]->flag == 'D') || (paths[n]->flag == 'F')) continue;
                if ( paths[n]->flag == 'U') { state = 4; continue; }
                if ( paths[n]->flag == 'W') { state = 8; break; }
	      } else if (state == 3) {
                if ((paths[n]->flag == 'D') || (paths[n]->flag == 'F')) continue;
                if ( paths[n]->flag == 'U') { state = 5; continue; }
                if ( paths[n]->flag == 'W') { state = 8; break; }
              } else if (state == 4) {
                if ((paths[n]->flag == 'U') || (paths[n]->flag == 'F')) continue;
                if ( paths[n]->flag == 'D') { state = 6; continue; }
                if ( paths[n]->flag == 'W') { state = 8; break; }
              } else if (state == 5) {
                if ((paths[n]->flag == 'U') || (paths[n]->flag == 'F')) continue;
                if ( paths[n]->flag == 'D') { state = 7; continue; }
                if ( paths[n]->flag == 'W') { state = 8; break; }
              } else if (state == 6) {
                if ((paths[n]->flag == 'D') || (paths[n]->flag == 'F')) continue;
                if ( paths[n]->flag == 'U') { state = 7; continue; }
                if ( paths[n]->flag == 'W') { state = 8; break; }
              } else if (state == 7) {
                if ( paths[n]->flag == 'W') { state = 8; break;}
                continue;
              } 
        }//end for
       
	
	int sig;
        if (paths[i+j-1]->flag == 'W'){
	  sig = 1;  // sig = A
        } else if (state == 8) {
          sig = 2;  // sig = B         
	} else if ((state >= 3) && (state <= 6)){
          sig = 3;  // sig = C
	} else if ((state == 1) || (state == 2)){
          sig = 4;  // sig = D
	} else if (state == 7) {
          sig = 5;  // sig = E
	} else if (state == 0) {
          sig = 6;  // sig = F
	}
        
        //write signature into each path
	for (int k = i ; k <i+j ; k++) {
		paths[k]->sig = sig;   
		paths[k]->sig_inds[paths[k]->num_sig_inds] = num_sigan;
		paths[k]->num_sig_inds++;
	}
	sigans[num_sigan].start = i;
	sigans[num_sigan].end = i+j-1;
	for (int s=0;s<10;s++) sigans[num_sigan].flags[s] = (unsigned char)0;
	sigans[num_sigan].flags[sig] = (unsigned char)1;
	
	num_sigan++;
        
        i = i+j-1;   
   }//end for
    
}

void DynamicsViz::CreateBatchSignatures() {

	int x,y,z;

	num_sigan = 0;
	for (x=0;x<3;x++) {
		for (y=0;y<3;y++) {
			for (z=0;z<3;z++) {
				if ((Sig.k[x]>=0) && (Sig.t[y]>=0) && (Sig.a[z]>=0.0)) {
					printf("creating batch %d %d %f\n", Sig.k[x], Sig.t[y], Sig.a[z]);
					CreateSignature(0, npaths-1, Sig.k[x], Sig.t[y], Sig.a[z]);
				}
			}
		}
	}

}

void DynamicsViz::CreateSignatureGroup(int p) {

	int i,j,k;
	int found;
	// find all associated signature events
	for (i=0;i<num_sigan;i++) {
		if ((p>=sigans[i].start) && (p<=sigans[i].end)) {
			// find overlapping signature group
			found = -1;
			for (j=0;j<num_siggroups;j++) {
				if (((sigans[i].start <= siggroups[j].end) &&
					 (sigans[i].start >= siggroups[j].start)) ||
					 ((siggroups[j].start <= sigans[i].end) &&
					 (siggroups[j].start >= sigans[i].start)) ) {
					found = j;
					// if found, then merge event with group
					if (sigans[i].start < siggroups[j].start) siggroups[j].start = sigans[i].start;
					if (sigans[i].end > siggroups[j].end) siggroups[j].end = sigans[i].end;
					for (k=0;k<10;k++) {
						if (sigans[i].flags[k] > (unsigned char)0) siggroups[j].flags[k] = (unsigned char)1;
					}
				}
			}
			if (found < 0) { // if not found, then start new group
				siggroups[num_siggroups].start = sigans[i].start;
				siggroups[num_siggroups].end = sigans[i].end;
				for (k=0;k<10;k++) {
					siggroups[num_siggroups].flags[k] = sigans[i].flags[k];
				}
				num_siggroups++;
			}
		}
	}
}


void DynamicsViz::GenerateOnlyEvents() {

	int i,j;
	float maxT;
	int x;
	int spath, epath;

	if (EV.num_events != 0) EV.num_events = 0;
	
	for (i=1; i<num_siggroups; i++) {
		EV.events[EV.num_events].Flag = 0;
		spath = siggroups[i].start;
		epath = siggroups[i].end;
		maxT = 0.0;
		for (j=siggroups[i].start; j<siggroups[i].end; j++) {
			if (paths[j]->T > maxT) {
				maxT = paths[j]->T;
			}
		}
		EV.events[EV.num_events].r = 4.0 * sqrt(maxT);
		for (j=0;j<10;j++) {
			if (siggroups[i].flags[j] > (unsigned char)0) {
				EV.events[EV.num_events].Flag = EV.events[EV.num_events].Flag | (1 << (j-1));
			}
		}
		EV.events[EV.num_events].sid = spath;
		EV.events[EV.num_events].eid = epath;
		// set x coordinates of the horizontal base according to date and histogram
		H.DataToScreen(paths[spath]->cdate,x);
		EV.events[EV.num_events].x1 = (float)x; 
		H.DataToScreen(paths[epath]->cdate,x);
		EV.events[EV.num_events].x2 = (float)x; 
		EV.events[EV.num_events].x = 0.5 * (EV.events[EV.num_events].x1+EV.events[EV.num_events].x2); 
		// set y coordinates of the vertical base according to time and time text
		EV.num_events++;
	}
	EV.SettleHeights();

}

void DynamicsViz::GenerateSigAnEvents() {
	
	int i,j;
	num_siggroups = 0;
	for (i=1; i<npaths; i++) {
		if (paths[i]->T > sig_thres) CreateSignatureGroup(i);
	}
	GenerateOnlyEvents();
	
}


void DynamicsViz::Draw() {
	
	int i,p;
	int tsx, tsy, txs, tys;
	H.Draw();
	if (show_events) {
		EV.Draw();
		LG.Draw();
	}
	if (show_timeline) {
		TT.Draw();
		if (aux_mode==0) {
			AC.Draw();
		} else if (aux_mode==1) {
			MV.Draw();
			EV.DrawVert();
		} else if (aux_mode==2) {
			G.Draw();
			for (p=0;p<npaths;p++) {
				if (paths[p]->cdate == current_date) {
					DrawPath(paths[p]);
				}
			}
		} else if (aux_mode==3) {
			DrawPaths3D();
		} else if (aux_mode==4) {
			tsx = G.startx;
			tsy = G.starty;
			txs = G.xsize;
			tys = G.ysize;
			G.startx = startx+510;
			G.starty = starty+10;
			G.xsize = 200;
			G.ysize = 200;
			G.Draw();
			for (p=0;p<npaths;p++) {
				if (paths[p]->cdate == current_date) {
					DrawPath(paths[p]);
				}
			}
			for (i=0;i<n_observations;i++) {
				for (p=0;p<notherpaths[i];p++) {
					if (other_observation_paths[i][p]->cdate == current_date) {
						DrawPath(other_observation_paths[i][p]);
					}
				}
			}
			G.startx = tsx;
			G.starty = tsy;
			G.xsize = txs;
			G.ysize = tys;
			for (i=0;i<n_observations;i++) {
				OTT[i]->Draw();
			}
		}
	}
}


void DynamicsViz::AdvanceDate() {
	int d;
	d = nextday(current_date);
	SetDate(d);
}

void DynamicsViz::BackDate() {
	int d;
	d = prevday(current_date);
	SetDate(d);
}

void DynamicsViz::SetDate(int d) {
	if (d>last_date) { current_date = last_date;
	} else if (d<first_date) { current_date = first_date;
	} else { current_date = d;
	}
	AddTimeItems();
	H.SetInd(current_date);
}


void DynamicsViz::AddTimeItems() {

	int p, i, j, id;
	float x;
	char buf[32];
	int c_ind;
	int first_path;

	TT.numitems = 0;
	AC.num_paths = 0;
	MV.num_paths = 0;

	for (p=0;p<npaths;p++) {
		if (paths[p]->cdate == current_date) {
			if (TT.numitems < TT.maxitems) {
				TT.times[TT.numitems] = timetoint(paths[p]->ctime);
				sprintf(TT.texts[TT.numitems],"%06d    ", paths[p]->ctime);
				AC.AddPath();
				for (j=0;j<paths[p]->length;j++) {
					sprintf(buf,"%d ", paths[p]->nodeArray[j]);
					strcat(TT.texts[TT.numitems],buf);
					x = (float)G.nodes[paths[p]->nodeArray[j]]->linearX/(float)G.maxlinearx;
					c_ind = G.nodes[paths[p]->nodeArray[j]]->linearX % MAX_ARCCOLORS;
					AC.AddNode(AC.num_paths-1, paths[p]->nodeArray[j], c_ind, x);
				}
				i = paths[p]->dpid;
				for (j=0;j<3;j++) {
					TT.colors[TT.numitems][j] = pcolors[i][j];
				}
				TT.colors[TT.numitems][3] = 1.0;
				MV.AddPath();
				for (j=0;j<5;j++) {
					MV.measures[MV.num_paths-1][j][0] = paths[p]->Q[j];
					MV.measures[MV.num_paths-1][j][1] = paths[p]->S[j];
				}
				MV.measures[MV.num_paths-1][5][0] = paths[p]->T;
				if (TT.numitems == 0) first_path = p;
				TT.numitems++;
			}
		}
	}
	TT.Arrange();

	for (i=0;i<n_observations;i++) {
		OTT[i]->numitems = 0;
		for (p=0;p<notherpaths[i];p++) {
			if (other_observation_paths[i][p]->cdate == current_date) {
				if (OTT[i]->numitems < OTT[i]->maxitems) {
					OTT[i]->times[OTT[i]->numitems] = timetoint(other_observation_paths[i][p]->ctime);
					sprintf(OTT[i]->texts[OTT[i]->numitems],"%06d    ", other_observation_paths[i][p]->ctime);
					
					for (j=0;j<other_observation_paths[i][p]->length;j++) {
						sprintf(buf,"%d ", other_observation_paths[i][p]->nodeArray[j]);
						strcat(OTT[i]->texts[OTT[i]->numitems],buf);
					}
					id = other_observation_paths[i][p]->dpid;
					for (j=0;j<3;j++) {
						OTT[i]->colors[OTT[i]->numitems][j] = pcolors[id][j];
					}
					OTT[i]->colors[OTT[i]->numitems][3] = 1.0;
					OTT[i]->numitems++;
				}
			}
		}
		OTT[i]->Arrange();
	}


	for (i=0;i<TT.numitems;i++) {
		AC.SetPathY(i,TT.ycoords[i]);
		MV.pathy[i] = TT.ycoords[i];
	}

	for (i=0;i<EV.num_events;i++) {
		EV.events[i].show_begin = EV.events[i].show_end = 0;
	}

	for (i=0;i<EV.num_events;i++) {
		if ((EV.events[i].sid >= first_path) && (EV.events[i].sid < first_path + TT.numitems)) {
			EV.events[i].y1 = TT.ycoords[EV.events[i].sid - first_path];
			EV.events[i].show_begin = 1;
		}
		if ((EV.events[i].eid >= first_path) && (EV.events[i].eid < first_path + TT.numitems)) {
			EV.events[i].y2 = TT.ycoords[EV.events[i].eid - first_path];
			EV.events[i].show_end = 1;
		}
	}
	for (i=0;i<EV.num_events;i++) {
		if ((EV.events[i].show_begin == 1) && (EV.events[i].show_end == 1)) {
			EV.events[i].yv = 0.5 * (EV.events[i].y1 + EV.events[i].y2);
			printf("vertical coords set %f %f %f\n", EV.events[i].y1, EV.events[i].y2, EV.events[i].yv);
		}
	}

}

void DynamicsViz::Init() {
	int i;
	for (i=0;i<npaths;i++) delete paths[i];
	npaths = 0;
	G.Init();
}


void DynamicsViz::DrawPath(Path *pptr) {
	int i, id;
	int nodes[32];

	id = pptr->dpid;

	nodes[0] = G.root;
	for (i=0;i<pptr->length;i++) {
		nodes[i+1] = pptr->nodeArray[i];
	}

	G.DrawPath(nodes,pptr->length+1,ndistinctpaths-id,pcolors[id],0.1+(float)id*0.2/(float)ndistinctpaths);
	
}

void DynamicsViz::DrawPaths3D() {

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective( // field of view in degree  , aspect ratio  , Z near , Z far 
						45.0, 1.0, 1.0, 100.0);	
		glViewport(toprightx,toprighty,toprightxsize,toprightysize);
				// startx, starty, xsize, ysize
				// coordinates begin from lower left corner of window
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
		glDisable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);	
		gluLookAt(5.0,15.0,30.0,  // eye  
					5.0,0.0,0.0,      // center 
					0.0, 1.0, 0.0);      // up is in postive Y direction 
		glPushMatrix();
		for (int i=0;i<npaths;i++) {
			if (paths[i]->cdate == current_date) {
				DrawPath3D(i);
			}
		}

		glColor3f(0.5,0.5,0.5);
		glBegin(GL_QUADS);
		glVertex3f(0.0,0.0,0.0);
		glVertex3f(0.0,0.0,10.0);
		glVertex3f(10.0,0.0,10.0);
		glVertex3f(10.0,0.0,0.0);
		glEnd();
		glLineWidth(2);
		glBegin(GL_LINES);
		glVertex3f(11.0,0.0,0.0);
		glVertex3f(11.0,0.0,10.0);
		glEnd();
		glBegin(GL_TRIANGLES);
		glVertex3f(11.0,0.0,0.0);
		glVertex3f(10.5,0.0,1.0);
		glVertex3f(11.5,0.0,1.0);
		glEnd();
		glRasterPos3f(12.0,0.0,0.0);
		NT.font.Render("Time");

		glPopMatrix();

}


void DynamicsViz::DrawPath3D(int p) {

	Path *pptr;
	int i, id;
	int nodes[32];

	pptr = paths[p];
	id = pptr->dpid;

	nodes[0] = G.root;
	for (i=0;i<pptr->length;i++) {
		nodes[i+1] = pptr->nodeArray[i];
	}

	G.DrawPath(nodes,pptr->length+1,3,pcolors[id],10.0-10.0 * (float)(timetoint(paths[p]->ctime))/(float)(timetoint(235959)));
	
}


