#include <windows.h>
#define GLUT_DISABLE_ATEXIT_HACK 
#include <GL/glut.h>
#include <iostream>
#include <fstream>
#include <string>
#include <math.h>
#include "world.h"
#include "constants.h"

// viewpoint
double theta = VIEW_THETA;
double phi = VIEW_PHI;
double viewRadius = 0.5;
double camX = INIT_CAM_X;
double camY = INIT_CAM_Y;
double camZ = INIT_CAM_Z;

// window
int width = 400;
int height = 400;


// function prototypes
void display(void);
void reshape(int width, int height);
void keyboard(unsigned char key, int x, int y);
void motion(int x, int y);
void init(void);
void timeStep(int value);
void read();
void help();
static void setPause(bool result);


int	windowWidth = 400;
int windowHeight = 400;
bool again = true;
bool step = false;


World* GameWorld = NULL;

using namespace std;

int main(int argc, char **argv)
{
	help();
  
	// set up window
	glutInitWindowSize(windowWidth, windowHeight);
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
	glutCreateWindow("Moon Tanks");


	// register callback functions
	glutDisplayFunc(display);
	glutReshapeFunc(reshape); 
	glutKeyboardFunc(keyboard);
	glutTimerFunc(TIME_STEP, timeStep, VALUE);
	glutMotionFunc(motion);


	// initialize opengl parameters
	init();

	read();

	// loop until something happens
	glutMainLoop();
	return 0;           
}

void init()
{
  // initialize viewing system
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  gluPerspective(45.0, 1.0, 1.0, 100.0);
  glMatrixMode(GL_MODELVIEW);
 
  // initialize background color to some specified color
  glClearColor(SKY_RED,SKY_GREEN,SKY_BLUE,0.0);

  // enable depth buffering
  glEnable(GL_DEPTH_TEST);

  // Enable smoothing
  glEnable(GL_SMOOTH);
}



void reshape(int width, int height)
{
	glViewport(0,0,width,height);
}

void display()
{

// compute eye position
	glLoadIdentity();
	Vector eye(0,0,0);
	eye[0] = (float) -viewRadius * cos(theta) * sin(phi);
	eye[1] = (float) viewRadius * sin(theta);
	eye[2] = (float) viewRadius * cos(theta) * cos(phi);

	Vector mapCenter(MAP_SIZE/2, 0, MAP_SIZE/2);

	// compute up vector
	// the is the vector orthogonal to eye that lies in the same
	// plane as eye and (0,1,0) and has a positive dot product with
	// (0,1,0)
	Vector up = Vector(0,1,0);
	Vector toEye = eye/eye.length();
	up = up - toEye * toEye.dot(up);
	up = up.normalize();
	
	double eyeX = eye[0] + camX;
	double eyeY = eye[1] + camY;
	double eyeZ = eye[2] + camZ;

	gluLookAt(eyeX, eyeY, eyeZ, camX, camY, camZ, up[0], up[1], up[2]);

	// clear buffers
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	GameWorld->display();

	// draw to screen
	glutSwapBuffers();
}

void keyboard(unsigned char key, int x, int y)
{
	// Actions can only be performed when there is a world.
	if (GameWorld != NULL)
	{
	switch(key)
	{
	case ZOOM_IN:
		viewRadius -= 1.0f;
		if (viewRadius < 0.5)
			viewRadius = 0.5;
		break;

	case ZOOM_OUT:
		viewRadius += 1.0f;
		break;

	// Pause/resume the ball.
	case PAUSE:
		{
			again = !again;
			step = false;
	
			if (again)
			{
				cout << "Resumed." << endl;
				glutTimerFunc(TIME_STEP, timeStep, VALUE);
			}
			else
			{
				cout << "Paused." << endl;
			}
			break;
		}
		// tank fires and bullet is created with some initial velocity
	case FIRE1:
		{
			GameWorld->fire();
			break;
		}


	// Step forward
	case STEP:
		{
			// Pause
			again = false;
			step = true;
			cout << "Stepping..." << endl;
			break;
		}

	case THETA_UP:
		{
			GameWorld->upTheta();
			break;
		}

	case THETA_DOWN:
		{
			GameWorld->downTheta();
			break;
		}

	case PHI_UP:
		{
			GameWorld->upPhi();
			break;
		}

	case PHI_DOWN:
		{
			GameWorld->downPhi();
			break;
		}
	
	case POWER_UP:
		{
			GameWorld->upPower();
			break;
		}

	case POWER_DOWN:
		{
			GameWorld->downPower();
			break;
		}

	case CAM_POSX:
		{
			camX += CAM_INCREMENT;
			break;
		}

	case CAM_NEGX:
		{
			camX -= CAM_INCREMENT;
			break;
		}

	case CAM_POSY:
		{
			camY += CAM_INCREMENT;
			break;
		}
	case CAM_NEGY:
		{
			camY -= CAM_INCREMENT;
			break;
		}
	case CAM_POSZ:
		{
			camZ += CAM_INCREMENT;
			break;
		}
	case CAM_NEGZ:
		{
			camZ -= CAM_INCREMENT;
			break;
		}




	// Default case; display help
	default:
		{
			help();
			break;
		}
	}
	
	glutPostRedisplay();  // redraw screen when all other requests 
						  // are satisfied
	}
	
}

void timeStep(int value)
{
	if (GameWorld != NULL && (again || (!again && step)))
	{
		GameWorld->step();
		step = false;
	}
	glutTimerFunc(TIME_STEP, timeStep, VALUE);
}

void read()
{
	int numPlayers;
	bool validPlayers = false;
	while (!validPlayers)
	{
		cout << "Please enter the number of players (max " << MAX_PLAYERS << "): ";
		cin >> numPlayers;

		if (numPlayers > 1 && numPlayers <= MAX_PLAYERS)
		{
			validPlayers = true;
		}
	}
	GameWorld = new World(numPlayers);
}

/*
 * Print the help information.
 */
void help()
{
	cout << endl;
	cout << "--------------------------------------------------------------------------" << endl;
	cout << "                 MOON TANKS CONTROLS"											         << endl;
	cout << "--------------------------------------------------------------------------" << endl;
	cout << "WARNING: NUM LOCK MUST BE TURNED ON FOR THE CONTROLS TO FUNCTION PROPERLY!" << endl;
	cout << "Tank controls:" << endl;
	cout << "Press \'" << THETA_UP << "\' and \'" << THETA_DOWN << "\' to rotate left and right." << endl;
	cout << "Press \'" << PHI_UP << "\' and \'" << PHI_DOWN << "\' to rotate up and down." << endl;
	cout << "Press \'" << POWER_UP << "\' and \'" << POWER_DOWN << "\' to increase and decrease shot power." << endl;
	cout << "Press \'" << FIRE1 << "\' to fire a bullet." << endl;
	cout << "Camera controls:" << endl;
	cout << "Press \'" << ZOOM_IN << "\' to zoom in." << endl;
	cout << "Press \'" << ZOOM_OUT << "\' to zoom out." << endl;
	cout << "Press \'" << CAM_POSX << "\' and \'" << CAM_NEGX << "\' to pan in the X direction." << endl;
	cout << "Press \'" << CAM_POSY << "\' and \'" << CAM_NEGY << "\' to pan in the Y direction." << endl;
	cout << "Press \'" << CAM_POSZ << "\' and \'" << CAM_NEGZ << "\' to pan in the Z direction." << endl;
	cout << "Click the mouse and drag to change the camera view angle." << endl;
	cout << endl;
	cout << "--------------------------------------------------------------------------" << endl;
	cout << endl;
}
void motion(int x, int y)
{

	static int currX=-1;
	static int currY=-1;

	if (currX>0  && abs(x-currX) < width/6 && abs(y-currY) < height/6) {
		phi += (double) (x-currX)/ (double) width * PI;
		theta += (double) (y-currY)/ (double) height * PI;
	// limit theta to -4pi/9 and 4pi/9
	if (theta < -4*PI/9.0)
		theta = -4*PI/9.0;
	if (theta > 4*PI/9.0)
		theta = 4*PI/9.0;
	}
	currX = x;
	currY = y;
	glutPostRedisplay();
}

