/* 
 * Robotics 3D Map Tool
 * program.cpp
 *
 * Developer: Kamil A. Wnuk
 * Advisor: Professor Zach Dodds
 * Harvey Mudd College
 *
 * Updated: 5/20/04
 */

#include <windows.h>

#define GLUT_DISABLE_ATEXIT_HACK 

#include <GL/glut.h>


#include <iostream>

#include <fstream>

#include <vector>

#include "program.h"



using namespace std;


Program& Program::Get()
{
	static Program p;
	return p;
}


Program::Program():
	window_wid(0), window_ht(0),
	view_zoom(1.0), view_dx(0), view_dy(0),
	controlState(CONTROL_NONE)
{
	view_dx = 0.0;
	view_dy = 0.0;
	view_th = 0.0;
	view_zoom = 1.0;
}


Program::~Program()
{
	DrawableList::iterator lastDrawable = all_Drawables.end();
	for (DrawableList::iterator i = all_Drawables.begin();
			i != lastDrawable; )
	{
		delete (*i);
		i = all_Drawables.erase(i);
	}
}


bool Program::Init( const int & wid, const int & ht )
{
	cout << "Init OpenGL" << endl;

	window_wid = wid;
	window_ht = ht;

	// initialize viewing system

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D( 0, window_wid, 0, window_ht);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();

	// enable depth buffering

	glEnable(GL_DEPTH_TEST);
	//	glEnable(GL_BLEND);

	//	glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);


	// initialize background color to black

	glClearColor(0.5,0.5,0.5,0);	

	// shading model

	// glShadeModel(GL_SMOOTH); // Goraud Shading

	// glShadeModel(GL_FLAT); // takes the first vertex's color


	LoadMap();
	LoadRobot();
	LoadLog();

	cout << "Init Finished" << endl;
	return true;
}


void Program::Render( )
{
	glLoadIdentity();

	// Control zoom

	// glPixelZoom (view_zoom, view_zoom);


	// Move the view around


	glViewport(0,0,window_wid,window_ht); // this should never change... all 

	// changes to our window's view of the global coordinate system

	// should be done in the following...

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

    // here is where we set our window's view of the global coordinate system,

	// which (at start) should have the origin in the center of the window,

	// with the robot's origin at this origin and 1 pixel/centimeter

	// 

	// certainly, changing the view will change all of these parameters!

	//

	// see the key event handling method to get a sense of how we can change things...


    // center at the origin (plus our user-specified view_dx and view_dy

    double lower_left_x = -window_wid/2;
	double lower_left_y = -window_ht/2;
	double upper_right_x = lower_left_x + window_wid;
	double upper_right_y = lower_left_y + window_ht; // these should be computed once per window resize, eventually...


	gluOrtho2D(lower_left_x,upper_right_x,lower_left_y,upper_right_y);
	glTranslatef(view_dx, view_dy, 0.0);
	glRotatef(view_th,0.0,0.0,1.0);
    glScalef(view_zoom,view_zoom,1.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();



	// clear buffers

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// draw all drawable items

	
	DrawableList::iterator lastDrawable = all_Drawables.end();
	for (DrawableList::iterator i = all_Drawables.begin();
			i != lastDrawable; ++i )
	{
		(*i)->Draw();
	}
	

	glutSwapBuffers();
}


void Program::Reshape(const int & wid, const int & ht)
{
	window_wid = wid;
	window_ht = ht;

	glViewport(0,0,window_wid,window_ht);
	//glMatrixMode(GL_PROJECTION);

	//glLoadIdentity();

	//gluOrtho2D(0.0, window_wid + view_dx, 0.0, window_ht + view_dy);

	//glMatrixMode(GL_MODELVIEW);

	//glLoadIdentity();

}


/*
 * LoadRobot
 */
void Program::LoadRobot()
{
	robot = new Robot();
	all_Drawables.push_back(robot);
}

void Program::LoadMap()
{
	map = new Map();
	all_Drawables.push_back(map);
}

void Program::LoadLog()
{
	log = new Log();
}

 
//time intervals

void Program::Timestep( float dt )
{
	glutPostRedisplay();

	//redraws path taken logged in log_in.txt

	if(log->inlog==true)
	{
		double x,y,th;
		log->getNextPos(&x,&y,&th);
		if (x < -100000000000)
		{
			log->inlog = false;
		}
		else
		{
			robot->setCoordinates(x,y,th);
		}
	}
}


void Program::KeyEvent( char key, bool key_down )
{
	//key = tolower(key);

	cout << "KEY " << key << ((key_down)?" down":" up") << endl;
	
	if (key_down) {

		// move robot up

		if (key == 'i') {
			robot->deltaCoordinates(0,10,0);
		}
		
		// move robot down

		if (key == 'k') {
			robot->deltaCoordinates(0,-10,0);
		}
		
		// move robot to left

		if (key == 'j') {
			robot->deltaCoordinates(-10,0,0);
		}
		
		// move robot to right

		if (key == 'l') {
			robot->deltaCoordinates(10,0,0);
		}

		// rotate robot counterclockwise

		if (key == 'o') {
			robot->deltaCoordinates(0,0,10);
		}

		// rotate robot clockwise

		if (key == 'u') {
			robot->deltaCoordinates(0,0,-10);	
		}

		// move map up

		if (key == 'e') {
			view_dx += 0; view_dy += 10; view_th += 0; view_zoom += 0;
		}
		
		// move map down

		if (key == 'd') {
			view_dx += 0; view_dy -= 10; view_th += 0; view_zoom += 0;
		}

		// move map to left

		if (key == 's') {
			view_dx -= 10; view_dy += 0; view_th += 0; view_zoom += 0;
		}

		// move map to right

		if (key == 'f') {
			view_dx += 10; view_dy += 0; view_th += 0; view_zoom += 0;
		}

		// rotate map counterclockwise

		if (key == 'w') {
			view_dx -= 0; view_dy += 0; view_th += 10; view_zoom += 0;
		}

		// rotate map clockwise

		if (key == 'r') {
			view_dx += 0; view_dy += 0; view_th -= 10; view_zoom += 0;
		}

		// zoom in

		if (key == 'z') {
			view_dx += 0; view_dy += 0; view_th += 0; view_zoom *= 1.1;
		}
		
		// zoom out

		if (key == 'x') {
			view_dx += 0; view_dy += 0; view_th += 0; view_zoom /= 1.1;
		}

		// show the robot IR sensors

		if (key == 'c') {
			robot->show_IR = !robot->show_IR;
		}

		// show the robot's sonar sensor

		if (key == 'a') {
			robot->show_sonar = !robot->show_sonar;
		}

	    // show the robot's sonar sensor

		if (key == 'A') {
			//robot->computeSonarDistance();

			//cout << "son_dist is " << robot->son_dist << endl;

			robot->show_particle_sonar = !robot->show_particle_sonar;
		}

		// draw particles 

		if (key == 'p') {
			robot->draw_particles = !robot->draw_particles;
		}

		// show the path taken by the robot

		if (key == 'v') {
			robot->show_Hist = !robot->show_Hist;
		}

        // clear the history of the robot (the path)

		if (key == 'h') {
			robot->x_hist.clear();
			robot->y_hist.clear();
			robot->th_hist.clear();
		}

		// updates the particle position when pushed

		if (key == 'm') {
			Program::Get().robot->MoveParticles();
		}

		// updates particle probability

		if (key == 'n') {
			Program::Get().robot->UpdateParticleProbabilities();
		}

		// updates the particles based on probability

		if (key == 'b') {
			Program::Get().robot->ResampleParticles();
		}

		/*
		//allows the logging to take place
		if (key == '1') {
			log->outlog = !log->outlog;
		}

		//emulates log stored in folder
		if (key == '2') {
			log->inlog = !log->inlog;
		}
		*/

		// clear particles and set N of them at the robot's current pose...

		if (key == '1') {
			int N = 100; // getNumParticlesFromUser();

			Program::Get().robot->setupParticles(N,Robot::AT_POSE);
		}

		// clear particles and set N of them at the robot's current position...

		// this time with random orientation

		if (key == '2') {
			int N = 500; // getNumParticlesFromUser();

			Program::Get().robot->setupParticles(N,Robot::AT_POSITION);
		}

		// clear particles and set N of them all over the place (but in bounds)

		if (key == '3') {
			int N = 500; // getNumParticlesFromUser();

			Program::Get().robot->setupParticles(N,Robot::ANYWHERE);
		}

		// clear particles and set N of them all over the place (but in bounds)

		// sampled by pulling back from the environment!

		if (key == '4') {
			int N = 500; // getNumParticlesFromUser();

			Program::Get().robot->setupParticles(N,Robot::WALL_RELATIVE);
		}

	
		// quit program

		if (key == 'q') {
			cout << "Exit" << endl;
			exit(0);
		}
	}
}


void Program::MouseClick (int button, int button_state, int x, int y)
{
	;
	/*
	prevMousePos_x = x;
	prevMousePos_y = y;

	// Print the mouse position (x,y) when the left button is clicked
	// The mouse origin is in the upper left corner, positive y is down,
	// positive x is right
	if (button == GLUT_LEFT_BUTTON && button_state == 0)
	{
		// Allocate space for one pixel of data
		unsigned char* requested_pixel = new byte[4];

		// read just one pixel by specifying a 1x1 pixel rectangle
		glReadPixels(x, (window_ht-1)-y, 1, 1, GL_RGBA, 
			GL_UNSIGNED_BYTE, requested_pixel);
		
		if (requested_pixel != NULL)
			cout << " mousePos = ( " << x << ", " << y 
				<< " )  color = ( " 
				<< (int)requested_pixel[0] << ", " << (int)requested_pixel[1] << ", "
				<< (int)requested_pixel[2] << ", " << (int)requested_pixel[3] << 
				" )" << endl;	

		delete[] requested_pixel;
	}
	// right button controls the point you are looking at in X and Y
	else if (button == GLUT_RIGHT_BUTTON)
	{
		if( button_state == GLUT_DOWN )
		{
			controlState = CONTROL_TRANSLATE;
		}
		else
		{
			controlState = CONTROL_NONE;
		}
	}*/
	//// middle button controls the point you are looking at in X and Y

	//else if (button == GLUT_MIDDLE_BUTTON)

	//{

	//	if( button_state == GLUT_DOWN )

	//	{

	//		controlState = CONTROL_TRANSLATE;

	//	}

	//	else

	//	{

	//		controlState = CONTROL_NONE;

	//	}

	//}

}


void Program::MouseMove (int x, int y)
{
	/*
	const float SENSITIVITY = 0.004f;
	const double VIEWSCALE = 60.0f;

	int delta_x = x - prevMousePos_x;
	int delta_y = y - prevMousePos_y;
	prevMousePos_x = x;
	prevMousePos_y = y;

	if( controlState == CONTROL_TRANSLATE )
	{
		MoveView( delta_x * SENSITIVITY * VIEWSCALE,
			-delta_y * SENSITIVITY * VIEWSCALE);	//Moving mouse up is negative
													// so reverse it
	}*/
}




syntax highlighted by Code2HTML, v. 0.9.1