#include "project_mercury.h"

#define TITLE	"project mercury"

int main_window;
int theme_idx = 0;

//access to client code
plane ground;
CCamera cam;
kugel sphere;
skybox my_skybox;
Lightning lightning;

//grafics options
bool light_on;
bool lightning_on;
bool skybox_on;

int w,h;   //the screen weidth / heigth

int mouseX;	//old values of the mouse (to compute the difference)
int mouseY;
bool mouse_shift; //instead of changeing the viewing dir; move camera position

GLfloat white[4] = {1.0,1.0,1.0,1.0};
GLfloat yellow[4] = {1.0,0.95,0.55,.5};
GLfloat grey[4] = {0.5,0.5,0.5,0.5};

GLfloat light0_pos[4] = { 0.0f, 5.0f, 0.0f, 1.0f };

int frame_counter, new_time, old_time, light_time, idle_counter=0;
char fps[30]; //holds fps string


//booleans for the polling of keyboard
bool move = false;
bool move_up = false;
bool move_down = false;
bool move_forward = false;
bool move_backward = false;
bool move_left = false;
bool move_right = false;
bool nick = false;
bool nick_up = false;
bool nick_down = false;
bool nick_left = false;
bool nick_right = false;
bool move_ball_up = false;
bool move_ball_down = false;




void render_bitmap_string (float x, float y, void *font,char *string)
{ 
	//glColor3f(1,1,1);		//can't see anything
	//glShadeModel(GL_SMOOTH);
	//glDepthFunc(GL_ALWAYS);
	float mat_color[4] = { 1.0f, 1.0f, 1.0f, 1.0f };
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_color);
	  char *c;
	  glRasterPos2f(x, y);
	 
	  for (c=string; *c != '\0'; c++) {
		glutBitmapCharacter(font, *c);
	  }
 
   //glDepthFunc(GL_LEQUAL);
   
}



void setOrthographicProjection() {

	// switch to projection mode
	glMatrixMode(GL_PROJECTION);
	// save previous matrix which contains the 
	//settings for the perspective projection
	glPushMatrix();
	// reset matrix
	glLoadIdentity();
	// set a 2D orthographic projection
	gluOrtho2D(0, w, 0, h);
	// invert the y axis, down is positive
	glScalef(1, -1, 1);
	// mover the origin from the bottom left corner
	// to the upper left corner
	glTranslatef(0, -h, 0);
	glMatrixMode(GL_MODELVIEW);
}

void resetPerspectiveProjection() {
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}

void setOrthographicCamProjection() {

	// switch to projection mode
	glMatrixMode(GL_PROJECTION);
	// save previous matrix which contains the 
	//settings for the perspective projection
	glPushMatrix();
	// reset matrix
	glLoadIdentity();
	// set a 2D orthographic projection
	gluOrtho2D(0, w, 0, h);
	// invert the y axis, down is positive
	//glScalef(1, -1, 1);
	// mover the origin from the bottom left corner
	// to the upper left corner
	//glTranslatef(0, -h, 0);
	glMatrixMode(GL_MODELVIEW);
}







void display_handler(){

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	//glClear(GL_COLOR_BUFFER_BIT);

	glLoadIdentity();


	//setOrthographicCamProjection();
	//glMatrixMode(GL_PROJECTION);
	//glPushMatrix();
	// reset matrix
	//glLoadIdentity();

	cam.render();

	//glPopMatrix();
	//glMatrixMode(GL_MODELVIEW);
	//resetPerspectiveProjection();


	//glScalef(3.0,1.0,3.0);
	//	glTranslatef(0.0,-0.5,-6.0);


		
	

	
 // glPopMatrix();

	//render plane
	glPushMatrix();
		//glColor3f(1,1,1);
		glRotatef(ground.nickX,1,0,0);
		glRotatef(ground.nickZ,0,0,1);
		ground.render();
	glPopMatrix();

	//render sphere
	glPushMatrix();
		glRotatef(ground.nickX,1,0,0);
		glRotatef(ground.nickZ,0,0,1);
		//glTranslatef(0,2,0);// move by up (radius)
		sphere.render();
	glPopMatrix();

	//render skybox
	if (skybox_on){
	glPushMatrix();
		my_skybox.render_skybox();
	glPopMatrix();}

	//render lightning (if available)
	if (lightning_on){
	glPushMatrix();
		glRotated(cam.rotatedY,0,1,0);
		lightning.update();
	glPopMatrix();}

	//text output in orthographic projection
	setOrthographicProjection();
		glPushMatrix();
		glLoadIdentity();
		//glMatrixMode(GL_PROJECTION);
			frame_counter++;
			new_time=glutGet(GLUT_ELAPSED_TIME);
			//add lightnings new done in the lightning render method
			//if (new_time - light_time>3000) {lightning.rnd_add(); light_time = new_time;}
			if (new_time - old_time > 1000) {
					
				sprintf_s(fps,"FPS:%4.2f",frame_counter*1000.0/(new_time-old_time));
				
				old_time = new_time;		
				frame_counter = 0;
			}
			render_bitmap_string(20,20,GLUT_BITMAP_9_BY_15,fps);

			//render_bitmap_string(300,300,GLUT_BITMAP_HELVETICA_10,"test");
			//glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
	resetPerspectiveProjection();


	//finish rendering:
	//glFlush();  

	
	glutSwapBuffers();
}

void idle_handler(){

	ground.damp();
	sphere.update(Vector3(-ground.nickZ,0.0,ground.nickX));

	sphere.update_internal_forces();


	//lightning handler
	/*
	if (lightning.light_count>0 && idle_counter<80) {
		idle_counter++;
		if (idle_counter<=1)glEnable(GL_LIGHT1);	//set only the first time
													//to avoid flickering		
	} else {lightning.light_count=0;idle_counter=0; glDisable(GL_LIGHT1);}
	*/


	//simulate keyboard handler polling
	if (move)
	{
		float kb_delta = 0.04; //keyboard move speed

		if (move_forward)  cam.move_forward( -kb_delta ) ;
	
		if (move_backward) cam.move_forward( kb_delta ) ;

		if (move_left) cam.strafe_right(-kb_delta);

		if (move_right) cam.strafe_right(kb_delta);

		if (move_up) cam.move_up(kb_delta);

		if (move_down) cam.move_up(-kb_delta);
		
	}

	if (nick)
	{
		int delta = 1;		//mouse rot angle / nick delta

		if (nick_up) ground.nick_plane(delta, 0, cam.get_view_dir());

		if (nick_down) ground.nick_plane(-delta, 0, cam.get_view_dir());

		if (nick_left) ground.nick_plane(0, -delta, cam.get_view_dir());

		if (nick_right) ground.nick_plane(0, delta, cam.get_view_dir());

		if (move_ball_up) sphere.update_gravity(.3);

		if (move_ball_down && sphere.vertices_touching_ground < 25) sphere.update_gravity(-.3);

	}

	

	
	display_handler();
}

void mouse_motion (int x, int y){

	int diffX = mouseX-x;
	int diffY = mouseY-y;
	mouseX=x;
	mouseY=y;

	//int mouse_modifier = glutGetModifiers(); 

	if (mouse_shift) 
	{
		GLfloat moveSideward = (GLfloat) -diffX;
		GLfloat moveForward = (GLfloat) -diffY;

		cam.move_forward(moveForward);
		//display_handler();
		cam.strafe_right(moveSideward);
		//display_handler();
		//mouse_shift = false;

	}
	else
	{
		GLfloat angleX = (GLfloat) diffX;
		GLfloat angleY = (GLfloat) diffY;

		cam.rotateY(angleX);
		//display_handler();
		cam.rotateX(angleY);	
		//display_handler();
	}

}

//not used...
void mouse_button(int button, int state, int x, int y)
{
	int diffX = mouseX-x;
	int diffY = mouseY-y;
	mouseX=x;
	mouseY=y;
	if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN) {
		GLfloat moveSideward = (GLfloat) -diffX;
		GLfloat moveForward = (GLfloat) -diffY;

		cam.move_forward(moveForward);
		//display_handler();
		cam.strafe_right(moveSideward);
		//display_handler();
		//mouse_shift = false;
	}
}

void keyboard_handler(unsigned char key, int x, int y){
	
	mouse_shift = false;
	int delta = 40;		//mouse rot angle / nick delta
	float kb_delta = 0.9; //keyboard move speed
	
	

		switch (key) 
	{
	case 27:		//ESC
		glutDestroyWindow(main_window);
		exit(0);
		break;
	case 'a':		
		//cam.strafe_right(-kb_delta);
		move = true;
		move_left = true;
		//display_handler();
		break;
	case 'd':		
		//cam.strafe_right(kb_delta);
		move = true;
		move_right = true;
		//display_handler();
		break;
	case 'w':		
		//cam.move_forward( -kb_delta ) ;
		move = true;
		move_forward = true;
		//display_handler();
		break;
	case 's':		
		//cam.move_forward( kb_delta ) ;
		move = true;
		move_backward = true;
		//display_handler();
		break;
	case 'f':
		//cam.move_up(-kb_delta);
		move = true;
		move_down = true;
		//display_handler();
		break;
	case 'r':
		//cam.move_up(kb_delta);
		move = true;
		move_up = true;
		//display_handler();
		break;

	case 'l':
		lightning.add();
		display_handler();
		break;
	case 't':
		//sphere.update_gravity(1.);
		nick = true;
		move_ball_up = true;
		//display_handler();
		break;
	case 'g':
		//sphere.update_gravity(-1.);
		nick = true;
		move_ball_down = true;
		//display_handler();
		break;
	case '1':
		sphere.toggle_vertices();
		display_handler();
		break;
	case '2':
		sphere.toggle_springs();
		display_handler();
		break;
	case '3':
		sphere.toggle_triangle();
		display_handler();
		break;
	case '0':
		sphere.reset();
		display_handler();
		break;
	case '9':
		sphere.reconstitute();
		display_handler();
		break;
	case '8':
		if (light_on) {light_on=false; glDisable(GL_LIGHTING);} else {light_on=true; glEnable(GL_LIGHTING);}
		ground.toggle_gfx();
		if (lightning_on) lightning_on=false; else lightning_on=true;
		if (skybox_on) skybox_on=false; else skybox_on=true;
		display_handler();
		break;

	case 32:	//space    can't find ascii code for shift?!
		//int mouse_modifier = glutGetModifiers(); 
		//if (mouse_modifier==GLUT_ACTIVE_SHIFT) mouse_shift = true;
		mouse_shift = true;
		break;
	
	}

	
}

void keyboard_up_handler(unsigned char key, int x, int y){


	
		switch (key) 
	{
	case 'a':		
		//cam.strafe_right(-kb_delta);
		move = false;
		move_left = false;
		//display_handler();
		break;
	case 'd':		
		//cam.strafe_right(kb_delta);
		move = false;
		move_right = false;
		//display_handler();
		break;
	case 'w':		
		//cam.move_forward( -kb_delta ) ;
		move = false;
		move_forward = false;
		//display_handler();
		break;
	case 's':		
		//cam.move_forward( kb_delta ) ;
		move = false;
		move_backward = false;
		//display_handler();
		break;
	case 'f':
		//cam.move_up(-kb_delta);
		move = false;
		move_down = false;
		//display_handler();
		break;
	case 'r':
		//cam.move_up(kb_delta);
		move = false;
		move_up = false;
		//display_handler();
		break;
	case 't':
		//sphere.update_gravity(1.);
		nick = false;
		move_ball_up = false;
		//display_handler();
		break;
	case 'g':
		//sphere.update_gravity(-1.);
		nick = false;
		move_ball_down = false;
		//display_handler();
		break;
	case 32:
		mouse_shift = false;
		break;
	
	}
}

void spec_keyboard_handler (int k, int x, int y)
{
	switch(k)      {
      case GLUT_KEY_UP:
            nick = true;
			nick_up = true;
            break;
      case GLUT_KEY_DOWN:
            nick = true;
			nick_down = true;
            break;
      case GLUT_KEY_LEFT:
            nick = true;
			nick_left = true;
            break;
      case GLUT_KEY_RIGHT:
            nick = true;
			nick_right = true;
            break;
      }

      //glutPostRedisplay();
}

void spec_keyboard_up_handler (int k, int x, int y)
{
	switch(k)      {
      case GLUT_KEY_UP:
            nick = false;
			nick_up = false;
            break;
      case GLUT_KEY_DOWN:
            nick = false;
			nick_down = false;
            break;
      case GLUT_KEY_LEFT:
            nick = false;
			nick_left = false;
            break;
      case GLUT_KEY_RIGHT:
            nick = false;
			nick_right = false;
            break;
      }

      //glutPostRedisplay();
}


void reshape_handler(int _w, int _h){
	w = _w;
	h = _h;
	h = (h==0)?1:h;
	//w = (w==0)?1:w;
	//glViewport(0,0,w,h);
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluPerspective(45.0, (double)w/(double)h,0.1,300.0);
	
	glMatrixMode(GL_MODELVIEW);
	glViewport(0,0,w,h);
	glLoadIdentity();
}

int main(int argc, char** argv){
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_RGBA|GLUT_DOUBLE|GLUT_DEPTH);
	glutInitWindowSize(640,480);
	main_window = glutCreateWindow(TITLE);
	glClearColor(0.0,0.0,0.0,0.0);
	//glutFullScreen();
	


	//handler functions
	glutIgnoreKeyRepeat(true);   //simulate polling
	glutDisplayFunc(display_handler);
	glutIdleFunc(idle_handler);
	glutReshapeFunc(reshape_handler);
	glutKeyboardFunc(keyboard_handler);
	glutKeyboardUpFunc (keyboard_up_handler);
	glutSpecialFunc(spec_keyboard_handler);
    glutSpecialUpFunc(spec_keyboard_up_handler);
	glutPassiveMotionFunc(mouse_motion);
	//glutMotionFunc(mouse_motion);
	//glutMouseFunc(mouse_button);
	//ShowCursor(FALSE);


	//gl init
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA,GL_ONE);	
	//glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_DEPTH_TEST);
	glShadeModel(GL_SMOOTH);   
	//glEnable(GL_COLOR_MATERIAL); 
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); 
	glHint(GL_POINT_SMOOTH_HINT,GL_NICEST);

	//light
	glLightfv(GL_LIGHT2, GL_DIFFUSE, white);
	glLightfv(GL_LIGHT2, GL_AMBIENT, white);
	glLightfv(GL_LIGHT2, GL_SPECULAR, white);
	glLightfv(GL_LIGHT2, GL_POSITION, light0_pos);
	GLfloat dir[3] = {0.4,-1.0,0.2};
	glLightfv(GL_LIGHT2, GL_SPOT_DIRECTION, dir);
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT2);

	glLightfv(GL_LIGHT1, GL_DIFFUSE, white);
	glLightfv(GL_LIGHT1, GL_AMBIENT, white);
	glLightfv(GL_LIGHT1, GL_SPECULAR, white);
	GLfloat light1_pos[4] = {0.0, 7., 0.0, 1.0f };
	glLightfv(GL_LIGHT1, GL_POSITION, light1_pos);

	//glEnable(GL_LIGHT1);
	
  
	printf("Physically-based Simulation  ETHZ WS 06/07 \n");
	printf("Programmed by:\nRolf Weilenmann, Lars Krapf, Florian Hotz\n");
	

	ground = *(new plane());
	if (!ground.initialie(Vector3 (light0_pos[0],light0_pos[1],light0_pos[2]))) printf("fehler beim laden der ground textur\n");

	cam = *(new CCamera());

	cam.move( Vector3(0.0, 7.0, 15.0));
	cam.move_forward( 1.0 );
	cam.rotateY(180.0);

	sphere = *(new kugel());
	if (!sphere.init()) printf("fehler beim initialisieren der kugel\n");

	my_skybox = *(new skybox());

	if (!my_skybox.init_skybox(200,200,200)) printf("fehler beim initialisieren der skybox\n");

	
	lightning = *(new Lightning());
	if (!lightning.init()) printf("fehler beim initialisieren des Blitzes\n");

	light_on=true;
	skybox_on=true;
	lightning_on=true;


	old_time=glutGet(GLUT_ELAPSED_TIME);
	light_time =old_time;

	
	glutMainLoop();
	return 0;
}


