#pragma warning( disable : 4244)
#include <omp.h>

#include <iostream>
#include <string>
#include "precompile.h"

//----------Headers--------------//
#include "headers\global_headers.h"
#include "headers\math_headers.h"
#include "headers\opengl_headers.h"
//----------Framework--------------//
#include "fps.h"
#include "timer_wrapper.h"
#include "stb_image_write.h"
#include "glsl_wrapper.h"
#include "anttweakbar_wrapper.h"
#include "camera.h"
#include "selection_tool.h"
#ifdef ENABLE_MATLAB_DEBUGGING
#include "matlab_debugger.h"
#endif
//----------Mesh--------------//
#include "mesh.h"
//----------Core--------------//
#include "simulation.h"

//----------Project Key Globals--------------//
AntTweakBarWrapper* g_config_bar;
Camera* g_camera;
RenderWrapper* g_renderer;
SelectionTool* g_selection_tool;
Mesh* g_mesh = NULL;
Sim * g_simulation;
#ifdef ENABLE_MATLAB_DEBUGGING
MatlabDebugger * g_debugger;
#endif

//----------Global Parameters----------------//
int g_screen_width = DEFAULT_SCREEN_WIDTH;
int g_screen_height = DEFAULT_SCREEN_HEIGHT;
char g_obj_file_path[255];
char g_mesh_file_path[255];
float g_mesh_scaling;
glm::vec3 g_handle_color;
bool g_random_handle_color;

//----------State Control--------------------//
bool g_only_show_sim = false;
bool g_show_handle = true;
bool g_camera_mode = MODE_2D;
bool g_record = false;
bool g_animation_mode = false;
bool g_pause = true;
int g_mesh_color_type = 0;
bool g_show_simulation_color = false;
bool g_show_wireframe = false;
int  g_wireframe_linewidth = 1;
bool g_show_w11 = false;
bool g_show_detw = false;
bool g_show_detf = false;
bool g_show_texture = false;
bool g_texture_load_succeed = false;
bool g_mouse_down = false;
bool g_start_nudge = false;

//----------Mouse Control--------------------//
int g_mouse_old_x, g_mouse_old_y;
int g_mouse_wheel_pos;
unsigned char g_button_mask = 0x00;

//----------Frame Rate/Frame Number----------//
mmc::FpsTracker g_fps_tracker;
int g_max_fps = 30;
int g_timestep = 1000 / g_max_fps;

//----------Recording Related----------------//
bool g_recording_limit = false;
int g_current_frame = 0;
int g_total_frame = 0;

//----------glut function handlers-----------//
void resize(int, int);
void timeout(int);
void display(void);
void key_press(unsigned char, int, int);
void mouse_click(int, int, int, int);
void mouse_motion(int, int);
void mouse_wheel(int, int, int, int);
void mouse_over(int, int);

//----------anttweakbar handlers----------//
void TW_CALL reset_simulation(void*);
void TW_CALL step_through(void*);
//void TW_CALL update_starting_point(void*);
//void TW_CALL update_starting_point_phase_I(void*);
void TW_CALL reset_camera(void*);
void TW_CALL set_handle(void*);
void TW_CALL save_weights(void*);
void TW_CALL load_weights(void*);
void TW_CALL compute_weights(void*);
void TW_CALL matlab_reset_current_data(void*);
void TW_CALL matlab_reset_all_data(void*);
void TW_CALL matlab_new_data(void*);
void TW_CALL matlab_export_data(void*);
void TW_CALL matlab_plot(void*);
void TW_CALL matlab_plot_all(void*);

//----------other utility functions----------//
void init(void);
void cleanup(void);
void draw_overlay(void);
void grab_screen(void);
void grab_screen(char* filename);

int main(int argc, char ** argv)
{
	srand (time(NULL));

	// gl init
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
	glutCreateWindow("Noninversion Skinning T.L.");
	glEnable(GL_DEPTH_TEST);
	glEnable(GL_BLEND);
	glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
	glDepthFunc(GL_LEQUAL);
	glutInitWindowSize(g_screen_width, g_screen_height);
	glViewport(0, 0, g_screen_width, g_screen_height);

	// user init
	init();
	glutReshapeWindow(g_screen_width, g_screen_height);

	// bind function callbacks
	glutDisplayFunc(display);
	glutTimerFunc(g_timestep, timeout, g_timestep);
	glutReshapeFunc(resize);
	glutKeyboardFunc(key_press);
	glutMouseFunc(mouse_click);
	glutMotionFunc(mouse_motion);
	glutPassiveMotionFunc(mouse_over);
	glutMouseWheelFunc(mouse_wheel);
	glutCloseFunc(cleanup);
	glutIdleFunc(display);

	glutMainLoop();

	return 0;
}

void resize(int width, int height) {
	g_screen_width = width;
	g_screen_height = height;
	//set the viewport, more boilerplate
	glViewport(0, 0, width, height);
	g_camera->ResizeWindow(width, height);
	g_config_bar->ChangeTwBarWindowSize(g_screen_width, g_screen_height);

	glutPostRedisplay();
}

void timeout(int value)
{
	glutTimerFunc(g_timestep, timeout, g_timestep);
	// keep track of time
	g_fps_tracker.timestamp();

	// ant tweak bar update
	int atb_feed_back = g_config_bar->Update();
	if (atb_feed_back&ATB_RESHAPE_WINDOW)
	{
		glutReshapeWindow(g_screen_width, g_screen_height);
	}
	if (atb_feed_back&ATB_CHANGE_CAMERA_MODE)
	{
		reset_simulation(NULL);
		g_camera->ForceUpdate();
	}
	if (atb_feed_back&ATB_CHANGE_METHOD)
	{
		g_simulation->NeedUpdate();
		g_simulation->ChangeMethod();
#ifdef ENABLE_MATLAB_DEBUGGING
		g_debugger->ResetCurrent();
#endif
	}
#ifdef ENABLE_MATLAB_DEBUGGING
	if (atb_feed_back&(ATB_INIT_MATLAB))
	{
		g_debugger->Init();
		g_debugger->MatlabResetSDPMosek();
	}
#endif

	if (g_recording_limit && g_current_frame > g_total_frame)
	{
		g_pause = true;
	}

	// simulation update
	if (!g_pause) 
	{
		if (g_animation_mode)
		{
			g_simulation->AnimateHandle();
		}
		if (atb_feed_back&(ATB_FORCE_UPDATE_HANDLE))
		{
			g_simulation->UpdateHandleInfo();
		}
		// update simulation data
		g_simulation->Update();
		// copy data back from simulation
		g_mesh->ReadFromSimulation(g_simulation->V());

		// grab screen
		if (g_record) 
		{
			char cap_filename[256];
			sprintf_s(cap_filename, 256, "output/ScreenCap%04d.png", g_current_frame);
			grab_screen(cap_filename);

			// TODO: If you want to export everything to Maya, this is what you need:
			//char mesh_filename[256];
			//sprintf_s(mesh_filename, 256, "output/Mesh%04d.obj", g_current_frame);
			//g_mesh->ExportToOBJ(mesh_filename);
			//char handle_filename[256];
			//sprintf_s(handle_filename, 256, "output/Handle%04d.obj", g_current_frame);
			//g_simulation->SaveAttachmentConstraint(handle_filename);
		}

		g_current_frame ++;
	}

	glutPostRedisplay();
}

void display() {

	//Always and only do this at the start of a frame, it wipes the slate clean
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(1.0f, 1.0f, 1.0f, 1.0f);

	// aim camera
	g_renderer->SetCameraModelview(g_camera->GetViewMatrix());
	g_renderer->SetCameraProjection(g_camera->GetProjectionMatrix());

	// Draw world and cloth (using programmable shaders)
	g_renderer->ActivateShaderprog();
	if (g_show_simulation_color)
	{
		g_mesh->DrawSimulationColor(g_renderer->getVBO());
}
	switch (g_mesh_color_type)
	{
		case 0: // no fill
			break;
		case 1: // own color
			g_mesh->Draw(g_renderer->getVBO(), g_show_texture & g_texture_load_succeed);
			break;
		case 2: // skin weight
			g_simulation->ShowSkinWeights(g_mesh->SimulationColors());
			g_mesh->DrawSimulationColor(g_renderer->getVBO());
			break;
	}
	if (g_show_wireframe)
	{
		g_mesh->DrawWireFrame(g_renderer->getVBO(), false, g_wireframe_linewidth);
	}
	g_renderer->EnableUserMode();
	if (g_show_detf)
	{
		g_simulation->OutputDetFFromSolver(g_mesh->UserData());
		g_mesh->DrawUserData(g_renderer->getVBO());
	}
	if (g_show_w11)
	{
		g_simulation->OutputW11FromSolver(g_mesh->UserData());
		g_mesh->DrawUserData(g_renderer->getVBO());
	}
	if (g_show_detw)
	{
		g_simulation->OutputDetWFromSolver(g_mesh->UserData());
		g_mesh->DrawUserData(g_renderer->getVBO());
	}
	g_renderer->DisableUserMode();
	if (g_show_handle)
	{
		// draw handles
		g_simulation->Draw(g_renderer->getVBO());
	}
	
	// highlight selections
	g_selection_tool->HighlightSelectedVertices(g_renderer->getVBO());

	g_renderer->DeactivateShaderprog();

	g_selection_tool->Draw();

	if (!g_only_show_sim)
	{
		// Draw axis
		g_camera->DrawAxis();

		// Draw overlay
		draw_overlay();
	}

	// Draw tweak bar
	g_config_bar->Draw();

	glutSwapBuffers();
}

void key_press(unsigned char key, int x, int y) {
	if (!TwEventKeyboardGLUT(key, x, y))
	{
		switch(key) {
		case 32:
			g_pause = !g_pause;
			break;
		case '1':
			g_show_wireframe = true;
			g_mesh_color_type = 0;
			break;
		case '2':
			g_show_wireframe = false;
			g_mesh_color_type = 1;
			break;
		case '3':
			g_show_wireframe = true;
			g_mesh_color_type = 2;
			break;
		//case '4':
		//	g_mesh_color_type = 3;
		//	break;
		case '5':
			g_show_simulation_color = !g_show_simulation_color;
			break;
		case '6':
			g_show_detf = !g_show_detf;
			break;
		case '7':
			g_show_w11 = !g_show_w11;
			break;
		case '8':
			g_show_detw = !g_show_detw;
			break;
		case 't':
		case 'T':
			g_show_texture = !g_show_texture;
			break;
		case 'q':
		case 'Q':
			g_selection_tool->SetMode(GUI_MODE_SELECTION);
			break;
		case 'w':
		case 'W':
			g_selection_tool->SetMode(GUI_MODE_TRANSLATION);
			break;
		case 'e':
		case 'E':
			break;
		case 'r':
		case 'R':
			g_selection_tool->SetMode(GUI_MODE_ROTATION);
			break;
		case 'p':
		case 'P':
			step_through(NULL);
			break;
		case 'm':
			g_simulation->UpdateMatlabSDPSolveYalip(true);
			g_mesh->ReadFromSimulation(g_simulation->V());
			// grab screen
			if (g_record) 
			{
				char cap_filename[256];
				sprintf_s(cap_filename, 256, "output/ScreenCap%04d.png", g_current_frame);
				grab_screen(cap_filename);
			}
			g_current_frame++;
			break;
		case 'M':
			g_simulation->UpdateMatlabSDPSolveYalip(false);
			g_mesh->ReadFromSimulation(g_simulation->V());
			g_current_frame++;
			break;
		case ',':
			g_simulation->UpdateMatlabSDPSolveEpigraph();
			g_mesh->ReadFromSimulation(g_simulation->V());
			// grab screen
			if (g_record) 
			{
				char cap_filename[256];
				sprintf_s(cap_filename, 256, "output/ScreenCap%04d.png", g_current_frame);
				grab_screen(cap_filename);
			}
			g_current_frame++;
			break;
		case '.':
			g_simulation->RotateHandleToValue();
			break;
		case '?':
			g_simulation->TranslateHandleSetStepSize();
			g_simulation->RotateHandleSetStepSize();
			break;
		case '/':
			g_simulation->AnimateHandle();
			break;
		case 27: // ascii code of esc key
			cleanup();
			exit(EXIT_SUCCESS);
			break;
		case '9':
			g_show_handle = !g_show_handle;
			break;
		case '0':
			if (g_only_show_sim)
			{
				g_only_show_sim = false;
				g_config_bar->Show();
			}
			else
			{
				g_only_show_sim = true;
				g_config_bar->Hide();
			}
			break;
		case 8: // backspace
		case 127: // delete
			g_simulation->DeleteHandle();
			break;
		case 's':
		case 'S':
			g_camera->SaveCamera();
			g_simulation->SaveSimulation();
			break;
		case 'l':
		case 'L':
			g_simulation->LoadSimulation();
			g_simulation->InitSolver();
			g_simulation->UpdateHandleInfo();
			//g_simulation->LoadSimulation();
			g_mesh->ReadFromSimulation(g_simulation->V());
			g_camera->LoadCamera();
			glutPostRedisplay();
			break;
		case 'g':
		case 'G':
			{
				char cap_filename[256];
				sprintf_s(cap_filename, 256, "output/ManualScreenCap%04d.png", g_current_frame);
				grab_screen(cap_filename);
				g_mesh->WriteToFile(DEFAULT_OUTPUT_MESH_OBJ_FILE);
				std::cout << "Screenshot saved manually to '" << cap_filename << "'" << std::endl;
			}
			break;
		case 'f':
		case 'F':
			g_camera->Lookat(g_mesh);
			break;
		}
	}

	glutPostRedisplay();
}

void mouse_click(int button, int state, int x, int y)
{
	if (!TwEventMouseButtonGLUT(button, state, x, y))
	{
		switch(state)
		{
		case GLUT_DOWN:
			// left: 2^0. right: 2^2. middle: 2^1.
			g_button_mask |= 0x01 << button;
			g_mouse_old_x = x;
			g_mouse_old_y = y;
			if (glutGetModifiers() != GLUT_ACTIVE_ALT)
			{
				switch(g_selection_tool->GetMode())
				{
				case GUI_MODE_SELECTION:
					// if selection mode
					// selection box start
					g_selection_tool->SelectFirstPoint(x, y, g_screen_width, g_screen_height, g_button_mask);
					break;
				case GUI_MODE_TRANSLATION:
					g_selection_tool->TranslateRotateFirstPoint();
					g_mouse_down = true;
					break;
				case GUI_MODE_ROTATION:
					g_selection_tool->TranslateRotateFirstPoint();
					g_mouse_down = true;
					break;
				}
			}
		    break;
		case GLUT_UP:
			if (glutGetModifiers() != GLUT_ACTIVE_ALT)
			{
				g_selection_tool->SelectSecondPoint(x, y, g_screen_width, g_screen_height, true);
				switch(g_selection_tool->GetMode())
				{
				case GUI_MODE_SELECTION:
					// if selection mode
					// selection box end and selection
					g_selection_tool->SelectVertices(g_mesh->m_vertices, g_camera->GetMVP());
					break;
				case GUI_MODE_TRANSLATION:
					g_simulation->MoveHandleFinalize();
					g_mouse_down = false;
					g_start_nudge = true;
					break;
				case GUI_MODE_ROTATION:
					g_simulation->RotateHandleFinalize();
					g_mouse_down = false;
					g_start_nudge = true;
					break;
				}
			}

			unsigned char mask_not = ~g_button_mask;
			mask_not |= 0x01 << button;
			g_button_mask = ~mask_not;
			break;
		}
	}
}

void mouse_motion(int x, int y)
{
	if (!TwEventMouseMotionGLUT(x, y))
	{
		float dx, dy;
		dx = (float)(x - g_mouse_old_x);
		dy = (float)(y - g_mouse_old_y);

		if (g_button_mask & 0x01) 
		{// left button
			// alt + left button
			if (glutGetModifiers() == GLUT_ACTIVE_ALT)
			{
				g_camera->MouseChangeHeadPitch(0.2f, dx, dy);
			}
			else
			{
				g_selection_tool->SelectSecondPoint(x, y, g_screen_width, g_screen_height);
				if (g_selection_tool->GetMode() == GUI_MODE_TRANSLATION)
				{
					g_simulation->MoveHandleTemporary(g_camera->GetCurrentTargetPoint(x, y));
				}
				else if (g_selection_tool->GetMode() == GUI_MODE_ROTATION)
				{
					ScalarType theta;
					glm::vec3 axis;
					g_camera->GetCurrentRotation(x, y, axis, theta);
					g_simulation->RotateHandleTemporary(axis, theta);
				}
			}
		} 
		else if (g_button_mask & 0x02)
		{// middle button
			if (glutGetModifiers() == GLUT_ACTIVE_ALT)
			{
				g_camera->MouseChangeLookat(0.01f, dx, dy);
			}
			else
			{
				g_selection_tool->SelectSecondPoint(x, y, g_screen_width, g_screen_height);	
				if (g_selection_tool->GetMode() == GUI_MODE_TRANSLATION)
				{
					g_simulation->MoveHandleTemporary(g_camera->GetCurrentTargetPoint(x, y));
				}
				else if (g_selection_tool->GetMode() == GUI_MODE_ROTATION)
				{
					ScalarType theta;
					glm::vec3 axis;
					g_camera->GetCurrentRotation(x, y, axis, theta);
					g_simulation->RotateHandleTemporary(axis, theta);
				}
			}
		}
		else if (g_button_mask & 0x04) 
		{// right button
			if (glutGetModifiers() == GLUT_ACTIVE_ALT)
			{
				g_camera->MouseChangeDistance(0.05f, dx, dy);
			}
			else
			{
				g_selection_tool->SelectSecondPoint(x, y, g_screen_width, g_screen_height);	
				if (g_selection_tool->GetMode() == GUI_MODE_TRANSLATION)
				{
					g_simulation->MoveHandleTemporary(g_camera->GetCurrentTargetPoint(x, y));
				}
				else if (g_selection_tool->GetMode() == GUI_MODE_ROTATION)
				{
					ScalarType theta;
					glm::vec3 axis;
					g_camera->GetCurrentRotation(x, y, axis, theta);
					g_simulation->RotateHandleTemporary(axis, theta);
				}
			}
		}
		//else if (g_button_mask & 0x08)
		//{// ctrl + button
		//	g_simulation->MoveSelectedAttachmentConstraintTo(GLM2Eigen(g_camera->GetCurrentTargetPoint(x, y)));
		//}

		g_mouse_old_x = x;
		g_mouse_old_y = y;
	}
}

void mouse_wheel(int button, int dir, int x, int y)
{
	if (!TwMouseWheel(g_mouse_wheel_pos+=dir))
	{
		if (button == 0)
		{
			g_camera->MouseChangeDistance(1.0f, 0, (ScalarType)(dir));
		}
	}
}

void mouse_over(int x, int y)
{
	if (!TwEventMouseMotionGLUT(x, y))
	{
		if (g_button_mask == 0)
		{
			if (g_selection_tool->HoverSelectHandle(g_simulation, g_camera->CastRay(x, y), g_camera->GetMVP()))
			{
				g_camera->CacheLastSelectedPointLocalCoM(g_simulation->SelectedHandleLocalCoM());
				g_camera->CacheLastSelectedPointGlobalCoM(g_simulation->SelectedHandleCoM());
			}
		}
	}
}

void init()
{
	// glew init
	fprintf(stdout, "Initializing glew...\n");
	glewInit();
	if (!glewIsSupported( "GL_VERSION_2_0 " 
		"GL_ARB_pixel_buffer_object"
		)) {
			std::cerr << "ERROR: Support for necessary OpenGL extensions missing." << std::endl;
			exit(EXIT_FAILURE);
	}

	// config init
	fprintf(stdout, "Initializing AntTweakBar...\n");
	g_config_bar = new AntTweakBarWrapper();
	g_config_bar->ChangeTwBarWindowSize(g_screen_width, g_screen_height);

	// render wrapper init
	fprintf(stdout, "Initializing render wrapper...\n");
	g_renderer = new RenderWrapper();
	g_renderer->InitShader(DEFAULT_VERT_SHADER_FILE, DEFAULT_FRAG_SHADER_FILE);
	g_texture_load_succeed = g_renderer->InitTexture(DEFAULT_TEXTURE_FILE);

	// selection tool
	g_selection_tool = new SelectionTool();

	// camera init
	fprintf(stdout, "Initializing camera...\n");
	g_camera = new Camera(&g_camera_mode);

	// simulation init
	fprintf(stdout, "Initializing simulation...\n");
	g_simulation = new Sim();

	// load or get default value
	g_config_bar->LoadSettings();
	g_camera->LoadCamera();

#ifdef ENABLE_MATLAB_DEBUGGING
	// matlab init
	g_debugger = new MatlabDebugger();
#endif
	reset_simulation(NULL);
}

void cleanup() // clean up in a reverse order
{
	//if (g_scene)
	//	delete g_scene;
	if (g_config_bar) // clean config bar first because it needs to save all parameters
	{
		delete g_config_bar;
	}
	if (g_simulation)
	{
		delete g_simulation;
	}
	if (g_mesh)
	{
		delete g_mesh;
	}
	if (g_camera)
	{
		delete g_camera;
	}
	if (g_renderer)
	{
		g_renderer->CleanupShader();
		delete g_renderer;
	}
#ifdef ENABLE_MATLAB_DEBUGGING
	if (g_debugger)
	{
		delete g_debugger;
	}
#endif
}

void TW_CALL reset_simulation(void*)
{
	// save current setting before reset
	AntTweakBarWrapper::SaveSettings(g_config_bar);

	// reset frame#
	g_current_frame = 0;
	g_pause = true;

	// mesh reset
	fprintf(stdout, "Initializing mesh...\n");
	Mesh* mesh;
	if (g_camera_mode == MODE_2D)
	{
		mesh = new ObjMesh();
		mesh->ReadFromFile(g_obj_file_path, g_mesh_scaling);
	}
	else
	{
		mesh = new MeshMesh();
		mesh->ReadFromFile(g_mesh_file_path, g_mesh_scaling);
	}
	if (mesh->Info())
	{
		if (g_mesh)
			delete g_mesh;
		g_mesh = mesh;

		g_config_bar->LoadSettings();

		// reset simulation
		g_simulation->Reset(g_mesh, g_camera_mode);

		// reset selection
		g_selection_tool->Reset();

		// reset config, (config bar is recommended to reset last)
		g_config_bar->Reset();

#ifdef ENABLE_MATLAB_DEBUGGING
		// reset matlab debugger
		g_debugger->Reset();
#endif
	}
}

void TW_CALL reset_camera(void*)
{
	// reset camera
	g_camera->Reset(g_screen_width, g_screen_height);
	// reset selection
	g_selection_tool->Reset();
}

void TW_CALL step_through(void*)
{
	if(!g_pause)
	{
		g_pause = true;
	}

	// update simulation data
	g_simulation->Update();
	// copy data back from simulation
	g_mesh->ReadFromSimulation(g_simulation->V());

	g_current_frame++;
}

//void TW_CALL update_starting_point_phase_I(void*)
//{
//	if (!g_pause)
//	{
//		g_pause = true;
//	}
//
//	// update simulation data
//	g_simulation->UpdateStartingPointPhaseI();
//	// copy data back from simulation
//	g_mesh->ReadFromSimulation(g_simulation->V());
//
//	// grab screen
//	if (g_record) 
//	{
//		char cap_filename[256];
//		sprintf_s(cap_filename, 256, "output/ScreenCap%04d.png", g_current_frame);
//		grab_screen(cap_filename);
//	}
//
//	g_current_frame ++;
//}
//
//void TW_CALL update_starting_point(void*)
//{
//	if (!g_pause)
//	{
//		g_pause = true;
//	}
//
//	// update simulation data
//	g_simulation->UpdateStartingPoint();
//	// copy data back from simulation
//	g_mesh->ReadFromSimulation(g_simulation->V());
//
//	// grab screen
//	if (g_record) 
//	{
//		char cap_filename[256];
//		sprintf_s(cap_filename, 256, "output/ScreenCap%04d.png", g_current_frame);
//		grab_screen(cap_filename);
//	}
//
//	g_current_frame ++;
//}

void TW_CALL set_handle(void*)
{
	// set handle
	if (!g_selection_tool->SelectedIndices().empty())
	{
		g_simulation->NewHandle(g_selection_tool->SelectedIndices(), g_handle_color);
		// change color
		if (g_random_handle_color)
		{
			glm::vec3 hsv_color;
			hsv_color[0] = rand()/(float)RAND_MAX * 360.0f;
			hsv_color[1] = 0.95f;
			hsv_color[2] = 1.0f;
			g_handle_color = glm::rgbColor(hsv_color);
		}
	}
	g_selection_tool->Reset();
}

void TW_CALL save_weights(void*)
{
	g_simulation->SaveSkinWeights();
	g_simulation->SaveHandles();
}
void TW_CALL load_weights(void*)
{
	g_simulation->LoadHandles();
	g_simulation->LoadSkinWeights();
	g_simulation->InitSolver();
	g_simulation->UpdateHandleInfo();
}
void TW_CALL compute_weights(void*)
{
	g_simulation->ComputeSkinWeights();
	g_simulation->InitSolver();
	g_simulation->UpdateHandleInfo();
}

void TW_CALL matlab_reset_current_data(void*)
{
#ifdef ENABLE_MATLAB_DEBUGGING
	g_debugger->ResetCurrent();
#endif
}
void TW_CALL matlab_reset_all_data(void*)
{
#ifdef ENABLE_MATLAB_DEBUGGING
	g_debugger->Reset();
#endif
}
void TW_CALL matlab_new_data(void*)
{
#ifdef ENABLE_MATLAB_DEBUGGING
	g_debugger->AddNewPlotData();
#endif
}
void TW_CALL matlab_export_data(void*)
{
#ifdef ENABLE_MATLAB_DEBUGGING
	g_debugger->Export();
#endif
}

void TW_CALL matlab_plot(void*)
{
#ifdef ENABLE_MATLAB_DEBUGGING
	g_debugger->PlotDistToSolution();
#endif
}

void TW_CALL matlab_plot_all(void*)
{
#ifdef ENABLE_MATLAB_DEBUGGING
	g_debugger->PlotAll();
#endif
}

void grab_screen(void)
{
	char anim_filename[256];
	sprintf_s(anim_filename, 256, "output/Simulation%04d.png", g_current_frame);
	grab_screen(anim_filename);
}

void grab_screen(char* filename)
{
	unsigned char* bitmapData = new unsigned char[3 * g_screen_width * g_screen_height];

	for (int i=0; i < g_screen_height; i++) 
	{
		glReadPixels(0, i, g_screen_width, 1, GL_RGB, GL_UNSIGNED_BYTE, 
			bitmapData + (g_screen_width * 3 * ((g_screen_height - 1) - i)));
	}

	stbi_write_png(filename, g_screen_width, g_screen_height, 3, bitmapData, g_screen_width * 3);

	delete [] bitmapData;
}

void draw_overlay()
{
	// Draw Overlay
	glColor4d(0.0, 0.0, 0.0, 1.0);
	glPushAttrib(GL_LIGHTING_BIT);
	glDisable(GL_LIGHTING);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	gluOrtho2D(0.0, 1.0, 0.0, 1.0);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	glRasterPos2d(0.03, 0.01);

	char overlay_char_from_mesh[255] = ""; 
	g_mesh->GetMeshInfo(overlay_char_from_mesh);

	char overlay_char_from_simulation[255] = "";
	g_simulation->GetOverlayChar(overlay_char_from_simulation);

	char overlay_char_from_mesh2[255] = "";
	//ScalarType min = g_simulation->OutputMinDetWFromSolver();
	//sprintf_s(overlay_char_from_mesh2, "| minimum det(W): %e", min);
	ScalarType progress_length = g_simulation->LastStepProgress();
	sprintf_s(overlay_char_from_mesh2, "| last step progress: %e", progress_length);

	char info[1024];
	sprintf_s(info, "FPS: %3.1f | Frame#: %d | Iteration#: %u | %s %s %s", g_fps_tracker.fpsAverage(), g_current_frame, g_simulation->Iteration(), overlay_char_from_mesh, overlay_char_from_mesh2, overlay_char_from_simulation);

	for (unsigned int i = 0; i < strlen(info); i++)
	{
		glutBitmapCharacter(GLUT_BITMAP_HELVETICA_12, info[i]);
	}

	glPopAttrib();
}