
#ifndef SIGNATURE_H
#define SIGNATURE_H

#define MAX_N_EVENTS 256
#define SHORTLONG 1200

class SignatureEvent {

public:

	
	int sy, sm, sd, sh, smin;
	int ey, em, ed, eh, emin;

	int Flag;  // os pure/impure bal/unbal sc preWD postWD rshort rlong
			   // 0  1           2         3  4     5      6      7
			   // multWD
			   // 8
	int spath, epath;
	int npaths;

	SignatureEvent() : Flag(0) , npaths(0) { };

	void AddAnn(int p) {
		epath = p;
		if (npaths==0) spath = p;
		npaths++;
	};

	void Settle(Graph *gptr) {
		int i;
		int numwd;
		numwd = 0;
		if (gptr->paths[spath]->length==0) numwd++;
		if (gptr->paths[spath+1]->length==0) numwd++;
		for (i=spath+2; i<=epath; i++) {
			if ((gptr->paths[i]->dpid==gptr->paths[i-2]->dpid) &&
				(gptr->paths[i]->dpid!=gptr->paths[i-1]->dpid) &&
				(gptr->paths[i]->length > 0) &&
				(gptr->paths[i-1]->length > 0) &&
				(gptr->paths[i-2]->length > 0)) {
				// detect oscillation
				Flag = Flag | 1;
			}
			if ((gptr->paths[i]->dpid!=gptr->paths[i-2]->dpid) &&
				(gptr->paths[i]->dpid!=gptr->paths[i-1]->dpid) &&
				(gptr->paths[i-1]->dpid!=gptr->paths[i-2]->dpid) &&
				(gptr->paths[i]->length > 0) &&
				(gptr->paths[i-1]->length > 0) &&
				(gptr->paths[i-2]->length > 0)) {
				// detect slow convergence
				Flag = Flag | (1 << 3);
			}
			if (((gptr->paths[i]->dpid==gptr->paths[i-1]->dpid) &&
				((gptr->paths[i]->timestamp - gptr->paths[i]->timestamp) < SHORTLONG)) ||
				((gptr->paths[i-1]->dpid==gptr->paths[i-2]->dpid) &&
				((gptr->paths[i-1]->timestamp - gptr->paths[i-2]->timestamp) < SHORTLONG)) ) {
				// detect repeats
				Flag = Flag | (1 << 6);
			}
			if (gptr->paths[i]->length==0) {
				numwd++;
			}
		}

		if (numwd>1) Flag = Flag | (1 << 8);

		sy = gptr->paths[spath]->year;
		sm = gptr->paths[spath]->month;
		sd = gptr->paths[spath]->day;
		sh = gptr->paths[spath]->hour;
		smin = gptr->paths[spath]->min;
		ey = gptr->paths[epath]->year;
		em = gptr->paths[epath]->month;
		ed = gptr->paths[epath]->day;
		eh = gptr->paths[epath]->hour;
		emin = gptr->paths[epath]->min;
	};

};


class Signature {

public:

	SignatureEvent EArray[MAX_N_EVENTS];

	int nevents;

	Signature() : nevents(0) { };

	int attrssame(Path *p1, Path *p2) {
		int i;
		int r;
		r = 1; // assume same
		for (i=0;i<5;i++) {
			if (strcmp(p1->attributes[i],p2->attributes[i])!=0) {
				r = 0;
			}
		}
		return r;
	}

	void GenerateEvents(Graph *gptr) {
		int i;
		int currentlyactive;
		int currentlylong;
		if (nevents > 0) {
			for (i=0;i<nevents;i++) {
				EArray[i].npaths = 0;
				EArray[i].Flag = 0;
			}
			nevents = 0;
		}
		currentlyactive = 0;
		currentlylong = 0;
		for (i=1; i<gptr->npaths; i++) {
			// for each path
			// if timestamp is not too far from prev, then include in event
			if (gptr->paths[i]->timestamp < gptr->paths[i-1]->timestamp + SHORTLONG) {
				if (currentlyactive) {
					EArray[nevents-1].AddAnn(i);
				} else {
					nevents++;
					EArray[nevents-1].AddAnn(i-1);
					EArray[nevents-1].AddAnn(i);
					currentlyactive = 1;
				}
				if (currentlylong) currentlylong = 0;
			} else {
				if (currentlyactive) {
					if (EArray[nevents-1].npaths<=2) {
						EArray[nevents-1].npaths = 0;
						nevents--;
					}
					currentlyactive = 0;
				}
					
				if (currentlylong) {
					if ((gptr->paths[i]->dpid == gptr->paths[i-1]->dpid) &&
						(attrssame(gptr->paths[i],gptr->paths[i-1]))) {
						EArray[nevents-1].AddAnn(i);
					} else {
						currentlylong = 0;
					}
				} else {
				
					if (currentlyactive) {
						if (EArray[nevents-1].npaths<=2) {
							EArray[nevents-1].npaths = 0;
							nevents--;
						}
						currentlyactive = 0;
					}
					if ((gptr->paths[i]->dpid == gptr->paths[i-1]->dpid) &&
						(attrssame(gptr->paths[i],gptr->paths[i-1]))) {
						nevents++;
						EArray[nevents-1].AddAnn(i-1);
						EArray[nevents-1].AddAnn(i);
						currentlylong = 1;
						EArray[nevents-1].Flag |= (1 << 7);
					}
				}
			}
		}
		if (EArray[nevents-1].npaths<=2) {
			EArray[nevents-1].npaths = 0;
			nevents--;
		}
		for (i=0; i<nevents;i++) {
			// for each event, finalize start and end times and classify
			EArray[i].Settle(gptr);
		}
	};


	void OutputEvents(char *fname) {
		FILE *fptr;
		int i,j;
		char flagstring[32];
		fptr = fopen(fname,"wt");
		for (i=0;i<nevents; i++) {
			for (j=0;j<9;j++) {
				if ((EArray[i].Flag & (1 << j)) != 0) {
					flagstring[j] = '1';
				} else {
					flagstring[j] = '0';
				}
			}
			flagstring[9] = '\0';
			fprintf(fptr,"%d %d %d %d %d %d %d %d %d %d %s %d\n",
				EArray[i].sy, EArray[i].sm, EArray[i].sd, EArray[i].sh, EArray[i].smin,
				EArray[i].ey, EArray[i].em, EArray[i].ed, EArray[i].eh, EArray[i].emin,  
				flagstring, EArray[i].npaths);
		}
		fclose(fptr);

	};

};

#endif

