/* TriWorld.cpp
 *
 * This file contains the program entry point, main, and initiallizes OpenGL.
 */

// Choose exactly 1 timing mode!
//#define TURING
#define WINDOWS
//#define SLOW_WINDOWS

#include <iostream>
#include <time.h>
#include <string>
#include <fstream>
#include "World.h"
#include "Tuplex.h"
#include "Error.h"
#include "Camera.h"
#include "Parser.h"
#include <GL/glut.h>

using namespace std;

// Screen size
const int SCREEN_SIZE = 400;
int height = SCREEN_SIZE;
int width = SCREEN_SIZE;

// Globals
const string DEF_WORLD = "cityscape.wld";
World world;
Parser parser;
clock_t lastTick;

// Enumeration for Menu
enum { M_NONE, M_OPEN, M_EXIT };

// Function Prototypes
void initOpenGL();
void display(void);
void reshape(int w, int h);
void idle();
//void menu(int value);
void keyboard(unsigned char key, int x, int y);
void mouse(int x, int y);

/* main
 *
 * Program entry point.  You know what it does...
 */
int main(int argc, char **argv)
{
	// Initialize glut
	glutInit(&argc, argv);
	glutInitWindowSize(SCREEN_SIZE, SCREEN_SIZE);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutCreateWindow("Cityscape Golf");

	// Set up Right-Click Menu
//	glutCreateMenu(menu);
//	glutAddMenuEntry("Open File", M_OPEN);
//	glutAddMenuEntry("-----------------------", M_NONE);
//	glutAddMenuEntry("Exit", M_EXIT);
//	glutAttachMenu(GLUT_RIGHT_BUTTON);

	// Initalize OpenGL parameters
	initOpenGL();

	// Register callback functions
	glutDisplayFunc(display);
	glutReshapeFunc(reshape);
	glutIdleFunc(idle);
	glutKeyboardFunc(keyboard);
	glutMotionFunc(mouse);

	// Initialize the timer
	lastTick = clock();

	// Setup the world. (Has to be after initOpenGL).
	world.init();
	parser.getFile(DEF_WORLD,&world);

	// Loop until something happens
	glutMainLoop();

	return 0;
}

/* initOpenGL
 *
 * Initializes a few OpenGL parameters.
 */
void initOpenGL()
{
	// Enable depth buffering
	glEnable(GL_DEPTH_TEST);

	// Turn off back face drawing
	//	glCall(GL_BACK); // *****

	// Initialize background color to black
	glClearColor(0.015,0,.25,0);
}


/* idle
 *
 * Called every chance that glut gets.  We'll implement a timer in this
 * function to handle all time-dependant tasks (such as moving the ball).
 */
void idle()
{
	clock_t tick = clock();

	if (!world.gameOver())
	{
#ifdef TURING
	world.timeStep(0.01);								/* For Turing */
#endif
#ifdef WINDOWS
	world.timeStep(double(tick - lastTick) / 1000.0);	/* For Windows */
#endif
#ifdef SLOW_WINDOWS
	world.timeStep(double(tick - lastTick) / 10000.0);	/* For Windows */
#endif

		lastTick = tick;
	}
	else
	{
		if (double(tick - lastTick) / 1000.0 > 0.02)
		{
			Triangle::enlarge();

			lastTick = tick;
		}
	}

	glutPostRedisplay();
}

/* display
 *
 * In charge of drawing the screen by telling the ball and each of the
 * triangles to draw themselves.
 */
void display()
{
	// Clear buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Reset transform matrix
	glLoadIdentity();

	world.draw();

	// Swap buffers
	glutSwapBuffers();
}

/* reshape
 *
 * Called whenever the user resizes the graphics window.  In this function
 * we'll make sure that the draw image doesn't get deformed and is more or
 * less centered.
 */
void reshape(int w, int h)
{
	if (w < h)
		glViewport((w - h) / 2, 0, h, h);
	else
		glViewport(0, (h - w) / 2, w, w);

	width = w;
	height = h;
	lastTick = clock(); // resync timer
}

///* menu
// *
// */
//void menu(int value)
//{
//   string cmd;
//
//   switch(value)
//   {
//	case M_EXIT:
//		exit(0);
//		break;
//	case M_OPEN:
//		//*****
//		/*
//		cout << "Please enter filename of input file.\n-> ";
//		cin >> cmd;
//		world.reconfigure(cmd);
//		*/
//		parser.loadFile(&world);
//		break;
//	default:
//		cerr << "Invalid Menu argument: " << value << endl;
//		break;
//   }
//   lastTick = clock(); // resync timer
//}


/* keyboard
 *
 * This function will load a input file. If the user presses any key, he is prompted
 * to type in the filename of an input file. This file is then parsed and, pending
 * errors encountered while parsing, adds the required vertices, triangles and edges
 * to the World.
 *
 * A later refinement might be to have a load key 'l' that prompts for the filename,
 * freeing up all the other keys for other functionality.
 */
void keyboard(unsigned char key, int x, int y)
{

	//printf("Key = %d\n", key); // *****

	// surpress unused variable warnings
	(void)x;
	(void)y;

	switch(key)
	{
	case ' ':
		world.swing();
		break;
	case 'I':
	case 'i':
		world.lightToggle();
		break;
	case '_':
	case '-':
		world.camera.zoomOut();
//		cout << "Camera: Zooming out\n";
		break;
	case '=':
	case '+':
		world.camera.zoomIn();
//		cout << "Camera: Zooming in\n";
		break;
	case 'W':
	case 'w':
		world.camera.moveForward();
//		cout << "Camera: Forward\n";
		break;
	case 'S':
	case 's':
		world.camera.moveBackward();
//		cout << "Camera: Backward\n";
		break;
	case 'A':
	case 'a':
		world.camera.moveLeft();
//		cout << "Camera: Left\n";
		break;
	case 'D':
	case 'd':
		world.camera.moveRight();
//		cout << "Camera: Right\n";
		break;
	case 'V':
	case 'v':
		world.camera.moveUp();
//		cout << "Camera: Up\n";
		break;
	case 'C':
	case 'c':
		world.camera.moveDown();
//		cout << "Camera: Down\n";
		break;
	case 'X':
	case 'x':
		world.camera.axesToggle();
//		cout << "Axes " << (world.camera.axes() ? "On" : "Off") << endl;
		break;
	case 'B':
	case 'b':
		(world.camera.tracking()) ? 
			world.camera.track(NULL) :
			world.camera.track(world.pBallPosition());
		break;
	case 'P':
	case 'p':
		parser.cycleVerbosity();
		break;
	case 'O':
	case 'o':
		world.rollDebugToggle();
		break;
	case 'n':
		cout << "Ball At: " << *(world.pBallPosition()) << endl;
		break;
	case 'N':
		world.ballDebugToggle();
		break;
	case 'L':
	case 'l':
		parser.loadFile(&world);
		break;
	case '\\':
		Triangle::toggleCrazyTris();
		break;
	case '[':
		Triangle::enlarge();
		break;
	case ']':
		Triangle::shrink();
		break;
	default:
		cout << "i - toggles lights\n"
			 << "b - toggles ball follow\n"
			 << "+/- - zooms in/out\n"
			 << " w   v\n"
			 << "asd  c - move the view (when not following ball)\n"
			 << "x - toggles axes\n"
			 << "p - cycles parsing verbosity\n"
			 << "o - toggle collision/rolling/stop notifications\n"
			 << "n - output ball's position\n"
			 << "N - toggle constand Ball updates\n"
			 << "l - loads a file\n"
			 << "\"\\\" - toggle triangle resize\n"
			 << "[ / ] - separate/conjoin triangles\n"
			 << endl;
		break;
	}
	lastTick = clock(); // resync timer
}

void mouse(int x, int y)
{
	world.camera.rotate(x,y,width,height);
}
