#include <stdlib.h>
#include <stdio.h>
#include "app_window.h"
#include "texture.h"
#include "bicycle.h"
#include "math.h"
# include <gsim/gs.h>
# include <gsim/gs_color.h>
# include <gsim/gs_array.h>
# include <gsim/gs_light.h>
# include <gsim/gs_vec.h>
#include <freeglut.h>

App* app;
const double pi = 3.14159265;
double camera = 0; 
//variable to time when to redraw
double redrawTime;
//grass texture holder
GLuint grass;
//bicycle object
Bicycle *bicycle;
//variables for tree rendering
int numTrees = 10000;
Vect trees[10000];

void appKeyboardFunc(unsigned char key, int x, int y) {
	//if the space key is pressed the camera is switched from locked to free and vice versa
	if (key == ' ') {
		if (camera == 0)
			camera = 1;
		else
			camera = 0;
	}
	//if the escape key is pressed the program will exit
	if (key == 27) {
		exit(1);
	}
}

void appKeyboardFunc(int key, int x, int y) {
	//if we are in free camera mode
	if (camera == 0) {
		switch (key) {
		//the arrow keys here will allow the user to rotate the camera around the object
		//while they are remaining in free camera mode
		case GLUT_KEY_LEFT:      
			app->roty -= 5;
			break;
		case GLUT_KEY_RIGHT:     
			app->roty += 5;
			break;
		case GLUT_KEY_UP:        
			app->rotx += 5;
			break;
		case GLUT_KEY_DOWN:      
			app->rotx -= 5;
			break;
		case GLUT_KEY_PAGE_UP:
			app->fovy += 10;
			break;
		case GLUT_KEY_PAGE_DOWN:
			app->fovy -= 10;
			break;
		}
	}
	//if we are in locked camera mode
	else {
		//this will turn the bike left
		if (key == GLUT_KEY_LEFT) {
			if (bicycle->turnAngle<60)
				bicycle->turnAngle += 5;
		}
		//this will turn the bike right
		else if (key == GLUT_KEY_RIGHT) {
			if (bicycle->turnAngle>-60)
				bicycle->turnAngle -= 5;
		}
		//this will increase the speed of the bike
		if (key == GLUT_KEY_UP) {
			bicycle->speed += 1;
		}
		//this will decrement the speed of the bike
		else if (key == GLUT_KEY_DOWN) {
			bicycle->speed -= 1;
		}
	}
}

void appResizeWindow(int w, int h) {
	glViewport(0, 0, w, h);
}

void appDrawScene() {	
	glClearColor(0, 0.5, 1, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	int width = glutGet(GLUT_WINDOW_WIDTH);
	int height = glutGet(GLUT_WINDOW_HEIGHT);
	double aspect = double(width)/double(height);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(app->fovy, aspect, 2, 600);
	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	//this is for the free camera mode
	if (camera == 0) {
		gluLookAt(bicycle->getX() - 50*cos(app->rotx*pi / 180)*sin(app->roty*pi / 180), bicycle->getY() + 50*sin(app->rotx*pi / 180), bicycle->getZ() - 50*cos(app->rotx*pi / 180)*cos(app->roty*pi / 180), bicycle->getX(), bicycle->getY(), bicycle->getZ(), 0, 1, 0);  
	}
	//locked camera mode, where the camera is positioned above the bike
	else if (camera == 1) {
		gluLookAt(bicycle->getX() + 70 * cos(bicycle->rot), bicycle->getY() + 50, bicycle->getZ() - 70 * sin(bicycle->rot), bicycle->getX(), bicycle->getY(), bicycle->getZ(), 0, 1, 0);
	}
	//draw the bike object
	drawBicycle(bicycle);
	//draw some grass for ground
	glEnable(GL_TEXTURE_2D);
		glBindTexture(GL_TEXTURE_2D, grass);
		setColor(1, 1, 1, 1);
		glPushMatrix();
		glBegin(GL_QUADS);
			glNormal3d(0, 1, 0);
			glTexCoord2d(5000 / 32.0, 0.0); 
			glVertex3d(5000, 0, 5000);
			glTexCoord2d(0.0, 0.0); 
			glVertex3d(5000, 0, -5000);
			glTexCoord2d(0.0, 5000 / 32.0); 
			glVertex3d(-5000, 0, -5000);
			glTexCoord2d(5000 / 32.0, 5000 / 32.0); 
			glVertex3d(-5000, 0, 5000);
		glEnd();
		glPopMatrix();
	glDisable(GL_TEXTURE_2D);

	//draw the trees
	for (int i = 0; i < numTrees; i++) {
		if (bicycle->location.distance(trees[i]) < 100000) {
			glPushMatrix();
				glTranslated(trees[i].x, trees[i].y + 0.5, trees[i].z);
				glTranslated(0, 25, 0);
				setColor(0, 0.5, 0, 1);
				drawBox(15, 4, 15);
				glTranslated(0, 5, 0);
				drawBox(12, 4, 12);
				glTranslated(0, 5, 0);
				drawBox(9, 4, 9);
				glTranslated(0, 5, 0);
				drawBox(6, 4, 6);
				glTranslated(0, -40, 0);
				setColor(0.3, 0.2, 0, 1);
				drawCylinder(50, 5);
			glPopMatrix();
		}
	}
	glFlush();
	glutSwapBuffers();
}

void appUpdate() {
	if (redrawTime <= getTime()) {
		redrawTime = getTime() + 1 / 60.0;
		//this will update the bicycle position
		update(bicycle);
		//redraw
		glutPostRedisplay();
	}
}

int main(int argc, char** argv) {
	app = new App;
	app->fovy = 60;
	app->rotx = 0;
	app->roty = 0;
	app->viewaxis = true;

	//initialize GLUT settings
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutInitWindowPosition(20, 60);
	glutInitWindowSize(640, 640);
	glutCreateWindow("UCM CSE170 Project 1");
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_POINT_SMOOTH);
	glEnable(GL_LINE_SMOOTH);
	glEnable(GL_NORMALIZE);
	glHint(GL_LINE_SMOOTH_HINT, GL_NICEST);
	glHint(GL_POINT_SMOOTH_HINT, GL_NICEST);
	glPointSize(6);
	glLineWidth(1);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);

	//create light parameters and components
	GLfloat amb[] = { 0.5f, 0.5f, 0.5f, 1.0f };
	GLfloat dif[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	GLfloat spe[] = { 0.5f, 0.5f, 0.5f, 1.0f };
	GLfloat location[] = { 0.0f, 10.0f, 0.0f, 1.0f };

	//GL_LIGHT0 is assigned components
	glLightfv(GL_LIGHT0, GL_AMBIENT, amb);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, dif);
	glLightfv(GL_LIGHT0, GL_SPECULAR, spe);
	glLightfv(GL_LIGHT0, GL_POSITION, location);

	//set material properties here
	float color[] = { 1.0f, 0.0f, 0.0f, 1.0f };
	glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, color);
	float speReflect[] = { 0.2f, 0.2f, 0.2f, 1.0f };
	float difReflect[] = { 1.0f, 1.0f, 1.0f, 1.0f };
	glMaterialfv(GL_FRONT, GL_SPECULAR, speReflect);
	glMaterialfv(GL_FRONT, GL_DIFFUSE, difReflect);
	glMateriali(GL_FRONT, GL_SHININESS, 30);

	//update functions time is set
	redrawTime = getTime() + 1 / 60.0;

	//GLUT callback functions are set up here
	glutKeyboardFunc(appKeyboardFunc);
	glutSpecialFunc(appKeyboardFunc);
	glutReshapeFunc(appResizeWindow);
	glutDisplayFunc(appDrawScene);
	glutIdleFunc(appUpdate);

	//load texture for grass on floor
	grass = loadTexture("grass.bmp");
	
	//create bike object here
	bicycle = new Bicycle();

	//randomly generated number
	srand((unsigned int)redrawTime);
	//randomize locations of trees
	for (int i = 0; i<numTrees; i++) {
		trees[i].x = (rand() % 2000 - 1000) * 5;
		trees[i].z = (rand() % 2000 - 1000) * 5;
	}

	//start main loop
	glutMainLoop();
}

