#include <ctime>
#include <iostream>
#include "SeeGraph.h"
#include "seeGScript.h"

using namespace std;

int featureColors = 2;
FILE* cameraPositions;

extern bool RecordActions;
extern clock_t ScriptStartTime;
extern float scriptWaitTime;
extern bool isScripting;


//------------------------------------------------------------------------
/// The code here processes mouse input to rotate the screen accordingly.
//------------------------------------------------------------------------
void mouseHandler(int button, int bstate, int x, int y) {
	int skipFlag;

  if(RecordActions)
	  RecordScript_Mouse(button,bstate,x,y);

	/* your code here */
	if(showBTDMatrix) {
		if(btdClicked(d2,x,GL_WIN_HEIGHT-y)==1) {
			//Processing for left mouse button
         if (button == GLUT_LEFT_BUTTON && bstate == GLUT_DOWN) {
            btdBeginPickBlocks(d2, x,GL_WIN_HEIGHT-y);
         }
			else if (button == GLUT_LEFT_BUTTON && bstate == GLUT_UP) {
            if(!showBTDMatrixBelt) skipFlag = btdEndPickBlocks(d2, x,GL_WIN_HEIGHT-y);
            else skipFlag = btdEndPickDiamonds(d2, x,GL_WIN_HEIGHT-y);

				if (!showBTDasLOD && skipFlag==0) {
					// Update to only show selected BTD
					showOnlyBTD(d2, g);
				} else if (skipFlag==0) {
	/*printf(" G DATA: %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x\n",
			g->name,g->mat2d,g->edgeLabels,g->degree,g->VERTEX,g->vi,g->vertRoot,
			g->vertData,g->edgeRoot,g->edgeData,g->queryColors,g->fltQuery,g->strQuery,g->numBin,
			g->lodBin,g->vertList,g->vertColorList,g->vertPosn,g->edgeList,g->edgeList1d,g->tl1d2);*/
					// Rearrange the display to show the new LOD layout
					if(d2->numSArea==1) {
						lod  = createLOD(d2,g);
						glod = g;
						g    = lod;
						//printf(" SHOULD DISPLAY %d verts %d edges\n",lod->numVerts,lod->numEdges);
					} else {
						updateLastLOD(d2,glod, lod);
						g = lod;
						//printf(" SHOULD DISPLAY %d verts %d edges\n",lod->numVerts,lod->numEdges);
					}
				}

				//Known Issue: BTD not currently compatible with splats
				//updateVoxelVisibility(&voxels, -1, g);
				//in case the user ends rotation over btd
				Spaceball.StopMotion(x, y, glutGet(GLUT_ELAPSED_TIME)); 
			}
		}
		else {
			if(allowPicking) {
				if (button == GLUT_LEFT_BUTTON && bstate == GLUT_DOWN)
					pickVertices(x,y,button);
				else if (button==GLUT_MIDDLE_BUTTON && bstate==GLUT_DOWN)
					pickVertices(x,y,button);
			} else {
				if (button == GLUT_LEFT_BUTTON && bstate == GLUT_DOWN)
					Spaceball.StartMotion(x, y, glutGet(GLUT_ELAPSED_TIME));
				else if (button == GLUT_LEFT_BUTTON && bstate == GLUT_UP)
					Spaceball.StopMotion(x, y, glutGet(GLUT_ELAPSED_TIME));
			}
		}
	}
	else {
		if(allowPicking) {
			if (button == GLUT_LEFT_BUTTON && bstate == GLUT_DOWN)
				pickVertices(x,y,button);
			else if (button==GLUT_MIDDLE_BUTTON && bstate==GLUT_DOWN)
				pickVertices(x,y,button);
		} else {
			if (button == GLUT_LEFT_BUTTON && bstate == GLUT_DOWN)
				Spaceball.StartMotion(x, y, glutGet(GLUT_ELAPSED_TIME));
			else if (button == GLUT_LEFT_BUTTON && bstate == GLUT_UP)
				Spaceball.StopMotion(x, y, glutGet(GLUT_ELAPSED_TIME));
		}
	}
}

//------------------------------------------------------------------------
/// The code here updates the tracking for the arcball
//------------------------------------------------------------------------
void trackMotion(int x, int y) {
	/* your code here */
	if (Spaceball.Track())
		Spaceball.TrackMotion(x, y, glutGet(GLUT_ELAPSED_TIME));
}

//------------------------------------------------------------------------
// Window resize function
//------------------------------------------------------------------------
void glutResize(int width, int height)
{
	GL_WIN_WIDTH  = width;
	GL_WIN_HEIGHT = height;

	glViewport(0, 0, width, height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
      /* modify this line to change perspective values */
	gluPerspective(45.0, (float)width/(float)height, 1.0, 1500.0);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//Make sure text stays at same location
	textY = (height-9)/(float)height;
}

//------------------------------------------------------------------------
// Function that handles keyboard inputs
//------------------------------------------------------------------------
void glutKeyboard(unsigned char key, int x, int y)
{
	int i, j;// k, tmpInt;
	int stepSize=1;
  float min,max;
  char* str;
  static int queryOption = 0;

  if(RecordActions)
	  RecordScript_Keyboard(key,x,y);

	switch (key)
	{
		case ESC:
			exit(0);
		case '+':
		case '=':
			view.zoom+=0.01f;
			break;
		case '_':
		case '-':
			view.zoom-=0.01f;
			break;
		case '1':
			if (g->fltQuery[currentQuery][1]<g->numEdges) { //change to default smart-size max
				(g->fltQuery[currentQuery][1])+=stepSize;
				queryChanged();
			}
			break;
		case 'Q':
		case 'q':
			if (g->fltQuery[currentQuery][1]>g->fltQuery[currentQuery][0]) {
				(g->fltQuery[currentQuery][1])-=stepSize;
				queryChanged();
			}
			break;
		case 'A':
		case 'a':
			if (g->fltQuery[currentQuery][0]<g->fltQuery[currentQuery][1]) {
				(g->fltQuery[currentQuery][0])+=stepSize;
				queryChanged();
			}
			break;
		case 'Z':
		case 'z':
			if (g->fltQuery[currentQuery][0]>0) {
				(g->fltQuery[currentQuery][0])-=stepSize;
				queryChanged();
			}
			break;
		case 'F':
		case 'f':
			/*g->fltQuery[0][0]=9.f;
			g->fltQuery[1][0]=6.f;
			g->fltQuery[2][0]=3.f;*/
      g->fltQuery[0][2]=1485.f;g->fltQuery[0][3]=1486.f;
			g->fltQuery[1][2]=1360.f;g->fltQuery[1][3]=1485.f;
			g->fltQuery[2][2]=4.f;g->fltQuery[2][3]=4.f;

       /*
       if(queryOption==0) {
          g->fltQuery[0][2]=0.f;g->fltQuery[0][3]=1.f;
		   g->fltQuery[1][2]=1.f;g->fltQuery[1][3]=2.f;
		   g->fltQuery[2][2]=2.f;g->fltQuery[2][3]=3.f;
       }
       else if (queryOption==1) {
          g->fltQuery[0][2]=-1.f;g->fltQuery[0][3]=21.f;
		   g->fltQuery[1][2]=-1.f;g->fltQuery[1][3]=21.f;
		   g->fltQuery[2][2]=-1.f;g->fltQuery[2][3]=21.f;

          g->fltQuery[0][8]=0.f;g->fltQuery[0][9]=1.f;
		   g->fltQuery[1][8]=0.f;g->fltQuery[1][9]=-1.f;
		   g->fltQuery[2][8]=0.f;g->fltQuery[2][9]=-1.f;
       }
       else if (queryOption==2) {
          g->fltQuery[0][8]=0.f;g->fltQuery[0][9]=1.f;
		   g->fltQuery[1][8]=1.f;g->fltQuery[1][9]=2.f;
		   g->fltQuery[2][8]=3.f;g->fltQuery[2][9]=3.f;
          queryOption=0;
       }*/
		  queryChanged();
      queryOption++;
			break;
		case 'S':
		case 's':
			if(pixFile==NULL) {
				if( (pixFile=(char*)malloc( 10*sizeof(char))) == NULL) {
					perror("pixfile"); exit(-1);
				}
			}
			sprintf(pixFile,"%d.bmp",pixCount);
         takeScreenshot(pixFile);
         pixCount++;
			break;
    case 'C':
    case 'c':
      /*
      //I) 1 image of 20 largest paracliques
      glutSpecial(GLUT_KEY_RIGHT,0,0);
      for(j=0;j<18;j++) { //each threshold
        // Increaes threshold one step
        glutSpecial(GLUT_KEY_RIGHT,0,0);

        for(i=0;i<g->numQueries;i++) {
           g->fltQuery[i][4+2*j]=1.f-(1e-4);
           g->fltQuery[i][5+2*j]=0.f+(1e-4);
        }
        if(j>0) {
           getMinMax(g,2+j-1,&min,&max);
           g->fltQuery[0][4+2*(j-1)]=min;
           g->fltQuery[0][5+2*(j-1)]=max;
        }
        g->fltQuery[0][4+2*j]=-0.5f;
        g->fltQuery[0][5+2*j]=19.5f;
        queryChanged();

        // Normalise colors for only 20 pcs instead of all of them
        // note: have to disable colorbyfeature in writekaryo to see effect
        for(int k=0;k<g->numDisplayVerts;k++) {
          tmpInt = g->vertList[k]*(g->numVertFeatures+1);
          min = g->vertData[tmpInt+2+j];
          g->vertColorList[g->vertList[k]] = (int)(255.f*min/19.f);
        }

        if(karFile==NULL) {
			    if( (karFile=(char*)malloc(64*sizeof(char))) == NULL) {
				    perror("karFile"); exit(-1);
			    }
		    }
		    sprintf(karFile,"89_w%.2f_20pcs.bmp",.1+j*.05);
        //printf("%s - %d\n",karFile,g->numDisplayVerts);
        writeKaryo(karFile,g,tlut,featureColors);
        karCount++;
      }
      */

      
      //II) 20 largest paracliques for the 19 different weight settings
      if(karFile==NULL) {
		    if( (karFile=(char*)malloc(64*sizeof(char))) == NULL) {
			    perror("karFile"); exit(-1);
		    }
	    }
	    sprintf(karFile,"89_20colors.bmp");
      //printf("%s - %d\n",karFile,g->numDisplayVerts);
      writeKaryo(karFile,g,tlut,0);
      karCount++;

      for(j=0;j<18;j++) { //each threshold
        // Increaes threshold one step
        glutSpecial(GLUT_KEY_RIGHT,0,0);

        //twenty largest paracliques
        for(int k=0;k<20;k++) {
          // Update query for paracliques
          for(i=0;i<g->numQueries;i++) {
             g->fltQuery[i][4+2*j]=(float)(1.0-(1e-4));
             g->fltQuery[i][5+2*j]=(float)(0.0+(1e-4));
          }
          if(j>0) {
             getMinMax(g,2+j-1,&min,&max);
             g->fltQuery[0][4+2*(j-1)]=min;
             g->fltQuery[0][5+2*(j-1)]=max;
          }
          g->fltQuery[0][4+2*j]=(float)k-0.5f;
          g->fltQuery[0][5+2*j]=(float)k+0.5f;
          //printQuery(0, g);
          queryChanged();

          // Change vertex colors based on paracliques
          //colorByFeature(g,featureColors);
          //showFullVerts = 1;

          if(karFile==NULL) {
				    if( (karFile=(char*)malloc(64*sizeof(char))) == NULL) {
					    perror("karFile"); exit(-1);
				    }
			    }
			    sprintf(karFile,"89_w%.2f_pc%d.bmp",.1+j*.05,k);
          //printf("%s - %d\n",karFile,g->numDisplayVerts);
          writeKaryo(karFile,g,tlut,0);
          karCount++;
        }
        featureColors++;
      }
      break;
		//case 'R':
		//case 'r':
		//	callR(g);
		//	break;
    case 'R':
		case 'r':
      //Reset the threshold
      displayThresh = 0;

      //Reset BTD selections
      if(showBTDMatrix) {
        d2->numSArea=0;
        if(d2->sArea != NULL) {
           free (d2->sArea);
           d2->sArea=NULL;
        }
        d2->areaStarted = 0;
      }

      //Reset the database queries
      for(i=0;i<g->numQueries;i++) {
         for(j=0;j<g->numVertFeatures;j++) {
            getMinMax(g,j,&min,&max);
            g->fltQuery[i][2*j]   = min;
            g->fltQuery[i][2*j+1] = max;
         }
      }
      queryChanged();

      break;
		case 'H':
		case 'h':
				showUnselected=!showUnselected;
				/*if(showUnselected) {
					showOnlyLCC=0;
					geneTransparentAlpha=255;
					tlut[geneTransparentColor][3]=geneTransparentAlpha;
					if(viewMode==VIEW_SPLAT)	vcbVolrInitS(voxtransf);
				}
				else {
					geneTransparentAlpha=0;
					tlut[geneTransparentColor][3]=geneTransparentAlpha;
					if(viewMode==VIEW_SPLAT)	vcbVolrInitS(voxtransf);
				}*/
				updateDisplayVars(g);
			  break;
		case 'L':
		case 'l':
			if(showBTDMatrix && showBTDasLOD) {
				if ( initLearnDataLOD(learn,g,0) != -1 ) {
					oldTime=clock();
					classify(n,learn,test);
  					printf(" Learn = %0.2fms\n",1000.f*(clock()-oldTime)/(float)CLOCKS_PER_SEC);
					/*printf("  Learn = %fms, Epoch: %d, MSE: %f (%d correct)\n",
						1000.f*(clock()-oldTime)/(float)CLOCKS_PER_SEC,
						n->nepochs,n->a.mse,nCorrect);*/

					//Bad hardcoding since can't swap between LOD and regular graph yet
					lod = g;
					g   = glod;
					glod = g;
					updateClassification(n,g);
					allowPicking=!allowPicking;
/*printf(" G DATA: %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x %0x\n",
		g->name,g->mat2d,g->edgeLabels,g->degree,g->VERTEX,g->vi,g->vertRoot,
	    g->vertData,g->edgeRoot,g->edgeData,g->queryColors,g->fltQuery,g->strQuery,g->numBin,
	    g->lodBin,g->vertList,g->vertColorList,g->vertPosn,g->edgeList,g->edgeList1d,g->tl1d2);*/
				}
			}
			else {
				if ( initLearnData(learn,g,0) != -1 ) {
					oldTime=clock();
					classify(n,learn,test);
  					printf(" Learn = %0.2fms\n",1000.f*(clock()-oldTime)/(float)CLOCKS_PER_SEC);
					/*printf("  Learn = %fms, Epoch: %d, MSE: %f (%d correct)\n",
						1000.f*(clock()-oldTime)/(float)CLOCKS_PER_SEC,
						n->nepochs,n->a.mse,nCorrect);*/
					updateClassification(n,g);
				}
			}
			break;
		case 'K':
		case 'k':
			if (degreeStep<(1e-6)) {
				cameraPositions=fopen("CameraPositions.txt","w");
				degreeStep=2.f;
			}
			else {
				fclose(cameraPositions);
				degreeStep=0;
			}
			break;
		case '~':
		case '`':
			drawBusy();
			FILE* fout;
			fout = fopen("output-VertList.txt","w");
			fprintf(fout,"Vertices in Vertex List - %d\n\nVertex List:\n",g->numDisplayVerts);
			//for(i=0;i<g->numDisplayVerts;i++) fprintf(fout,"%d - %d passed\n",g->vertList[i],g->degree[displayThresh][g->vertList[i]]);
			for(i=0;i<g->numDisplayVerts;i++) fprintf(fout,"%d\t",g->vertList[i]);
			fprintf(fout,"\n");
			for(i=0;i<g->numDisplayVerts;i++) fprintf(fout,"%s\t",g->edgeLabels->list[g->vertList[i]]);
			fprintf(fout,"\n");
			for(i=0;i<g->numDisplayVerts;i++) {
				fprintf(fout,"\nVERTEX %d - %d EDGES:\n",g->vertList[i],g->degree[0][g->vertList[i]]);
				for(j=0;j<g->vertList[i];j++)
					if(g->mat2d[g->vertList[i]][j]!=INAN)
						fprintf(fout,"%d\t%d\t%f\n",g->vertList[i],j,(float)g->mat2d[g->vertList[i]][j]/(float)DENOM_THRESH);
				for(j=g->vertList[i]+1;j<g->numDisplayVerts;j++)
					if(g->mat2d[j][g->vertList[i]]!=INAN)
						fprintf(fout,"%d\t%d\t%f\n",j,g->vertList[i],(float)g->mat2d[j][g->vertList[i]]/(float)DENOM_THRESH);
			}
			fclose(fout);
			fout = fopen("output-EdgeList.txt","w");
			fprintf(fout,"Edges in Edge List - %d\n\nEdge List:\n",g->numDisplayEdges);
			for(i=0;i<g->numDisplayEdges;i++)
				fprintf(fout,"%d\t%d\t%f\n",g->edgeList[2*i],g->edgeList[2*i+1],
					(float)g->mat2d[g->edgeList[2*i]][g->edgeList[2*i+1]]/(float)DENOM_THRESH);
			fclose(fout);
			break;
		case 'P':
		case 'p':
			allowPicking=!allowPicking;
			break;
		case 'O':
		case 'o':
         str=(char*)malloc( (strlen(g->name)+7)*sizeof(char) );
         if(str==NULL) {perror("Key to change 2d/3d layout: str");}
      	strcpy(str,g->name);

         useTwoDLayout=!useTwoDLayout;
         if(useTwoDLayout) strcpy(&str[strlen(g->name)-4],"_2d.gvl");
	      else strcpy(&str[strlen(g->name)-4],".gvl");
         if(initOneLocation(str, g)!=0) exit(-1);
         free(str);
         free(g->vertPosn);
         g->vertPosn = view.locList[0];

			break;
      case 'T':
		case 't':
			showText=!showText;
			break;
		case 'V':
		case 'v':
			showVertLabels=!showVertLabels;
			break;
		case 'G':
		case 'g':
			drawBusy();
			createSubgraph(g);
			break;
		case 'D':
		case 'd':
			showBTDasLOD = !showBTDasLOD;
			//HACK: BIG MEMORY LEAK
			if(showBTDasLOD) {
				lod  = createLOD(d2,g);
				glod = g;
				g    = lod;
			}
			else {
				g = glod;
            glod = lod;
			}
         showVertHalos=0;
			break;
      case '}':
		case ']':
         //btdBeginPickBlocks(d2, 305,86); btdEndPickDiamonds(d2, 336,86);
         
         /* A few diamonds for LoD
         btdBeginPickBlocks(d2, 44,86);   btdEndPickDiamonds(d2, 102,85);
         btdBeginPickBlocks(d2, 305,87);  btdEndPickDiamonds(d2, 364,84);
         btdBeginPickBlocks(d2, 389,85);  btdEndPickDiamonds(d2, 458,85);
         btdBeginPickBlocks(d2, 350,132); btdEndPickDiamonds(d2, 406,130);
   		showOnlyBTD(d2, g);
			printf("Made custom selection\n");*/

         //10-selection BTD for LoD (tvcg1, height 150)
         /*btdBeginPickBlocks(d2, 47,86);  btdEndPickDiamonds(d2, 56,85);
         btdBeginPickBlocks(d2, 47,85);  btdEndPickDiamonds(d2, 73,85);
         btdBeginPickBlocks(d2, 47,84);  btdEndPickDiamonds(d2, 100,84);
         btdBeginPickBlocks(d2, 120,85); btdEndPickDiamonds(d2, 136,85);
         btdBeginPickBlocks(d2, 120,85); btdEndPickDiamonds(d2, 148,84);
         btdBeginPickBlocks(d2, 177,85); btdEndPickDiamonds(d2, 187,84);
         btdBeginPickBlocks(d2, 305,85); btdEndPickDiamonds(d2, 313,85);
         btdBeginPickBlocks(d2, 306,85); btdEndPickDiamonds(d2, 334,85);
         btdBeginPickBlocks(d2, 306,85); btdEndPickDiamonds(d2, 360,85);
         btdBeginPickBlocks(d2, 390,84); btdEndPickDiamonds(d2, 458,86);
   		showOnlyBTD(d2, g);
			printf("Made custom selection\n");*/

         /*//Josh's selections
         btdBeginPickBlocks(d2, 47,85);  btdEndPickDiamonds(d2, 56,85);
         btdBeginPickBlocks(d2, 47,85);  btdEndPickDiamonds(d2, 73,85);
         btdBeginPickBlocks(d2, 47,85);  btdEndPickDiamonds(d2, 100,85);
         btdBeginPickBlocks(d2, 120,85); btdEndPickDiamonds(d2, 136,85);
         btdBeginPickBlocks(d2, 120,85); btdEndPickDiamonds(d2, 148,85);
         btdBeginPickBlocks(d2, 177,85); btdEndPickDiamonds(d2, 187,85);
         btdBeginPickBlocks(d2, 306,85); btdEndPickDiamonds(d2, 313,85);
         btdBeginPickBlocks(d2, 306,85); btdEndPickDiamonds(d2, 334,85);
         btdBeginPickBlocks(d2, 306,85); btdEndPickDiamonds(d2, 360,85);
         btdBeginPickBlocks(d2, 390,85); btdEndPickDiamonds(d2, 458,85);
   		showOnlyBTD(d2, g);
			printf("Made custom selection\n");*/

         //Josh's selections
         btdBeginPickBlocks(d2, 47,85);  btdEndPickDiamonds(d2, 56,85);
         btdBeginPickBlocks(d2, 56,85);  btdEndPickDiamonds(d2, 73,85);
         btdBeginPickBlocks(d2, 73,85);  btdEndPickDiamonds(d2, 100,85);
         btdBeginPickBlocks(d2, 120,85); btdEndPickDiamonds(d2, 136,85);
         btdBeginPickBlocks(d2, 136,85); btdEndPickDiamonds(d2, 148,85);
         btdBeginPickBlocks(d2, 177,85); btdEndPickDiamonds(d2, 187,85);
         btdBeginPickBlocks(d2, 306,85); btdEndPickDiamonds(d2, 313,85);
         btdBeginPickBlocks(d2, 313,85); btdEndPickDiamonds(d2, 334,85);
         btdBeginPickBlocks(d2, 334,85); btdEndPickDiamonds(d2, 360,85);
         btdBeginPickBlocks(d2, 390,85); btdEndPickDiamonds(d2, 458,85);
   		showOnlyBTD(d2, g);
			printf("Made custom selection\n");

         //10-selection BTD for LoD (tvcg2, height 250)
         /*btdBeginPickBlocks(d2, 72,135);  btdEndPickDiamonds(d2, 88,135);
         btdBeginPickBlocks(d2, 135,135); btdEndPickDiamonds(d2, 144,135);
         btdBeginPickBlocks(d2, 72,135);  btdEndPickDiamonds(d2, 144,135);
         btdBeginPickBlocks(d2, 192,135); btdEndPickDiamonds(d2, 258,135);
         btdBeginPickBlocks(d2, 358,135); btdEndPickDiamonds(d2, 410,135);
         btdBeginPickBlocks(d2, 501,135); btdEndPickDiamonds(d2, 516,135);
         btdBeginPickBlocks(d2, 643,135); btdEndPickDiamonds(d2, 661,135);
         btdBeginPickBlocks(d2, 694,135); btdEndPickDiamonds(d2, 706,135);
         btdBeginPickBlocks(d2, 643,135); btdEndPickDiamonds(d2, 706,135);
         btdBeginPickBlocks(d2, 643,135); btdEndPickDiamonds(d2, 757,135);
   		showOnlyBTD(d2, g);
			printf("Made custom selection\n");*/
			break;
		case '2':
		case '4':
		case '6':
		case '8':
		case '/':
		case '*':
			if(showBTDasLOD) moveVertex(g,key);
			else if (numSGs!=0) moveGraph(sg[numSGs-1],key);
         else moveGraph(g,key);
	}
}

//------------------------------------------------------------------------
// The glut's menu callback function. Called every time an option is selected
//------------------------------------------------------------------------
void glutMenu(int value)
{
	int i;
	switch (value)
	{
		case 1:
			fullScreen=!fullScreen;
			if(fullScreen) glutFullScreen();
			else {
				glutReshapeWindow(GL_WIN_WIDTH, GL_WIN_HEIGHT);
				//Add room for menu, not taken into account by function
				glutPositionWindow(GL_WIN_INITIAL_X+4, GL_WIN_INITIAL_Y+23); 
			}
			return;
		case 2:
			exit(0);
		case 3:
			initColor(HOTCOLD);
			for(i=0;i<g->numVerts;i++) {
				if(g->vertColorList[i]==geneNodeColor)
					g->vertColorList[i]=(unsigned char)255;
				if(g->vertColorList[i]==geneTransparentColor)
					g->vertColorList[i]=(unsigned char)2;
			}
			tlut[geneTransparentColor][3]=255;
			geneNodeColor=255; geneTransparentColor=2;
			tlut[geneTransparentColor][3]=(float)geneTransparentAlpha;
         if(viewMode==VIEW_SPLAT) {
            enableSplatting();
	         vcbVolrInitS(voxtransf);
            disableSplatting();
         }
			return;
		case 4:
			initColor(FULLCOLOR);
			for(i=0;i<g->numVerts;i++) {
				if(g->vertColorList[i]==geneTransparentColor)
					g->vertColorList[i]=87;
				if(g->vertColorList[i]==geneNodeColor)
					g->vertColorList[i]=1;
			}
			tlut[geneTransparentColor][3]=255;
			geneNodeColor=1; geneTransparentColor=87;
			tlut[geneTransparentColor][3]=(float)geneTransparentAlpha;
         if(viewMode==VIEW_SPLAT) {
            enableSplatting();
         	vcbVolrInitS(voxtransf);
            disableSplatting();
         }
			return;
	}
}

//------------------------------------------------------------------------
// Function that handles special keyboards input (SHIFT, CTRL, ALT)
//------------------------------------------------------------------------
void glutSpecial(int value, int x, int y)
{
	if(RecordActions)
		RecordScript_SpecialKeyboard(value,x,y);
    switch (value)
    {
        case GLUT_KEY_F1:
				   fullScreen=!fullScreen;
				   if(fullScreen) glutFullScreen();
				   else {
					   glutReshapeWindow(GL_WIN_WIDTH, GL_WIN_HEIGHT);
					   //Add room for menu, not taken into account by function
					   glutPositionWindow(GL_WIN_INITIAL_X+4, GL_WIN_INITIAL_Y+23);
				   }
                break;
        case GLUT_KEY_F2:
               viewMode=VIEW_QUADRICS;
				   disableSplatting();
                break;
        case GLUT_KEY_F3:
               viewMode=VIEW_SPLAT;
               enableSplatting();
	            vcbVolrInitS(voxtransf);
               disableSplatting();
                break;
        case GLUT_KEY_F4:
					showNormals=!showNormals;
                break;
        case GLUT_KEY_F5:
					showEdges=!showEdges;
                break;
        case GLUT_KEY_F6:
					normalizeEdgeColors=!normalizeEdgeColors;
				   //printf("Norm=%d,Sep=%d\n",normalizeEdgeColors,separateEdgeColors);
                break;
        case GLUT_KEY_F7:
					separateEdgeColors=!separateEdgeColors;
			      //printf("Norm=%d,Sep=%d\n",normalizeEdgeColors,separateEdgeColors);
                break;
        case GLUT_KEY_F8:
					showDisconnected=!showDisconnected;
					if(showDisconnected) {
						showOnlyLCC=0;
						geneTransparentAlpha=255;
						tlut[geneTransparentColor][3]=(float)geneTransparentAlpha;
						if(viewMode==VIEW_SPLAT)	vcbVolrInitS(voxtransf);
					}
					else {
						geneTransparentAlpha=0;
						tlut[geneTransparentColor][3]=(float)geneTransparentAlpha;
						if(viewMode==VIEW_SPLAT)	vcbVolrInitS(voxtransf);
					}
					updateDisplayVars(g);
               break;
        case GLUT_KEY_F9:
					showOnlyLCC=!showOnlyLCC;
					if(showOnlyLCC) {
						showDisconnected=0;
						/*for(int i=0;i<g->numDisplayVerts;i++) {
							printf("%s %d\n",g->edgeLabels->list[i],g->vertColorList[g->vertList[i]]);
							//filterInducedSubgraphAllButColor(g, 0);
							filterInducedSubgraphShow(g,255);
						}*/
					}
					else { 
						numDisplayVerts=RunVertQuery(g);
                  //numDisplayEdges=RunEdgeQuery(getCurrentThresh(),absoluteValue,g);
					}
					updateDisplayVars(g);
               break;
        case GLUT_KEY_F10:
					showFullEdges=!showFullEdges;
					updateDisplayVars(g);
               break;
        case GLUT_KEY_F11:
					activeQuery=!activeQuery;
					if(activeQuery) {
						drawBusy();
						numDisplayVerts=RunVertQuery(g);
                  //numDisplayEdges=RunEdgeQuery(getCurrentThresh(),absoluteValue,g);
						updateDisplayVars(g);
					}
   				break;
        case GLUT_KEY_F12:
            showBTDMatrix=!showBTDMatrix;
            break;
		case GLUT_KEY_UP:
				view.zoom+=0.01f;
            //Adaptive resolution
            /*if( (view.zoom>0.1f) && ((int)(view.zoom*100.f)%10==0) ) {
               resX+=1;
               resY+=1;
            }*/
            break;
		case GLUT_KEY_DOWN:
				view.zoom-=0.01f;
            //Adaptive resolution
            /*if( (view.zoom>0.1f) && ((int)(view.zoom*100.f)%10==0) ) {
               resX-=1;
               resY-=1;
            }*/
				break;
		case GLUT_KEY_RIGHT:
			if(displayThresh<(numFiles-1)) {
				displayThresh++;//Needs to be fixed for F-keys
				numDisplayVerts=RunVertQuery(g);//numDisplayEdges=RunEdgeQuery(getCurrentThresh(),absoluteValue,g);//Needs to be fixed for F-keys
				if(viewOption==OPT_MULTIGVL) updateVoxelPosition(displayThresh, g);
				else if (viewOption==OPT_SINGLEGVLSTEP) updateDisplayVars(g);
			}
			break;
		case GLUT_KEY_LEFT:
			if(displayThresh>0) {
				displayThresh--;
				numDisplayVerts=RunVertQuery(g);//numDisplayEdges=RunEdgeQuery(getCurrentThresh(),absoluteValue,g);//Needs to be fixed for F-keys
				if(viewOption==OPT_MULTIGVL) updateVoxelPosition(displayThresh, g);
				else if (viewOption==OPT_SINGLEGVLSTEP) updateDisplayVars(g);
			}
			break;
		case GLUT_KEY_PAGE_UP:
			if(currentQuery>0)
				currentQuery--;
			break;
		case GLUT_KEY_PAGE_DOWN:
			if(currentQuery<g->numQueries)
				currentQuery++;
			break;
    }
}

//************************************************************************
// The main draw function
//************************************************************************
void drawGraph(graph* g) {
	if (viewMode==VIEW_QUADRICS && showVertHalos && !useTwoDLayout) {
		if(showVertHalos) {
         enableSplatting();
			drawVertexHalos(voxels,g,&view);
         disableSplatting();
		}
	}

	if(showEdges) {
		drawEdges(g,tlut,threshold,normalizeEdgeColors,separateEdgeColors);
	}

	if (viewMode==VIEW_QUADRICS) {
		//glLightfv(GL_LIGHT1, GL_POSITION, view.lightPos);	//Initial half-shadow
		drawVertQuadrics(g,tlut);
	}
	else if(viewMode==VIEW_SPLAT) {
      enableSplatting();
		if(updateNormals) {
			updateSplatNormals();
		}
		drawVertexSplats(voxels,g,&view);
		if(showNormals) {
			drawHairySplats();
		}
      disableSplatting();
	}
	else { printf("Unknown viewMode\n"); exit(-1); }
   
	if(showVertLabels) {
		drawVertLabels(g);
	}

	if(showBTDMatrix) {
		//drawBTDMatrix(d2, 50-4*800/2,-2150+4*600, 244,244);
      //TEST - VIDEO
		//drawBTDMatrix(d2, 150,10, 244,244);
      drawBTDMatrix(d2, 10,10, 244,244);
		//drawBTDCorners(d2);
	}
}

void glutDisplay(void)
{
 	if(showSeeEdit) {
		//Always place at beginning of draw method
		sedraw();
	}
	
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
   glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
	Spaceball.Update();

   //oglDrawImage(.9f, .08f, colorBar, 20, 512, 1);

	// Lots of drawing going on here
	drawGraph(g);
   if(showVertLabels) {
		showVertLabels = !showVertLabels;
		for(int i=0;i<numSGs;i++) drawGraph(sg[i]);
		showVertLabels = !showVertLabels;
	} else for(int i=0;i<numSGs;i++) drawGraph(sg[i]);
	
	if(showText) {
		drawCurrentThresh();
		drawNumVerts();
		drawNumEdges();
		if( !showSeeEdit) drawVertHyp();
	}

	if (calcFrameRate && showText)
		calculateFrameRate();

	//Take complete rotation animation
	if (degreeStep>(1e-6)) {
		//printf("%f: %f %f %f\n",degreeRot,-219.f*sin(0.0174532925*degreeRot),0.f,219.f*cos(0.0174532925*degreeRot));
		//fprintf(cameraPositions,"%.0f\t%f %f %f\t%f %f %f\t%f %f %f\n",
		//	50.f,50.f,50.f,-219.f*sin(0.0174532925*degreeRot)+50.f,50.f,219.f*cos(0.0174532925*degreeRot)+50.f,0.f,1.f,0.f);
      //glutKeyboard('s', 0,0);  //Take pictures and save data to a file
		degreeRot+=degreeStep;
	}
	if (degreeRot>360.f) degreeStep=degreeRot=0.f;

	if(isScripting) {
		if(scriptWaitTime * CLOCKS_PER_SEC < ((clock() - ScriptStartTime))) {
			seeGraphScript();
			ScriptStartTime = clock();
		}
	}
   
   glFlush();
   glutSwapBuffers();

   if(saveAndExit) {
      static int letDraw = 0;
      if(letDraw==2) {
         if(pixFile==NULL) {
            if( (pixFile=(char*)malloc( (strlen(g->name)+5) *sizeof(char))) == NULL) {
				   perror("glutDisplay(): pixFile"); exit(-1);
			   }
		   }
         strcpy(pixFile,g->name);
         strcpy(&(pixFile[strlen(pixFile)-4]),".bmp");
         takeScreenshot(pixFile);
         exit(1);
      }
      letDraw++;
    }
}

//------------------------------------------------------------------------
//   It's the main application function. Note the clean code you can
//   obtain using he GLUT library. No calls to dark windows API
//   functions with many obscure parameters list. =)
//------------------------------------------------------------------------
int main(int argc, char** argv)
{
  
   //Glut's initialization code. Set window's size and type of display.
   //The window is positioned at the top leftmost area of the screen.
   glutInitDisplayMode( GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA | GLUT_MULTISAMPLE );
   glutInitWindowPosition( GL_WIN_INITIAL_X, GL_WIN_INITIAL_Y );
   glutInitWindowSize( GL_WIN_WIDTH, GL_WIN_HEIGHT );
   glutInit( &argc, argv );

   mainWindow = glutCreateWindow("UTK Seelab - SeeGraph <www.cs.utk.edu/~seelab>");

   //The functions below are called when the respective event
   //is triggered. Very simple, isn't it?
   glutReshapeFunc(glutResize);    // called every time  the screen is resized
   glutDisplayFunc(glutDisplay);   // called when window needs to be redisplayed
   glutIdleFunc(glutDisplay);      // called whenever the application is idle
   glutKeyboardFunc(glutKeyboard); // called when the application receives an input from the keyboard
   glutSpecialFunc(glutSpecial);   // called when a special key is pressed like SHIFT
   glutMouseFunc(mouseHandler);	   // called when the application receives an input from the mouse
   glutMotionFunc(trackMotion);	   // called when a motion is detected

   //Do any lighting, material, alpha, etc initialization or
   //configuration here.
   GLenum err = glewInit();
   if (GLEW_OK != err) {
		fprintf(stderr,"glew error, initialization failed\n");
		fprintf(stderr,"Glew Error: %s\n", glewGetErrorString(err));
	}
	 //vcbDebugLevelSet(VCB_DEBUG_ALL);//For data
	 //vcbDebugLevelSet(VCB_DEBUG_INFO);//For runtimes
	
   initData(argc,argv);
   initOGL();
   printf("OpenGL Error: %d\n",glGetError());
   printf("Cleaning up ...\n");
   cleanup();

   //Axis-layout experiments (uncomment blocks to run offline)
  /*int i;
  int* layout1 = NULL;
  layout1 = bestByGreedy(7, g);
  printf("  Best by greedy - ");
  for(i=0;i<7;i++) {
     printf("  %d,",layout1[i]);
  }
  printf(" = %f\n",getFitness(7,layout1,g)/DENOM_THRESH);
  
  layout1 = bestByPairs(7, g);
  printf("  Best by pairs - ");
  for(i=0;i<7;i++) {
     printf("  %d,",layout1[i]);
  }
  printf(" = %f\n",getFitness(7,layout1,g)/DENOM_THRESH);*/


  /*layout1 = bestByBruteForce(7,g);
  printf("  Global Optimum - ");
  for(i=0;i<7;i++) {
     printf("  %d,",layout1[i]);
  }
  printf(" = %f\n",getFitness(7,layout1,g)/DENOM_THRESH);*/

  /*int layout[15][7]=
    {{34, 0, 25, 1, 43, 6, 12}, {49, 34, 40, 50, 24, 41, 51}, {61, 59, 0, 1, 43, 44, 14},
         {58, 56, 35, 36, 11, 43, 59}, {3, 49, 4, 34, 2, 48, 6},
     {26, 91, 87, 25, 86, 1, 43}, {24, 101, 50, 111, 102, 40, 34}, {72, 0, 1, 43, 120, 104, 59},
         {59, 104, 120, 43, 72, 11, 36}, {3, 49, 4, 110, 6, 50, 12},
     {635, 1, 330, 396, 269, 208, 147}, {416, 477, 599, 474, 535, 413, 538}, {0, 1, 43, 120, 165, 181, 226},
         {429, 242, 490, 181, 714, 120, 104}, {537, 291, 17, 334, 416, 477, 1}};
   for(i=0;i<15;i++) {
      printf("%d - %f\n",i,getFitness(7,layout[i],g)/DENOM_THRESH);
   }*/

  /*int layout2[10][7]=
    {{1382, 1, 320, 5276, 1736, 3152, 6692},
     {458, 1166, 2582, 1874, 3998, 2523, 4647},
     {0, 1, 41, 883, 100, 116, 159},
     {5430, 1296, 6846, 4482, 4722, 3066, 2598},
     {2207, 6125, 2216, 6833, 461, 6774, 3005},
{3007, 6236, 3051, 4112, 0, 320, 3034},
{2523, 3998, 458, 1166, 1874, 2582, 1225},
{234, 454, 116, 159, 3656, 3699, 0},
{5190, 5430, 1296, 6846, 4482, 4722, 2598},
{1606, 3514, 461, 6833, 2207, 6125, 3012}};
printf("\n");
  for(i=0;i<10;i++) {
     printf("%d - %f\n",i,getFitness(7,layout2[i],g)/DENOM_THRESH);
  }*/

   
   //Creates a menu attached to the mouse's right button
   //the menu here consists in two options, one to toggle
   //fullscreen mode and another for exiting the application.
   int coloring = glutCreateMenu(glutMenu);
   glutAddMenuEntry("Hot/Cold",3);
   glutAddMenuEntry("Full Color",4);
   glutCreateMenu(glutMenu);
   glutAddMenuEntry("Full Screen", 1);
   glutAddSubMenu("Color By",coloring);
   glutAddMenuEntry("Exit", 2);
   glutAttachMenu(GLUT_RIGHT_BUTTON);

   //Application's main loop. All the above functions
   //are called when the respective events are triggered 
   printf("Initializing transfer function...\n");
   // Default to no splatting
   enableSplatting();
   vcbVolrInitS(voxtransf);
   disableSplatting();

   printf("Rendering...\n");
   glutMainLoop();

   return 0;
}
