
#ifndef INSTABILITY_H
#define INSTABILITY_H

#include <math.h>

#define MAX_NUM_INSTABILITY_ENTRIES 128
#define MAX_NANNS 32
#define MAX_NUM_REGIONS 32
#define MAX_LEGEND_CLASSES 32

#ifndef PI
#define PI 3.14159265

float segmentcolors[4][3] = { { 1.0, 0.0, 0.0} , { 0.0, 1.0, 0.0} , { 1.0, 1.0, 0.0} , { 0.0, 0.0, 1.0} };

float regioncolors[5][5][3] = { 
{ {1.0,0.0,0.3},{1.0,0.2,0.0},{1.0,0.5,0.3},
	{1.0,0.3,0.1},{1.0,0.4,0.1} },
{ {0.3,1.0,0.4},{0.1,1.0,0.3},{0.3,1.0,0.1},
	{0.2,1.0,0.2},{0.0,1.0,0.2} },
{ {0.3,0.1,1.0},{0.1,0.4,1.0},{0.1,0.1,1.0},
	{0.2,0.0,1.0},{0.0,0.0,1.0} },
{ {0.3,1.0,0.4},{0.1,1.0,0.3},{0.3,1.0,0.1},
	{0.2,1.0,0.2},{0.0,1.0,0.2} },
{ {0.3,1.0,0.4},{0.1,1.0,0.3},{0.3,1.0,0.1},
	{0.2,1.0,0.2},{0.0,1.0,0.2} } 
};

class InstabilityEntry {

public:
	int Flags; // os pure/impure bal/unbal sc preWD postWD rshort rlong
			   // 0  1           2         3  4     5      6      7
			   // multWD
			   // 8

	int TimeStamp;

	float x, y, r, x1, x2;
	int resolution;

	int n; // num announcements in this instability sequence

	InstabilityEntry() : Flags(0) , TimeStamp(0) , resolution(60) , n(0) { };

	render() { // draw circle

		int i;
		int segments[4];
		int numsegments;
		int subflags[4];
		float incr, theta;
		float segmentangle;
		float adj,adj1,adj2;

		numsegments = 0;
		if (Flags & 1) { // os
			segments[numsegments] = 0;
			subflags[numsegments] = 0;
			if (Flags & (1 << 1)) subflags[numsegments] += 1;
			if (Flags & (1 << 2)) subflags[numsegments] += 2;
			numsegments++;
		}
		if (Flags & (1 << 3)) { // sc
			segments[numsegments] = 1;
			subflags[numsegments] = 0;
			if (Flags & (1 << 4)) subflags[numsegments] += 1;
			if (Flags & (1 << 5)) subflags[numsegments] += 2;
			numsegments++;
		}
		if ((Flags & (1 << 6)) || (Flags & (1 << 7))) { // r
			segments[numsegments] = 2;
			subflags[numsegments] = 0;
			if (Flags & (1 << 6)) subflags[numsegments] += 1;
			if (Flags & (1 << 7)) subflags[numsegments] += 2;
			numsegments++;
		}
		if (Flags & (1 << 8)) { // multWD
			segments[numsegments] = 3;
			numsegments++;
		}


		incr = 2.0*PI/(float)resolution;
		segmentangle = 2.0*PI/(float)numsegments;

		for (i=0;i<numsegments;i++) {
			if (segments[i]==0) { // os
				// pure/impure
				adj = 0.0;
				if ((subflags[i]%2)==1) adj = 0.3;
				glColor3f(segmentcolors[segments[i]][0],segmentcolors[segments[i]][1]+adj,segmentcolors[segments[i]][2]);
				glBegin(GL_POLYGON);
				glVertex3f(x,y,0.0);
				for (theta = segmentangle*(float)i; theta<(segmentangle*(0.5+(float)i)); theta += incr) {
					glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
				}
				glVertex3f(x+r*cos(segmentangle*(0.5+(float)i)),y+r*sin(segmentangle*(0.5+(float)i)),0.0);
				glEnd();	
				// balanced/unbalanced
				adj = 0.0;
				if ((subflags[i]/2)==1) adj = 0.3;
				glColor3f(segmentcolors[segments[i]][0],segmentcolors[segments[i]][1],segmentcolors[segments[i]][2]+adj);
				glBegin(GL_POLYGON);
				glVertex3f(x,y,0.0);
				for (theta = segmentangle*(0.5+(float)i); theta<(segmentangle*(float)(i+1)); theta += incr) {
					glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
				}
				glVertex3f(x+r*cos(segmentangle*(float)(i+1)),y+r*sin(segmentangle*(float)(i+1)),0.0);
				glEnd();	
			} else if (segments[i]==1) { // sc
				if (subflags[i]==3) {
					glColor3f(segmentcolors[segments[i]][0]+0.5,segmentcolors[segments[i]][1],segmentcolors[segments[i]][2]);
					glBegin(GL_POLYGON);
					glVertex3f(x,y,0.0);
					for (theta = segmentangle*(float)i; theta<(segmentangle*(0.5+(float)i)); theta += incr) {
						glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
					}
					glVertex3f(x+r*cos(segmentangle*(0.5+(float)i)),y+r*sin(segmentangle*(0.5+(float)i)),0.0);
					glEnd();	
					glColor3f(segmentcolors[segments[i]][0],segmentcolors[segments[i]][1],segmentcolors[segments[i]][2]+0.5);
					glBegin(GL_POLYGON);
					glVertex3f(x,y,0.0);
					for (theta = segmentangle*(0.5+(float)i); theta<(segmentangle*(float)(i+1)); theta += incr) {
						glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
					}
					glVertex3f(x+r*cos(segmentangle*(float)(i+1)),y+r*sin(segmentangle*(float)(i+1)),0.0);
					glEnd();	
				} else {
					if (subflags[i]==0) { adj1 = 0.0; adj2 = 0.0; }
					if (subflags[i]==1) { adj1 = 0.5; adj2 = 0.0; }
					if (subflags[i]==2) { adj1 = 0.0; adj2 = 0.5; }
					glColor3f(segmentcolors[segments[i]][0]+adj1,segmentcolors[segments[i]][1],segmentcolors[segments[i]][2]+adj2);
					glBegin(GL_POLYGON);
					glVertex3f(x,y,0.0);
					for (theta = segmentangle*(float)i; theta<(segmentangle*(float)(i+1)); theta += incr) {
						glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
					}
					glVertex3f(x+r*cos(segmentangle*(float)(i+1)),y+r*sin(segmentangle*(float)(i+1)),0.0);
					glEnd();
				}
			} else if (segments[i]==2) { // repeat
				if (subflags[i]==3) {
					glColor3f(segmentcolors[segments[i]][0]-0.1,segmentcolors[segments[i]][1]-0.1,segmentcolors[segments[i]][2]);
					glBegin(GL_POLYGON);
					glVertex3f(x,y,0.0);
					for (theta = segmentangle*(float)i; theta<(segmentangle*(0.5+(float)i)); theta += incr) {
						glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
					}
					glVertex3f(x+r*cos(segmentangle*(0.5+(float)i)),y+r*sin(segmentangle*(0.5+(float)i)),0.0);
					glEnd();	
					glColor3f(segmentcolors[segments[i]][0],segmentcolors[segments[i]][1],segmentcolors[segments[i]][2]);
					glBegin(GL_POLYGON);
					glVertex3f(x,y,0.0);
					for (theta = segmentangle*(0.5+(float)i); theta<(segmentangle*(float)(i+1)); theta += incr) {
						glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
					}
					glVertex3f(x+r*cos(segmentangle*(float)(i+1)),y+r*sin(segmentangle*(float)(i+1)),0.0);
					glEnd();	
				} else {
					adj = 0.0;
					if (subflags[i]==1) adj = -0.1;
					glColor3f(segmentcolors[segments[i]][0]+adj,segmentcolors[segments[i]][1]+adj,segmentcolors[segments[i]][2]);
					glBegin(GL_POLYGON);
					glVertex3f(x,y,0.0);
					for (theta = segmentangle*(float)i; theta<(segmentangle*(float)(i+1)); theta += incr) {
						glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
					}
					glVertex3f(x+r*cos(segmentangle*(float)(i+1)),y+r*sin(segmentangle*(float)(i+1)),0.0);
					glEnd();
				}
			} else { // multWD
				glColor3f(segmentcolors[segments[i]][0],segmentcolors[segments[i]][1],segmentcolors[segments[i]][2]);
				glBegin(GL_POLYGON);
				glVertex3f(x,y,0.0);
				for (theta = segmentangle*(float)i; theta<(segmentangle*(float)(i+1)); theta += incr) {
					glVertex3f(x+r*cos(theta),y+r*sin(theta),0.0);
				}
				glVertex3f(x+r*cos(segmentangle*(float)(i+1)),y+r*sin(segmentangle*(float)(i+1)),0.0);
				glEnd();
			}
		}
		
		if (publication) {
			glColor3f(0.0,0.0,0.0);
		} else {
			glColor3f(1.0,1.0,1.0);
		}
		
		glLineWidth(1);
		glBegin(GL_LINES);
		glVertex3f(x,y,0.1);
		glVertex3f(x1,0.0,0.1);
		glVertex3f(x,y,0.1);
		glVertex3f(x2,0.0,0.1);
		glEnd();

	};

	void render_base() {

		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		glColor4f(0.5,0.5,0.5,0.3);
		glBegin(GL_TRIANGLES);
		glVertex3f(x,y,0.1);
		glVertex3f(x1,0.0,0.1);
		glVertex3f(x2,0.0,0.1);
		glEnd();
		glDisable(GL_BLEND);

	};
};


class Instability {

public:

	int numentries;

	int beginunivtime, endunivtime;

	float startx, starty, xsize, ysize;

	InstabilityEntry Entries[MAX_NUM_INSTABILITY_ENTRIES];

	Instability() : numentries(0) { };

	void Init(float sx, float sy, float xs, float ys) {
		startx = sx; starty = sy; xsize = xs; ysize = ys;
	};

	void Draw() {

		int i;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0.0,1.0,0.0,ysize/xsize);
				// left, right, bottom, top
		glViewport(startx, starty, xsize, ysize);
				// startx, starty, xsize, ysize
				// coordinates begin from lower left corner of window
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	    glDisable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);

		for (i=0;i<numentries;i++) {
			Entries[i].render();
		}

		for (i=0;i<numentries;i++) {
			Entries[i].render_base();
		}
	};

	void ReadEntry(int y, int m, int d, int h, int min, int y2, int m2, int d2, int h2, int min2, char *flags, int nanns) {
		int t,i;
		t = univs[y-UNIVSTARTYEAR][m-1][d-1] * 24 * 60 + h + min;
		Entries[numentries].x1 = (float)(t-beginunivtime)/(float)(endunivtime-beginunivtime);
		t = univs[y2-UNIVSTARTYEAR][m2-1][d2-1] * 24 * 60 + h2 + min2;
		Entries[numentries].x2 = (float)(t-beginunivtime)/(float)(endunivtime-beginunivtime);
		Entries[numentries].x = 0.5 * (Entries[numentries].x1 + Entries[numentries].x2);
		Entries[numentries].y = 0.1;
		Entries[numentries].r = sqrt((float)nanns)/(MAX_NANNS*5.0);
		for (i=0;i<9;i++) {
			if (flags[i]=='1') {
				Entries[numentries].Flags = Entries[numentries].Flags | (1<<i);
			}
		}
		numentries++;	
	};

	void ReadIFile(char *fname) {
		FILE *fptr;
		char buf[256];
		int y,m,d,h,min,y2,m2,d2,h2,min2,n;
		char fg[16];
		int i,j;
		float intervals[64][2];
		int nintervals;
		float es;
		float proposedy, conflictedEnd;
		int conflicted;
		if (numentries > 0) {
			for (i=0; i<numentries; i++) {
				Entries[i].Flags = 0;
				Entries[i].TimeStamp = 0;
				Entries[i].n = 0;
			}
			numentries = 0;
		}
		beginunivtime = univs[2001-UNIVSTARTYEAR][0][0] * 24 * 60;
		endunivtime = univs[2002-UNIVSTARTYEAR][0][0] * 24 * 60;
		fptr = fopen(fname,"rt");
		while(!feof(fptr)) {
			fgets(buf,256,fptr);
			sscanf(buf,"%d %d %d %d %d %d %d %d %d %d %s %d", 
				&y,&m,&d,&h,&min,&y2,&m2,&d2,&h2,&min2,fg,&n);
			if (strlen(buf)>1) {
				ReadEntry(y,m,d,h,min,y2,m2,d2,h2,min2,fg,n);
			}
		}
		fclose(fptr);		
		numentries--;
		for (i=0;i<numentries;i++) {
			es = Entries[i].x - Entries[i].r;
			nintervals = 0;
			for (j=0;j<i;j++) {
				if (Entries[j].x + Entries[j].r > es) {
					intervals[nintervals][0] = Entries[j].y - Entries[j].r;
					intervals[nintervals][1] = Entries[j].y + Entries[j].r;
					nintervals++;
				}
			}
			conflicted = 1;
			conflictedEnd = 0.01;
			proposedy = conflictedEnd + Entries[i].r;
			while (conflicted) {
				conflicted = 0;
				for (j=0;j<nintervals;j++) {
					if (((intervals[j][0] < proposedy + Entries[i].r) && (intervals[j][0] >= proposedy - Entries[i].r)) ||
						((intervals[j][1] <= proposedy + Entries[i].r) && (intervals[j][1] > proposedy - Entries[i].r)) ||
						((proposedy + Entries[i].r <= intervals[j][1]) && (proposedy + Entries[i].r > intervals[j][0]))) {
						if (intervals[j][1] > conflictedEnd) conflictedEnd = intervals[j][1];
						proposedy = conflictedEnd + Entries[i].r + 0.001;
						conflicted = 1;
					}
				}
			}
			Entries[i].y = proposedy;
		}
	};

}; 

class Regions {

public:

	int beginunivtime, endunivtime;

	float startx, starty, xsize, ysize;


	int numregions;
	float regionsx[MAX_NUM_REGIONS];
	int regions[MAX_NUM_REGIONS];
	int subregions[MAX_NUM_REGIONS];

	void Init(float sx, float sy, float xs, float ys) {
		startx = sx; starty = sy; xsize = xs; ysize = ys;
	};

	Regions() : numregions(0) { };

	void ReadIFile(char *fname) {
		FILE *fptr;
		char buf[256];
		int r,sr,y,m,d,h,min;
		int t;
		beginunivtime = univs[2001-UNIVSTARTYEAR][0][0] * 24 * 60;
		endunivtime = univs[2002-UNIVSTARTYEAR][0][0] * 24 * 60;
		fptr = fopen(fname,"rt");
		while(!feof(fptr)) {
			fgets(buf,256,fptr);
			sscanf(buf,"%d %d %d %d %d %d %d",&r,&sr,&y,&m,&d,&h,&min);
			if (strlen(buf)>1) {
				regions[numregions] = r;
				subregions[numregions] = sr;
				t = univs[y-UNIVSTARTYEAR][m-1][d-1] * 24 * 60 + h + min;
				regionsx[numregions] = (float)(t-beginunivtime)/(float)(endunivtime-beginunivtime);
				numregions++;
			}
		}
		regionsx[numregions] = 1.0;
		fclose(fptr);		
	};

	void Draw() {

		int i;
		int r, sr;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0.0,1.0,0.0,1.0);
				// left, right, bottom, top
		glViewport(startx, starty, xsize, ysize);
				// startx, starty, xsize, ysize
				// coordinates begin from lower left corner of window
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	    glDisable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);

		for (i=0;i<numregions;i++) {
			r = regions[i]; sr = subregions[i];
			glColor3f(0.3*regioncolors[r][sr][0],0.3*regioncolors[r][sr][1],0.3*regioncolors[r][sr][2]);
			glBegin(GL_QUADS);
			glVertex3f(regionsx[i],0.0,-0.1);
			glVertex3f(regionsx[i],1.0,-0.1);
			glVertex3f(regionsx[i+1],1.0,-0.1);
			glVertex3f(regionsx[i+1],0.0,-0.1);
			glEnd();
			glColor3f(regioncolors[r][sr][0],regioncolors[r][sr][1],regioncolors[r][sr][2]);
			glBegin(GL_QUADS);
			glVertex3f(regionsx[i],0.23,0.0);
			glVertex3f(regionsx[i],0.25,0.0);
			glVertex3f(regionsx[i+1],0.25,0.0);
			glVertex3f(regionsx[i+1],0.23,0.0);
			glEnd();
		}

	};

};

class Legend {

public:

	int numClasses;

	float colors[MAX_LEGEND_CLASSES][3];
	char labels[MAX_LEGEND_CLASSES][64];

	float startx, starty, xsize, ysize;

	Legend() : numClasses(0) { };

	void Init(float sx, float sy, float xs, float ys) {
		startx = sx; starty = sy; xsize = xs; ysize = ys;
		colors[0][0] = 1.0; colors[0][1] = 0.0; colors[0][2] = 0.0; 
		colors[1][0] = 1.0; colors[1][1] = 0.3; colors[1][2] = 0.0; 
		colors[2][0] = 1.0; colors[2][1] = 0.0; colors[2][2] = 0.3;
		colors[3][0] = 0.0; colors[3][1] = 1.0; colors[3][2] = 0.0;
		colors[4][0] = 0.5; colors[4][1] = 1.0; colors[4][2] = 0.0;
		colors[5][0] = 0.0; colors[5][1] = 1.0; colors[5][2] = 0.5;
		colors[6][0] = 1.0; colors[6][1] = 1.0; colors[6][2] = 0.0;
		colors[7][0] = 0.9; colors[7][1] = 0.9; colors[7][2] = 0.0;
		colors[8][0] = 0.0; colors[8][1] = 0.0; colors[8][2] = 1.0;
		strcpy(labels[0],"os_pure_bal");
		strcpy(labels[1],"os_impure");
		strcpy(labels[2],"os_unbal");
		strcpy(labels[3],"sc_noWD");
		strcpy(labels[4],"sc_preWD");
		strcpy(labels[5],"sc_postWD");
		strcpy(labels[6],"rp_short");
		strcpy(labels[7],"rp_long");
		strcpy(labels[8],"multWD");
		numClasses = 9;
	};
	
	void Draw() {

		int i,j;
		float boxw, realboxw, margin;

		boxw = 1.0/(float)numClasses;
		margin = 0.1 * boxw;
		realboxw = 0.8 * boxw;

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluOrtho2D(0.0,1.0,0.0,1.0);
				// left, right, bottom, top
		glViewport(startx, starty, xsize, ysize);
				// startx, starty, xsize, ysize
				// coordinates begin from lower left corner of window
		glMatrixMode(GL_MODELVIEW);
		glLoadIdentity();
	    glDisable(GL_TEXTURE_2D);
		glDisable(GL_LIGHTING);

		glColor3f(0.6,0.6,0.6);
		glBegin(GL_QUADS);
		glVertex3f(0.0,0.0,0.0);
		glVertex3f(0.0,1.0,0.0);
		glVertex3f(1.0,1.0,0.0);
		glVertex3f(1.0,0.0,0.0);
		glEnd();

		for (i=0; i<numClasses; i++) {
			glColor3f(colors[i][0],colors[i][1],colors[i][2]);
			glBegin(GL_QUADS);
			glVertex3f(boxw*(float)i + margin, 0.5, 0.1);
			glVertex3f(boxw*(float)i + margin, 0.8, 0.1);
			glVertex3f(boxw*(float)i + margin + realboxw, 0.8, 0.1);
			glVertex3f(boxw*(float)i + margin + realboxw, 0.5, 0.1);
			glEnd();

			glColor3f(0.0,0.0,0.0);
			for (j=0;j<strlen(labels[i]);j++) {
				glRasterPos3f(boxw*(float)i + margin + 0.008*(float)j,0.3,0.1);
				glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_10,labels[i][j]);
			}
		
		}

	};


};

#endif PI

#endif INSTABILITY_H

