int main()
#endif

{
    // freopen("C:\\touch_plus_software_log.txt", "w", stdout);
	console_log_ipc = &ipc;	
	
#ifdef _WIN32
	char buffer[MAX_PATH];
    GetModuleFileName(NULL, buffer, MAX_PATH);
    string::size_type pos = string(buffer).find_last_of("\\/");
    executable_path = string(buffer).substr(0, pos);

#elif __APPLE__
    char path_buffer[1024];
    uint32_t path_size = sizeof(path_buffer);
    _NSGetExecutablePath(path_buffer, &path_size);
    string path_str(path_buffer);
    executable_path = path_str.substr(0, path_str.find_last_of("/"));
#endif

	data_path = executable_path + slash + "userdata";
	settings_file_path = data_path + slash + "settings.nrocinunerrad";
	ipc_path = executable_path + slash + "ipc";

	if (!directory_exists(ipc_path))
		create_directory(ipc_path);
	else
		delete_all_files(ipc_path);

	if (file_exists(executable_path + "/lock"))
		delete_file(executable_path + "/lock");

	Settings settings;
    
	if (!file_exists(settings_file_path))
	{
		settings.launch_on_startup = "1";
		settings.power_saving_mode = "0";
		settings.check_for_updates = "1";
		settings.touch_control = "1";
		settings.table_mode = "0";
		settings.detect_interaction_plane = "0";
		settings.click_height = "5";

		if (!directory_exists(data_path))
			create_directory(data_path);
		
		ofstream settings_ofs(settings_file_path, ios::binary);
		settings_ofs.write((char*)&settings, sizeof(settings));

		console_log("settings file created");
	}
	else
	{
		ifstream ifs(settings_file_path, ios::binary);
		ifs.read((char*)&settings, sizeof(settings));

		console_log("settings file loaded");
	}

	IPC* ipc_ptr = &ipc;
	Settings* settings_ptr = &settings;
	ipc.map_function("get toggles", [ipc_ptr, settings_ptr](const string message_body)
	{
		string response = "";
		response += settings_ptr->launch_on_startup
				 +  settings_ptr->power_saving_mode
				 +  settings_ptr->check_for_updates
				 +  settings_ptr->touch_control
				 +  settings_ptr->table_mode
				 +  settings_ptr->detect_interaction_plane
				 +  settings_ptr->click_height;

		ipc_ptr->send_message("menu_plus", "get toggles", response);
	});

	ipc.map_function("set toggle", [ipc_ptr, settings_ptr](const string message_body)
	{
		const string toggle_name = message_body.substr(0, message_body.size() - 1);
		const string toggle_value = message_body.substr(message_body.size() - 1, message_body.size());

		if (toggle_name == "launch_on_startup")
			settings_ptr->launch_on_startup = toggle_value;

		else if (toggle_name == "power_saving_mode")
			settings_ptr->power_saving_mode = toggle_value;

		else if (toggle_name == "check_for_updates")
			settings_ptr->check_for_updates = toggle_value;

		else if (toggle_name == "touch_control")
			settings_ptr->touch_control = toggle_value;

		else if (toggle_name == "table_mode")
			settings_ptr->table_mode = toggle_value;

		else if (toggle_name == "detect_interaction_plane")
			settings_ptr->detect_interaction_plane = toggle_value;

		else if (toggle_name == "click_height")
		{
			settings_ptr->click_height = toggle_value;
			console_log(toggle_value);
		}

		ofstream settings_ofs(settings_file_path, ios::binary);
		settings_ofs.write((char*)settings_ptr, sizeof(*settings_ptr));

		ipc_ptr->send_message("menu_plus", "set toggle", "");
		ipc_ptr->send_message("track_plus", "load settings", "");
	});

	ipc.map_function("exit", [](const string message_body)
	{
		block_guardian = true;
		ipc.send_message("everyone", "exit", "");
		ipc.clear();
		exit(0);
	});
    
	thread guardian_thread(guardian_thread_function);
	thread ipc_thread(ipc_thread_function);

#ifdef _WIN32
	while (true)
	{
		if (settings.launch_on_startup == "1" && !file_exists(get_startup_folder_path() + slash + "Touch+ Software.lnk"))
			create_shortcut(executable_path + slash + "daemon_plus" + extension0,
							get_startup_folder_path() + slash + "Touch+ Software.lnk",
							executable_path);

		else if (settings.launch_on_startup != "1" && file_exists(get_startup_folder_path() + slash + "Touch+ Software.lnk"))
			delete_file(get_startup_folder_path() + slash + "Touch+ Software.lnk");

		Sleep(500);
	}
	
#elif __APPLE__
    //todo: port to OSX
#endif
    
    while (true)
        Sleep(1000);

	return 0;
}
void Reprojector::load(IPC& ipc, bool flipped)
{
	bool serial_first_non_zero = false;
	string serial = "";

	for (int i = 4; i < 10; ++i)
	{
		string str_temp = "";
		str_temp += serial_number[i];

		if (!serial_first_non_zero && str_temp != "0")
			serial_first_non_zero = true;

		if (serial_first_non_zero)
			serial += serial_number[i];
	}

	bool has_complete_calib_data = false;

	if (directory_exists(data_path_current_module))
		if (file_exists(data_path_current_module + "\\0.jpg"))
			if (file_exists(data_path_current_module + "\\1.jpg"))
				if (file_exists(data_path_current_module + "\\stereoCalibData.txt"))
					if (file_exists(data_path_current_module + "\\rect0.txt"))
						if (file_exists(data_path_current_module + "\\rect1.txt"))
							has_complete_calib_data = true;

	if (!has_complete_calib_data)
	{
		static bool block_thread = true;
		ipc.send_message("menu_plus", "show window", "");
		ipc.get_response("menu_plus", "show download", "", [](const string message_body)
		{
			COUT << "unblock" << endl;
			block_thread = false;
		});
		
		while (block_thread)
		{
			ipc.update();
			Sleep(100);
		}

        create_directory(data_path);
        create_directory(data_path_current_module);

		copy_file(executable_path + "\\rectifier.exe", data_path_current_module + "\\rectifier.exe");
		copy_file(executable_path + "\\opencv_core249.dll", data_path_current_module + "\\opencv_core249.dll");
		copy_file(executable_path + "\\opencv_highgui249.dll", data_path_current_module + "\\opencv_highgui249.dll");
		copy_file(executable_path + "\\opencv_imgproc249.dll", data_path_current_module + "\\opencv_imgproc249.dll");
		copy_file(executable_path + "\\opencv_calib3d249.dll", data_path_current_module + "\\opencv_calib3d249.dll");
		copy_file(executable_path + "\\opencv_flann249.dll", data_path_current_module + "\\opencv_flann249.dll");
		copy_file(executable_path + "\\opencv_features2d249.dll", data_path_current_module + "\\opencv_features2d249.dll");

		//http://s3-us-west-2.amazonaws.com/ractiv.com/data/
		//http://d2i9bzz66ghms6.cloudfront.net/data/

		string param0 = "http://s3-us-west-2.amazonaws.com/ractiv.com/data/" + serial + "/0.jpg";
		string param1 = data_path_current_module + "\\0.jpg";

		string* serial_ptr = &serial;
		IPC* ipc_ptr = &ipc;
		ipc.get_response("menu_plus", "download", param0 + "`" + param1, [serial_ptr, ipc_ptr](const string message_body)
		{
			if (message_body == "false")
				ipc_ptr->send_message("daemon_plus", "exit", "");
			else
			{
				string param0 = "http://s3-us-west-2.amazonaws.com/ractiv.com/data/" + *serial_ptr + "/1.jpg";
				string param1 = data_path_current_module + "\\1.jpg";

				ipc_ptr->get_response("menu_plus", "download", param0 + "`" + param1, [serial_ptr, ipc_ptr](const string message_body)
				{
					if (message_body == "false")
						ipc_ptr->send_message("daemon_plus", "exit", "");
					else
					{
						string param0 = "http://s3-us-west-2.amazonaws.com/ractiv.com/data/" + *serial_ptr + "/stereoCalibData.txt";
						string param1 = data_path_current_module + "\\stereoCalibData.txt";

						ipc_ptr->get_response("menu_plus", "download", param0 + "`" + param1, [serial_ptr, ipc_ptr](const string message_body)
						{
							if (message_body == "false")
								ipc_ptr->send_message("daemon_plus", "exit", "");
							else
							{
								bool has_complete_calib_data = false;
								while (!has_complete_calib_data)
								{
									system(("cd " + cmd_quote + data_path_current_module + cmd_quote + " && rectifier.exe").c_str());

									if (directory_exists(data_path_current_module))
										if (file_exists(data_path_current_module + "\\0.jpg"))
											if (file_exists(data_path_current_module + "\\1.jpg"))
												if (file_exists(data_path_current_module + "\\stereoCalibData.txt"))
													if (file_exists(data_path_current_module + "\\rect0.txt"))
														if (file_exists(data_path_current_module + "\\rect1.txt"))
															has_complete_calib_data = true;
								}

								block_thread = false;
							}
						});
					}
				});
			}
		});

		block_thread = true;

		while (block_thread)
		{
			ipc.update();
			Sleep(100);
		}
	}

	ifstream file_stereo_calib_data(data_path_current_module + "\\stereoCalibData.txt");

	bool is_number_new = false;
	bool is_number_old = false;

	int block_count = 0;
	int block[4];

	vector<Point> disparity_data;

	string str_num_temp = "";
	string disparities_string = "";
	
	while (getline(file_stereo_calib_data, disparities_string))
	{
		const int i_max = disparities_string.length();
		for (int i = 0; i < i_max; ++i)
		{
			string str_temp = "";
			str_temp += disparities_string[i];

			if (str_temp != "," && str_temp != ";")
				is_number_new = true;
			else
				is_number_new = false;

			if (is_number_new)
			{
				if (!is_number_old)
					str_num_temp = str_temp;
				else
					str_num_temp += str_temp;
			}
			else if (is_number_old)
			{
				block[block_count] = stoi(str_num_temp);
				++block_count;
			}

			if (block_count == 3)
			{
				bool found = false;

				for (int a = 0; a < disparity_data.size(); ++a)
				{
					if (disparity_data[a].x == block[0])
					{
						found = true;
						disparity_data[a].y = (disparity_data[a].y + abs(block[1] - block[2])) / 2;
					}
					else if (disparity_data[a].y == abs(block[1] - block[2]))
					{
						found = true;
						disparity_data[a].x = min(disparity_data[a].x, block[0]);
					}
				}
				if (!found)
					disparity_data.push_back(Point(block[0], abs(block[1] - block[2])));

				block_count = 0;
			}

			is_number_old = is_number_new;
		}
	}
	sort(disparity_data.begin(), disparity_data.end(), compare_point_x());

	double *t, *y;

	t = new double[disparity_data.size()];
	y = new double[disparity_data.size()];

	for (unsigned int a = 0; a < disparity_data.size(); a++)
	{
		t[a] = (double)(disparity_data[a].y);
		y[a] = (double)(disparity_data[a].x);
	}
	CCurveFitting cf;
	cf.curve_fitting4(t, disparity_data.size(), y, &a_out, &b_out, &c_out, &d_out);

	delete []t;
	delete []y;

	ifstream file0(data_path_current_module + "\\rect0.txt");
	is_number_new = false;
	is_number_old = false;
	block_count = 0;

	rect_mat0 = new Point*[WIDTH_LARGE];
	for (int i = 0; i < WIDTH_LARGE; ++i)
		rect_mat0[i] = new Point[HEIGHT_LARGE];

	string rect0_string = "";
	while (getline(file0, rect0_string))
	{
		const int i_max = rect0_string.length();
		for (int i = 0; i < i_max; ++i)
		{
			string str_temp = "";
			str_temp += rect0_string[i];

			if (str_temp != " " && str_temp != "," && str_temp != ";")
				is_number_new = true;
			else
				is_number_new = false;

			if (is_number_new)
			{
				if (!is_number_old)
					str_num_temp = str_temp;
				else
					str_num_temp += str_temp;
			}
			else if (is_number_old)
			{
				block[block_count] = stoi(str_num_temp);
				++block_count;
			}
			if (block_count == 4)
			{
				if (!flipped)
					rect_mat0[block[0]][block[1]] = Point(block[2], block[3]);
				else
					rect_mat0[block[0]][HEIGHT_LARGE_MINUS - block[1]] = Point(block[2], block[3]);

				block_count = 0;
			}
			is_number_old = is_number_new;
		}
	}
	ifstream file1(data_path_current_module + "\\rect1.txt");
	is_number_new = false;
	is_number_old = false;
	block_count = 0;

	rect_mat1 = new Point*[WIDTH_LARGE];
	for (int i = 0; i < WIDTH_LARGE; ++i)
		rect_mat1[i] = new Point[HEIGHT_LARGE];

	string rect1_string = "";
	while (getline(file1, rect1_string))
	{
		const int i_max = rect1_string.length();
		for (int i = 0; i < i_max; ++i)
		{
			string str_temp = "";
			str_temp += rect1_string[i];

			if (str_temp != " " && str_temp != "," && str_temp != ";")
				is_number_new = true;
			else
				is_number_new = false;

			if (is_number_new)
			{
				if (!is_number_old)
					str_num_temp = str_temp;
				else
					str_num_temp += str_temp;
			}
			else if (is_number_old)
			{
				block[block_count] = stoi(str_num_temp);
				++block_count;
			}
			if (block_count == 4)
			{
				if (!flipped)
					rect_mat1[block[0]][block[1]] = Point(block[2], block[3]);
				else
					rect_mat1[block[0]][HEIGHT_LARGE_MINUS - block[1]] = Point(block[2], block[3]);

				block_count = 0;
			}
			is_number_old = is_number_new;
		}
	}
}