//CIRL GPU GLSL Geometry Shader Code
//GLEW version is 1.3.5
//I also used NVemulate!

#include <stdio.h>			//C standard IO
#include <stdlib.h>			//C standard lib
#include <string.h>			//C string lib
#include <vector>

#include <iostream>
#include <fstream>
#include <sstream>
#include <ostream>
#include <streambuf>
#include <istream>

#include "glew.h"		//GLEW lib
#include <GL/glut.h>			//GLUT lib

#include "helperGL.h"
#include "vector.h"
#include "loadObject.h"
#include "util.h"
#include "TileClass.h"
#include "GLSL_GPU\physics.h"
using namespace std;

#define MAX_PARTICLES 100

float xdist = 0;
float ydist = 0;
float zdist = 0;
float camAngle = 0;
float xrot = 0;
float chosenVel = 0;
vertex_t chosenDir;
float angleY = 0;
float angleZ = 0;
float angleX = 0;
float hitAngle = 0;
float transZ, transX, transY, origTransX, origTransZ, origTransY;
float lightX = -1.0, lightY = -3.0, lightZ = 3;
int cameraSetting = 1;
int texNum = 1;
int rotateX = 0, rotateY = 0;
int origRotateX = 0, origRotateY = 0;
int gWidth, gHeight;
double zNear = .01;
double zFar = 100;

		int closestEdge;
		float closestTime = 100000;
vector<vector<vertex_t>> Edges;

vertex_t ballPos;
int score = 0;
int courseNum = 0;
int holeNum = 0;
physics myPhysics;
int time;
GLuint DLid;
vertex_t edgeNorm;
bool canHit = true;

GLuint createDL(void);
bool is_spinningX = false;
bool is_spinningZ = false;
bool is_spinningY = false;

string courseName;

vertex_t ballMov;
const float MAX_VEL = 0.10;
GLfloat mat_shininess[] = {25.0};
GLfloat light_position[] = {5.0, 5.0, 5.0, 1.0};

GLuint v,f,f2,p,g;			//Handlers for our vertex, geometry, and fragment shaders
int gw,gh;				//Keep track of window width and height

//Particle particleList[MAX_PARTICLES];

vector<vertex_t> vertices;
vector<vertex_t> normals;
vector<GLushort> elements;


int currentCourse = 0;
string realCourseName;

struct tee{
	vertex_t pos;
	int teeId;
};
struct cup{
	vertex_t pos;
	int cupId;
};
vector<tee> theTees;
vector<cup> theCups;

//struct for a course so it knows its tiles, names etc.
struct course{
	int par;
	string name;
	vector<TileClass> theTiles;
	tee theTee;
	cup theHole;
};


vector<course> readInCourses;
//a struct for the ball so it can keep track of its own data
struct ball
{
	float size;
	int amountHit;
	vertex_t pos;
	vertex_t dir;
	float vel;
	vertex_t xzDir;
	int currentTile;
	int lastTile;
	vertex_t rollDir;
	vertex_t orig;
	void ApplyFriction(float fric)
	{
		if(vel > 0)
		vel = vel- fric;
		if(vel < 0)
			vel = 0;
	}
	//initial setup for the ball so it starts at correct poitn
	void InitialSetup()
	{
		lastTile = -1;
		vel = 0;
		pos = readInCourses[currentCourse].theTee.pos;
		orig = readInCourses[currentCourse].theTee.pos;
		size = 0.1f;
		amountHit = 0;
	}
	//hits the ball and sets some basic data
	void HitBall(float chosenVelocity, vertex_t direction)
	{
		vel = chosenVelocity;
		normalize(&direction);
		dir = direction;
		xzDir = dir;
		amountHit++;
	}
};

ball theBall;

void CreateProfile(string profileName)
{
	ofstream output;
	output.open(profileName.c_str());
	output.close();
};

void DeleteProfile(string profileName)
{
	if(remove(profileName.c_str()) == 0)
		cout << "Succesfully deleted profile: "<< profileName << endl;
	else
		cout << "Error.  Profile doesn't exist: " << profileName << endl;
};
void SaveProfile()
{
	string profileName;
	cout << "Please enter the name of your profile: ";
	cin >> profileName;
	DeleteProfile(profileName);
	cout << "Creating profile... " << endl;
	ofstream output;
	output.open(profileName.c_str());
	output << "course: " << realCourseName << endl;
	output << "hole: " << currentCourse << endl;
	output << "ball: " << theBall.pos.x << " " << theBall.pos.y << " " << theBall.pos.z << " " << theBall.currentTile << " " <<
		theBall.lastTile << endl;
	output << "score: " << score << endl;
	output.close();
	cout << "Profile Created." << endl;
};




//looks through a given line and adds data ontot he pointer it is given as the need arises
void readInHole(char line[128], std::vector<vertex_t> &allPosLoc, course *theCourse, char* token2)
{
	//vector<TileClass> theTiles = theCourse->theTiles;
	//char line[128]; //this is a line that will hold up to 128 chars
	char * token;   //this is the token variable which will hold each segment of the line string, seperated by spaces
	int tempId; //this'll be the point that holds the id for each tile as it goes through loop
	std::vector<int> tempNeighbors; //this will hold the neighbors for each tile as it goes through loop

	//this grabs the next line, until end of file is encountered
	//while (fgets (line, 128, f) != NULL)
	//{
		tempNeighbors.clear(); //ensures that this is cleared from previous uses
		allPosLoc.clear();		//ensures that this is cleared from previous uses
	//	token = strtok(line, " "); //this will grab the next segment of line, seperated by spaces
		if(strcmp("tile", token2) == 0) //checks if the first token found is equal to tile
		{
			token = strtok(NULL, " " ); //grabs a new token, should be the tile ID
			if(token != NULL) //makes sure it isn't null
			{
				tempId = atoi(token); //converst the token to an int(there is no errorchecking here)
				token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
				int amountOfPoints = atoi(token); //converts it to a int
				if(amountOfPoints >= 3)
				{
				//loops through the amount of sides
				for(int i = 0; i < amountOfPoints; i++)
				{
					//this will hold the x, y z, position of one point of tile
					vertex_t tmp;
					//will grab token, make sure it isn't null then convert it to a float, and assign to x, y or z
					token = strtok(NULL, " " );
					if(token != NULL)
						tmp.x = atof(token);
					else break; //if it breaks then exits from loop without adding position to allPosLoc
					
					//will grab token, make sure it isn't null then convert it to a float, and assign to x, y or z
					token = strtok(NULL, " " );
					if(token != NULL)
						tmp.y = atof(token);
					else break; //if it breaks then exits from loop without adding position to allPosLoc
					
					//will grab token, make sure it isn't null then convert it to a float, and assign to x, y or z
					token = strtok(NULL, " " );
					if(token != NULL)
					{
						tmp.z = atof(token);
						allPosLoc.push_back(tmp); //pushes the new vector3 position onto allPosLoc, to be added to tile later
					}
					else break; //if it breaks then exits from loop without adding position to allPosLoc
				}
				//makes sure the token isn't null yet
				if(token != NULL)
				{
					//once again loops through the amount of sides that tile should have
					for(int i = 0; i < amountOfPoints; i++)
					{
						int tempNeighborNumber;
						token = strtok(NULL, " ");
						//makes sure the token isn't null
						if(token != NULL)
						{
							tempNeighborNumber = atoi(token);	//converst token to int, no error checking
							tempNeighbors.push_back(tempNeighborNumber-1); //adds the neighbor number to tempNeighbors
						}
						else//if its null then it assumes that the rest of the neighbors are 0
						{
							tempNeighborNumber = -1; //make neighbor 0(none)
							tempNeighbors.push_back(tempNeighborNumber);//add neighbor to tempNeighbors
						}
					}
						
					TileClass tempTile;
					tempTile.loadTiles(allPosLoc, tempId-1, tempNeighbors);//create tile, using allPosLoc, for all points, tempId for tile Id and tempNeighbors for neighbors
						
					theCourse->theTiles.push_back(tempTile); //add this tile to the vector filled with tiles in this function
				}
				else //if its null, it stops, doesn't create tile and sends out this error message
					printf("%s\n", "ERROR: INSUFFICIENT DATA TO MAKE A TILE");
			}
			else//if its null, then not nearly enough information, so it skips to the next line and sends error
			{
				printf("%s\n", "Insufficient information.  Skipping this line.");
			}
			}
			else //if it has fewer then 3 points it can't be a tile
				printf("%s\n", "The tile has too few points.  Moving to next line.");
		}
		else if(strcmp("tee", token2) == 0)//if its equal to tee then begins creating tee information
		{

			token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
			if(token != NULL){
				int id = atoi(token);
				tee tempTee;
				tempTee.teeId = id-1;
				theBall.currentTile = id-1;
				//myPhysics.SetTile(id-1);
				token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
				if(token != NULL)
				{
					float pos = atof(token);
					tempTee.pos.x = pos;

					token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
					if(token != NULL)
					{
						pos = atof(token);
						tempTee.pos.y = pos;

						token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
						if(token != NULL)
						{
							pos = atof(token);
							tempTee.pos.z = pos;
							theBall.pos = tempTee.pos;
							theBall.orig = tempTee.pos;
							//theTees.push_back(tempTee);
							theCourse->theTee = tempTee;
						}
						cout << "Tee line ended too early. Skipping.";
					}
					cout << "Tee line ended too early. Skipping.";
				}
				cout << "Tee line ended too early. Skipping.";
			}
		}
		else if(strcmp("cup", token2) == 0)//if its cup then begins creating cup information
		{				
			token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
			if(token != NULL){
				int id = atoi(token);
				cup tempTee;
				tempTee.cupId = id-1;
				token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
				if(token != NULL)
				{
					float pos = atof(token);
					tempTee.pos.x = pos;

					token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
					if(token != NULL)
					{
						pos = atof(token);
						tempTee.pos.y = pos;

						token = strtok(NULL, " " ); //moves on to next token, which should be the amount of sides tile has
						if(token != NULL)
						{
							pos = atof(token);
							tempTee.pos.z = pos;
							//theCups.push_back(tempTee);
							theCourse->theHole = tempTee;
						}
						cout << "Cup line ended too early. Skipping.";
					}
					cout << "Cup line ended too early. Skipping.";
				}
				cout << "Cup line ended too early. Skipping.";
			}
		}
		else//if its none of the above, it lets the user know that the this line didn't do anything
		{
			printf("INCORRECT LINE BEGINNING:  Should be tile, tee, or cup");
		}
	//}
//	printf("%s\n", "Finished parsing file."); //sends thsi out so we know file is finished
}

//reads in a file and then gets the data we need from it
std::vector<course> ReadDB(const char* filename)
{
	char line[128];
	realCourseName = filename;
	//int i, j;
	//float x, y, z;
	FILE *f;
	bool hole = false;
	char* token;
	f = fopen (filename, "r");
	course aCourse;
	int amountOfCourses = 0;
	std::vector<vertex_t> allPosLoc; //this holds vector3 positions of each tile as it goes through each loop
	std::vector<course> theCourses;//this will hold the tiles that are created from this function
	//vector<vertex_t> allPosLoc;
	//makes sure that the file did in fact open, if not send error
	if (!f)
	{
		std::cout << "Cannot open the file " << filename << std::endl;
		//exit(-1);
		//return;
	}
	else
	{
		if (fgets (line, 128, f) != NULL)
		{
			int i = 0;
			token = strtok(line, " ");
			//while(token != NULL)
			//for (token = strtok(line, " "); token; token = strtok(NULL, " "))
			//{
			//int blah =  strcmp(token, "course");
				if (0 != strcmp(token, "course"))//token != "course")// && i == 0)
				{
					std::cout << "First line does not contain 'course'" << std::endl;
					exit(-1);
				}
				token = strtok(NULL, " \"");
				if (token != NULL)
				{
					courseName = token;
				}
				token = strtok(NULL, " ");
				 if (token != NULL)
				{
						amountOfCourses = atoi(token);
				}
				//i++;
			//}
		}
		for (int i = 0; i < amountOfCourses; i++)//theCourses.size(); i++)
		{
			while (fgets (line, 128, f) != NULL)
			{
				//char line2[128];
				//line2 = line;
				token = strtok(line, " ");
				if(token != NULL && strcmp(token, "begin_hole\n") == 0)//token == "begin_hole")
					hole = true;
				if(token != NULL && strcmp(token, "end_hole\n") == 0)//token == "end_hole")
				{
					hole = false;
					theCourses.push_back(aCourse);
					aCourse.theTiles.clear();
					break;
				}
				if(hole && strcmp(token, "begin_hole\n") != 0)
				{
					if(strcmp(token, "par") == 0)//token == "PAR")
					{
						token = strtok(NULL, " " );
						aCourse.par = atoi(token);
					}
					else if(strcmp(token, "name") == 0)//token == "name")
					{
						token = strtok(NULL, "\"" );
						//char* tempName = token;
						aCourse.name = token;//tempName;
					}
					else
						readInHole(line, allPosLoc, &aCourse, token);
				}
			}
		}
		fclose(f); //closes file once we're done with it
	}
	return theCourses;
}
// we need to pass it as a string to the GLSL driver
	//draws the course
void drawObj() {
	glUseProgram(0);

	//draw outline
	glDisable(GL_LIGHTING);
	glCullFace(GL_BACK);
	//glColor4f(0.0f, 0.0f, 0.0f, 0.0f);
	//glPolygonMode(GL_BACK, GL_LINE);
	//glLineWidth(5);
	//used to draw the triangles that make up our object
 //   for(int i = 0; i < elements.size(); i+=3){
 //       glBegin(GL_TRIANGLES);
 //       glNormal3f(normals[elements[i]].x, normals[elements[i]].y, normals[elements[i]].z);
 //       glVertex3f(vertices[elements[i]].x, vertices[elements[i]].y, vertices[elements[i]].z);
 //       glNormal3f(normals[elements[i+1]].x, normals[elements[i+1]].y, normals[elements[i+1]].z);
 //       glVertex3f(vertices[elements[i+1]].x, vertices[elements[i+1]].y, vertices[elements[i+1]].z);
 //       glNormal3f(normals[elements[i+2]].x, normals[elements[i+2]].y, normals[elements[i+2]].z);
 //       glVertex3f(vertices[elements[i+2]].x, vertices[elements[i+2]].y, vertices[elements[i+2]].z);
 //       glEnd();
 //   }
	//glPolygonMode(GL_BACK, GL_FILL);
	glCullFace(GL_BACK);

	glEnable(GL_LIGHTING);
	glUseProgram(p);
	for(int i = 0; i < elements.size(); i+=3){
        glBegin(GL_TRIANGLES);
        glNormal3f(normals[elements[i]].x, normals[elements[i]].y, normals[elements[i]].z);
        glVertex3f(vertices[elements[i]].x, vertices[elements[i]].y, vertices[elements[i]].z);
        glNormal3f(normals[elements[i+1]].x, normals[elements[i+1]].y, normals[elements[i+1]].z);
        glVertex3f(vertices[elements[i+1]].x, vertices[elements[i+1]].y, vertices[elements[i+1]].z);
        glNormal3f(normals[elements[i+2]].x, normals[elements[i+2]].y, normals[elements[i+2]].z);
        glVertex3f(vertices[elements[i+2]].x, vertices[elements[i+2]].y, vertices[elements[i+2]].z);
        glEnd();
    }
}
GLuint createDLList() {
	GLuint dlList;
	dlList = glGenLists(1);
	glNewList(dlList, GL_COMPILE);
	drawObj();
	glEndList();
	return(dlList);
}
//this loads up a new course
//clears the normals, verts, and indices and set sa new set
//creates a new buffer for the object so ti can be updated correctly
//and resets the ball
void ReloadCourse()
{
	canHit = true;
	chosenVel = 0;

			elements.clear();
			vertices.clear();
			normals.clear();
			GLuint lastSize = 0;
			for (int k = 0; k <readInCourses[currentCourse].theTiles.size(); k++)
			{
				//for(int i = 0; i < readInCourses[currentCourse].theTiles.size(); i++)
				//{
					vector<GLushort> tempInd = readInCourses[currentCourse].theTiles[k].GetFaces();
					for(int j= 0; j < tempInd.size(); j++) //loads in all the faces of the current tile
					{
						elements.push_back(tempInd[j]+lastSize);
					}
					for(int j =0; j < readInCourses[currentCourse].theTiles[k].GetCoord().size(); j++) //adds all the verts
					{
						vertices.push_back(readInCourses[currentCourse].theTiles[k].GetCoord()[j]);
					}
					lastSize = lastSize + readInCourses[currentCourse].theTiles[k].GetCoord().size();//this is used so the next set of normals know how many higher they have to be
				//}
			}
				vertex_t empty;
				empty.x = 0;
				empty.y = 0;
				empty.z = 0;

				Edges = getNeighborTiles(readInCourses[currentCourse].theTiles);
				normals.resize(elements.size(), empty);
				//normals = elements;
				for (int i = 0; i < elements.size(); i+=3) {
					GLushort ia = elements[i];
					GLushort ib = elements[i+1];
					GLushort ic = elements[i+2];

					vertex_t n;
					normal(vertices[ia], vertices[ib], vertices[ic], &n);
					normals[i] = normals[i+1] = normals[i+2] =  n;
			
				}
	theBall.currentTile = readInCourses[currentCourse].theTee.teeId;
	DLid = createDLList();
};
void LoadProfile()
{
	string profileName;
	cout << "Please enter the name of your profile: ";
	cin >> profileName;
	const char* fileName = profileName.c_str();
	ofstream myfile;
	FILE* file;
	file = fopen (fileName, "r");
	
	if (!f)
	{
		cout << "Profile does not exist: " << fileName << endl;
		return;
	}
	char line[128];
	char* token;
	while (fgets (line, 128, file) != NULL)
	{
		token = strtok(line, " \n");
		if (0 == strcmp(token, "course:"))//token != "course")// && i == 0)
		{
			token = strtok(NULL, " \n" );
			cout << "-" << token << "-" << endl;
			readInCourses = ReadDB(token);
		}
		else if (0 == strcmp(token, "hole:"))
		{
			token = strtok(NULL, " \n" );
			currentCourse = atoi(token);
		}
		else if (0 == strcmp(token, "ball:"))
		{
			theBall.InitialSetup();
			token = strtok (NULL, " \n");
			theBall.pos.x = atof(token);
			token = strtok (NULL, " \n");
			theBall.pos.y = atof(token);
			token = strtok (NULL, " \n");
			theBall.pos.z = atof(token);
			token = strtok(NULL, " \n" );
			theBall.currentTile = atoi(token);
		}
		else if (0 == strcmp (token, "score:"))
		{
			token = strtok (NULL, " \n");
			score = atoi(token);
		}
	}
	ReloadCourse();
	cout << "loaded" << endl;
	
};

// Do spin
void spinY() {
	if (is_spinningY) {
		is_spinningY = false;
	//	angleY = 0.0;
	} else {
		is_spinningY = true;
	}
	angleY++;
}
	void spinZ() {
		if (is_spinningZ) {
		is_spinningZ = false;
		//angleZ = 0.0;
	} else {
		is_spinningZ = true;
	}
		angleZ++;
	}
	void spinX() {
	if (is_spinningX) {
		is_spinningX = false;
		//angleX = 0.0;
	} else {
		is_spinningX = true;
	}
	angleX++;
	}




//creates a newdlllist which allows for change in objects


char *textFileRead(char *fn) 
{
	FILE *fp;
	char *content = NULL;

	int count=0;

	if (fn != NULL) {
		
		fp = fopen(fn,"rt");

		if (fp != NULL) {
      
        		fseek(fp, 0, SEEK_END);
        		count = ftell(fp);
        		rewind(fp);

			
				content = (char *)malloc(sizeof(char) * (count+1));
				count = fread(content,sizeof(char),count,fp);
				content[count] = '\0';
			
			fclose(fp);
		
		}
	}

	return content;
}

//Function from: http://www.evl.uic.edu/aej/594/code/ogl.cpp
//Read in a textfile (GLSL program)
// we can use this to write to a text file
int textFileWrite(char *fn, char *s) 
{
	FILE *fp;
	int status = 0;

	if (fn != NULL) {
		fp = fopen(fn,"w");

		if (fp != NULL) {
			
			if (fwrite(s,sizeof(char),strlen(s),fp) == strlen(s))
				status = 1;
			fclose(fp);
		}
	}
	return(status);
}

//Got this from http://www.lighthouse3d.com/opengl/glsl/index.php?oglinfo
// it prints out shader info (debugging!)
void printShaderInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;
    glGetShaderiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetShaderInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("printShaderInfoLog: %s\n",infoLog);
        free(infoLog);
	}else{
		printf("Shader Info Log: OK\n");
	}
}

//Got this from http://www.lighthouse3d.com/opengl/glsl/index.php?oglinfo
// it prints out shader info (debugging!)
void printProgramInfoLog(GLuint obj)
{
    int infologLength = 0;
    int charsWritten  = 0;
    char *infoLog;
	glGetProgramiv(obj, GL_INFO_LOG_LENGTH,&infologLength);
    if (infologLength > 0)
    {
        infoLog = (char *)malloc(infologLength);
        glGetProgramInfoLog(obj, infologLength, &charsWritten, infoLog);
		printf("printProgramInfoLog: %s\n",infoLog);
        free(infoLog);
    }else{
		printf("Program Info Log: OK\n");
	}
}

//this update looks at the current tile the balls in and determines whether its about to hit a wall or go into a new tile
//it updates the direction if it goes up a sloped surface and updates vel and all other ball info.
void cb_update_ball(int value)
{
	vector<TileClass> tiles = readInCourses[currentCourse].theTiles;
	//for (int i = 0; i < theTees.size(); i++)
	//{	
		bool flat = false;
		//for(int i = 0; i < tiles.size(); i++)
		//{
		//float closeTime = 10000;
		TileClass currentTile;
		for(int k = 0; k < tiles.size(); k++)
		{
			if(theBall.currentTile == tiles[k].GetId())
				currentTile = tiles[k];
		}
		vector<vertex_t> neighEdge = currentTile.GetNormalEdges();//tiles[myPhysics.GetTile()].GetNormalEdges();
			for(int j = 0; j < neighEdge.size(); j++)
			{
				int f = currentTile.GetNeighbors()[j];
				if(currentTile.GetNeighbors()[j] >= 0) //this checks if its moving into a tile, by checking the "wall" normal of the tile
				{
					
					if (myPhysics.rayTilePlaneIntersection(neighEdge[j], /*.Gtiles[myPhysics.GetTile()]*/currentTile.GetEdges()[j][0],
						theBall.pos, theBall.dir)
						&& f != myPhysics.lastTile)
					{
						flat = true;
						theBall.lastTile = theBall.currentTile;
						//myPhysics.lastTile = myPhysics.Ge;
						for(int k = 0; k < tiles.size(); k++)
						{
							if(tiles[k].loopThroughAllTriangles(ballPos))
							{
								theBall.currentTile = tiles[k].GetId();
								//myPhysics.SetTile(tiles[k].GetId());
								//theTees[i].teeId = tiles[k].GetId();
							}
						}
						vertex_t aNormal = tiles[theBall.currentTile].GetNormal();
						if(aNormal.x != 0.0 || aNormal.y != 1.0 || aNormal.z != 0.0)
						{
							flat = false;
							vertex_t up = myPhysics.GetWorldUp();
							vertex_t physicsDirection = theBall.xzDir;//myPhysics.GetDirection();
							vertex_t crossX = CrossProduct(physicsDirection, up);
							vertex_t dir = CrossProduct(aNormal, crossX);
							normalize(&dir);
							theBall.dir = dir;
							//myPhysics.ChangeDirection(dir);
							//dir.y = 0;
							//myPhysics.ChangeXZDirection(dir);
						}
						//break;
					}
				//}
				}
				else //this is nwo a check to see if its hitting a wall
				{
					if (myPhysics.rayPlaneIntersection(neighEdge[j],/* tiles[myPhysics.GetTile()]*/currentTile.GetEdges()[j][0], 
						theBall.pos, theBall.dir))
					{
						//myPhysics.SetTile(tiles[myPhysics.GetTile()].GetNeighbors()[j]);
						myPhysics.HitWall(neighEdge[j], theBall.dir, &theBall.xzDir, &theBall.dir);
					//	break;
					}
				}
			}
		//}
			//checks to see if the ball just went onto a flat surface
		if(flat)
			theBall.dir = theBall.xzDir;
		//checks to see if the ball has hit the hole
		if (myPhysics.collisionDetection(readInCourses[currentCourse].theHole.pos, 0.1, theBall.pos, theBall.size)
			&& theBall.vel < 0.05)
		{
			//myPhysics.changeVelocity(0);
			score += theBall.amountHit - readInCourses[currentCourse].par;
			if(currentCourse < readInCourses.size() -1)
				currentCourse++;
			theBall.InitialSetup();
			ReloadCourse();
		}
		vertex_t hi = theBall.pos;
		myPhysics.ApplyTime(theBall.vel, theBall.dir, &theBall.pos);
		theBall.ApplyFriction(myPhysics.friction);
		ballPos = theBall.pos;// theTees[i].pos; 
		if(theBall.vel == 0) canHit = true;
	glutTimerFunc(20,cb_update_ball,0);
}
//this is display function which draws the current scene
void cb_display() 
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	//gluPerspective(45, 1, zNear, zFar); 
	glTranslatef(transX, transY, transZ);
	
	if (cameraSetting == 1)
	{
		glTranslatef(-ballMov.x, -ballMov.y, -ballMov.z);
	}
	glRotatef(rotateX, 1, 0, 0);
	glRotatef(rotateY, 0, 1, 0);
	camera();

	glPushMatrix(); 
	glTranslatef(lightX, lightY, lightZ);
	GLfloat light_position[] = {lightX, lightY, lightZ, 0.0};
	glColor3d(1.0, 1.0, 1.0);
	gluSphere(gluNewQuadric(), .5, 10, 10);




	glPopMatrix();

	glLightfv(GL_LIGHT0, GL_POSITION, light_position);


	// Rotate along y-axis
	//if (is_spinningY) {
		glRotatef(angleY, 0, 1, 0);
		//angleY++;
	//}
	//if (is_spinningX) {
		glRotatef(angleX, 1, 0, 0);
		//angleX++;
	//}
	//if (is_spinningZ) {
		glRotatef(angleZ, 0, 0, 1);
		//angleZ++;
	//}
	draw_axis(4.0);
	float size = 1;
	glCallList(DLid);
	//this draws the ball
	glPushMatrix();
	glTranslatef(ballPos.x, ballPos.y+0.1f, ballPos.z);
	gluSphere(gluNewQuadric(), .1f, 10, 10);
	//This checks if the player can hit the ball, which happens when the ball isn't moving
	//it then draws a line to show direction and power of the ball
	if(canHit)
	{
		chosenDir.x = 0 + chosenVel/MAX_VEL * cos((double)hitAngle * PI / 180.0);
		chosenDir.z = 0 + chosenVel/MAX_VEL * sin((double)hitAngle * PI / 180.0);
		chosenDir.y = 0;

		glBegin( 3.0 );
			glLineWidth(chosenVel/MAX_VEL * 10);
			glColor3f(1.0f, 0.0f, 0.0f);
			glVertex3f(0, 0, 0);
			glVertex3f(chosenDir.x, 0.1f, chosenDir.z);
		glEnd();
		normalize(&chosenDir);
	}
		glPopMatrix();

		//this draws the hole
		glPushMatrix();
		glTranslatef(readInCourses[currentCourse].theHole.pos.x, readInCourses[currentCourse].theHole.pos.y+0.001f, readInCourses[currentCourse].theHole.pos.z);
		glRotatef(90.0f, 1, 0, 0);
		gluDisk(gluNewQuadric(), 0.0f, 0.2f, 15, 15);
		glPopMatrix();

				
						
	//this allows for the HUD		
				glPushMatrix();				
	glLoadIdentity();
	gluLookAt(0, 0, 6, 0, 0, 0, 0, 1, 0);
	char buf1[30];
	string temp1 = "SCORE: ";
	itoa(score, buf1, 10);
	temp1.append(buf1);
	char buf2[30];
	itoa(currentCourse+1, buf2, 10);
	string temp2 = "COURSE: ";
	temp2.append(buf2);
	char buf3[30];
	itoa(readInCourses[currentCourse].par, buf3, 10);
	string temp3 = "PAR: ";
	temp3.append(buf3);
	char* txt[3] = {(char*)temp1.c_str(),(char*)temp2.c_str(),  (char*)readInCourses[currentCourse].name.c_str()};
	// Vertex positions  

	draw_string(-5, 5.5, 0, txt[0]);
	draw_string(4, 5.5, 0, txt[1]);
	draw_string(3.2, 5, 0, txt[2]);
	draw_string(0, 5.5,0, (char*)temp3.c_str());
	glPopMatrix();



	glutSwapBuffers();
}

void pause()
{
	is_spinningX = false;
	is_spinningY = false;
	is_spinningZ = false;
}
void cb_reshape(int w, int h) 
{
	//Able to resize window without scaling distortion on animal.
	glMatrixMode(GL_PROJECTION);
	glViewport(0, 0, 1000, 1000); 
	glLoadIdentity();
	glFrustum(-1.0, 1.0, -1.0, 1.0, 1.0, 100.0);
	glMatrixMode(GL_MODELVIEW);
}

//GLUT callback fx
// this is for processing key commands (setup a exit key)
void cb_keyboard(unsigned char key, int x, int y) {
	// Add controls to animation, camera controls, etc
	// Some camera functions are provided for model viewing convinience.
	switch(key) {
		case '[':
			printf("%lf\n", zNear);
			if (zNear < zFar) zNear = zNear + .1;
			printf("zNear: %f\n", zNear);
			cb_reshape(gWidth, gHeight);
			break;
		case ']':
			printf("%lf\n", zNear);
			if (zNear > .1) zNear = zNear - .1;
			printf("zNear: %f\n", zNear);
			cb_reshape(gWidth, gHeight);
			break;
		case '{':
			printf("%lf\n", zFar);
			printf("zFar: %f\n", zFar);
			zFar = zFar + .1;
			cb_reshape(gWidth, gHeight);
			break;
		case '}':
			printf("%lf\n", zFar);
			if(zFar > zNear) zFar = zFar - .1;
			printf("zFar: %f\n", zFar);
			cb_reshape(gWidth, gHeight);
			break;
		case 'q':
			transX = transX + .1;
			printf("transX: %f\n", transX);
			break;
		case 'a':
			transX = transX - .1;
			printf("transX: %f\n", transX);
			break;
		case 'e':
			transZ = transZ + .1;
			printf("transZ: %f\n", transZ);
			break;
		case 'd':
			transZ = transZ - .1;
			printf("transZ: %f\n", transZ);
			break;
		case 'w':
			transY = transY + .1;
			printf("transY: %f\n", transY);
			break;
		case 's':
			transY = transY - .1;
			printf("transY: %f\n", transY);
			break;
		case 'u':
			lightX = lightX + .05;
			printf("lightX: %f\n", lightX);
			break;
		case 'j':
			lightX = lightX - .05;
			printf("lightX: %f\n", lightX);
			break;
		case 'i':
			lightY = lightY + .05;
			printf("lightY: %f\n", lightY);
			break;
		case 'k':
			lightY = lightY - .05;
			printf("lightY: %f\n", lightY);
			break;
		case 'o':
			lightZ = lightZ + .05;
			printf("lightZ: %f\n", lightZ);
			break;
		case 'l':
			lightZ = lightZ - .05;
			printf("lightZ: %f\n", lightZ);
			break;
		case '3':
			set_cam(DEFAULT);
			cameraSetting = 3;
			break;
		case '2':
			set_cam(TOP);
			cameraSetting = 2;
			break;
		case '1':
			set_cam(ANGLE);
			cameraSetting = 1;
			break;
		case '4':
			if (rotateY == 360) rotateY = 0;
			if (cameraSetting == 3) rotateY = rotateY - 1;
			else
			{
					rotateY = origRotateY;
					rotateX = origRotateX;
			}
			printf("rotateY: %f\n", rotateY);
			break;
		case '5':
			if (rotateY == 0) rotateY = 360;
			if (cameraSetting == 3) rotateY = rotateY + 1;
			else
			{
					rotateY = origRotateY;
					rotateX = origRotateX;
			}
			printf("rotateY: %f\n", rotateY);
			break;
		case '6':
			if (rotateX == 360) rotateX = 0;
			if (cameraSetting == 3) rotateX = rotateX - 1;
			else
			{
					rotateY = origRotateY;
					rotateX = origRotateX;
			}
			printf("rotateX: %f\n", rotateX);
			break;
		case '7':
			if (rotateX == 0) rotateX = 360;
			if (cameraSetting == 3) rotateX = rotateX + 1;
			else
			{
					rotateY = origRotateY;
					rotateX = origRotateX;
			}
			printf("rotateX: %f\n", rotateX);
			break;
		case 'z':
			spinZ();
			break;
		case 'c':
			spinY();
			break;
		case 'p':
			pause();
			break;
		case 'v':
			spinX();
			break;

		case 'x':
			exit(0);
			break;
		case ' ':
			if(canHit)
			{
				theBall.HitBall(chosenVel, chosenDir);
				canHit = false;
			}
			break;
		case '~':
			SaveProfile();
			break;
		case '`':
			LoadProfile();
			break;
	}
}

void SpecialInput(int key, int x, int y)
{
	switch(key)
	{
	case GLUT_KEY_UP:
	 if(chosenVel < MAX_VEL)
		 chosenVel += 0.002;
		break;
	case GLUT_KEY_DOWN:
		if(chosenVel >= 0)
		 chosenVel -= 0.002;
		if(chosenVel < 0)
			chosenVel = 0;
		break;
	case GLUT_KEY_LEFT:
		hitAngle -= 3;
		if (hitAngle < 0)
			hitAngle = 360;
	break;
	case GLUT_KEY_RIGHT:
		hitAngle += 3;
		if (hitAngle > 360)
			hitAngle = 0;
	break;
	}
}
//Setup shaders
void setShaders() 
{

	ifstream inVS;
	ifstream inFS;

	std::stringstream vsData;
	std::stringstream fsData;

	inVS.open ("shader.vert", ifstream::in);
	inFS.open ("shader.frag", ifstream::in);

	vsData << inVS.rdbuf(); // loads the entire string into a string stream
	fsData << inFS.rdbuf();
		
	string &vString = vsData.str();
	string &fString = fsData.str();

	//a few strings
	// will hold onto the file read in!
	char *vs = NULL, *fs = NULL;//, *fs2 = NULL, *gs = NULL;

	//First, create our shaders 
	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);

	//Read in the programs
	const char* vVar = vString.c_str();
	const char* fVar = fString.c_str();

	GLint vShaderLen = vString.size();
	GLint fShaderLen = fString.size();

	vs = textFileRead("GLSL/shader.vert");
	fs = textFileRead("GLSL/shader.frag");

	glShaderSource(v, 1, (const GLchar**) &vVar, NULL);
	glShaderSource(f, 1, (const GLchar**) &fVar, NULL);

	free(fs);

	glCompileShader(v);
	glCompileShader(f);

	p = glCreateProgram();

	glAttachShader(p,f);
	glAttachShader(p,v);

	glProgramParameteriEXT(p,GL_GEOMETRY_INPUT_TYPE_EXT,GL_TRIANGLES);

	int temp;

	glBindFragDataLocation(p, 0, "FragColor");

	glLinkProgram(p);
	glUseProgram(p);

	printShaderInfoLog(v);
	printShaderInfoLog(f);
	printProgramInfoLog(p);
}

void printHelp() {
    std::cout << "--------------------\nHow to use the program:\n\nControls:\n"
		"Note: control is all done by keyboard\n"
		"\nTo change the Mesh:\n"
		"c: Turns the display list on and off\n"
		"v: Changes the mesh normals from vertex to faces and back\n"
		"\nTo move the Camera around use:\n"
		"0: front view\n"
		"3: side view\n"
		"7: back view\n"
		"1: top view\n"
		"q: move in the X coordinate by -.1, a: move in the X coordinate by .1\n"
		"w: move in the y coordinate by -.1, s: move in the y coordinate by .1\n"
		"e: move in the z coordinate by -.1, d: move in the y coordinate by .1\n"
		"z: spins the camera around z axis\n"
		"c: spins the camera around y axis\n"
		"v: spins the camera around x axis\n"
		"[: moves zNear farther\n"
		"]: moves zNear closer\n\n"
		"{: moves zFar farther\n"
		"}: moves zFar closer\n\n"
		"Light Source Movement:\n"
		"u: move in the X coordinate by -.05, j: move in the X coordinate by .05\n"
		"i: move in the y coordinate by -.05, k: move in the y coordinate by .05\n"
		"o: move in the z coordinate by -.05, l: move in the y coordinate by .05\n"
		"--------------------\n";
}

void cb_idle() 
{
	glutPostRedisplay();
}
	
int main(int argc, char **argv) 
{

	glutInit(&argc, argv);

	// code to check if the command prompt is correct
	if (argc != 2) {
		printf("Please specify a .obj file on line\nPress Enter twice to exit\n");
		std::cin.ignore(1000,'\n');
		std::cin.get();
		return -1;
	}

	for (int a = 1; a < argc; a++) {
		std::string check = argv[a];
		if (check.size() < 4) {
			printf("Failed to open file: %s\nPress Enter twice to exit\n", argv[1]);
			std::cin.ignore(1000,'\n');
			std::cin.get();
			return -1;
		} else if (check[check.size()-1] != 'b' || check[check.size()-2] != 'd' ||
				   check[check.size()-3] != '.') {
			printf("Failed to open file, please insert a file ending with .obj : \nPress Enter twice to exit\n");
			std::cin.ignore(1000,'\n');
			std::cin.get();
			return -1;
		}
		if (a == 1) {
			printf("Loaded file: %s\n", argv[a]);
			//loadObj(argv[a], vertices, normals, elements);
			readInCourses = ReadDB(argv[a]);
			elements;
			GLuint lastSize = 0;
			for (int k = 0; k <readInCourses[currentCourse].theTiles.size(); k++)
			{
				//for(int i = 0; i < readInCourses[currentCourse].theTiles.size(); i++)
				//{
					vector<GLushort> tempInd = readInCourses[currentCourse].theTiles[k].GetFaces();
					for(int j= 0; j < tempInd.size(); j++)
					{
						elements.push_back(tempInd[j]+lastSize);
					}
					for(int j =0; j < readInCourses[currentCourse].theTiles[k].GetCoord().size(); j++)
					{
						vertices.push_back(readInCourses[currentCourse].theTiles[k].GetCoord()[j]);
					}
					lastSize = lastSize + readInCourses[currentCourse].theTiles[k].GetCoord().size();//GetFaces().size();//GetCoord().size();
				//}
			}
				vertex_t empty;
				empty.x = 0;
				empty.y = 0;
				empty.z = 0;

				Edges = getNeighborTiles(readInCourses[currentCourse].theTiles);
			
				normals.resize(elements.size(), empty);
				//normals = elements;
				for (int i = 0; i < elements.size(); i+=3) {
					GLushort ia = elements[i];
					GLushort ib = elements[i+1];
					GLushort ic = elements[i+2];

					vertex_t n;
					normal(vertices[ia], vertices[ib], vertices[ic], &n);
					normals[i] = normals[i+1] = normals[i+2] =  n;
			
				}
			//}
		}
	}
	
	theBall.currentTile = readInCourses[currentCourse].theTee.teeId;
	theBall.InitialSetup();
//	myPhysics.SetTile(theTees[0].teeId);

	printHelp();

	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(1000, 1000);
	glutCreateWindow("Cel-Shading");

	glShadeModel(GL_SMOOTH);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_NORMALIZE);
	glEnable(GL_TEXTURE_2D);
	//glEnable(GL_CULL_FACE);
	//glCullFace(GL_BACK);
	glLineWidth(1);

	glLightfv(GL_LIGHT0, GL_POSITION, light_position);
	
	glutDisplayFunc(cb_display);
	glutReshapeFunc(cb_reshape);
	glutIdleFunc(cb_idle);
	glutKeyboardFunc(cb_keyboard);
	glutSpecialFunc(SpecialInput);
	glutTimerFunc(20,cb_update_ball,0);


	glClearColor(1,1,1,0);

	glewInit();
	if (glewIsSupported("GL_VERSION_2_1"))
		printf("Ready for OpenGL 2.1\n");
	else {
		printf("OpenGL 2.1 not supported\n");
		exit(1);
	}
	if (GLEW_ARB_vertex_shader && GLEW_ARB_fragment_shader && GL_EXT_geometry_shader4)
		printf("Ready for GLSL - vertex, fragment, and geometry units\n");
	else {
		printf("Not totally ready :( \n");
		exit(1);
	}

	setShaders();

	DLid = createDLList();

	glutMainLoop();

	// just for compatibiliy purposes
	return 0;

}

