/**
 * @brief	Move a message to a new folder in the database.
 * @param	usernum		the numerical id of the user that owns the message.
 * @param	messagenum	the numerical id of the message to be moved.
 * @param	source		the numerical id of the current parent folder of the specified message.
 * @param	target		the numerical id of the folder to which the specified message will be moved.
 * @return	-1 on error, 0 if the message wasn't found, or 1 on success.
 */
int_t mail_move_message(uint64_t usernum, uint64_t messagenum, uint64_t source, uint64_t target) {

	int_t result;
	int64_t transaction;

	// QUESTION: Transaction seems unnecessary right now.
	// Begin the transaction.
	if ((transaction = tran_start()) < 0) {
		log_error("Could not start a transaction. {start = %li}", transaction);
		return -1;
	}

	// Insert a record into the database.
	if ((result = mail_db_update_message_folder(usernum, messagenum, source, target, transaction)) != 1) {
		log_pedantic("Could not move a message between folders. { mail_db_update_message_folder = %i }", result);
		tran_rollback(transaction);
		return result;
	}

	// Commit the transaction.
	if ((result = tran_commit(transaction))) {
		log_error("Could not commit message move transaction. { commit = %i }", result);
		return -1;
	}

	return 1;
}
/**
 * @brief	Store a mail message, with its meta-information in the database, and the contents persisted to disk.
 * @note	The stored message is always compressed, but only encrypted if the user's public key is suppplied.
 * @param	usernum		the numerical id of the user to which the message belongs.
 * @param	pubkey		if not NULL, a public key that will be used to encrypt the message for the intended user.
 * @param	foldernum	the folder # that will contain the message.
 * @param	status		a pointer to the status flags value for the message, which will be updated if the message is to be encrypted.
 * @param	signum		the spam signature for the message.
 * @param	sigkey		the spam key for the message.
 * @param	message		a managed string containing the raw body of the message.
 * @return	0 on failure, or the newly inserted id of the message in the database on success.
 *
 */
uint64_t mail_store_message(uint64_t usernum, stringer_t *pubkey, uint64_t foldernum, uint32_t *status, uint64_t signum, uint64_t sigkey, stringer_t *message) {

	chr_t *path;
	cryptex_t *encrypted = NULL;
	compress_t *reduced;
	uint64_t messagenum;
	int64_t transaction, ret;
	size_t write_len;
	uint8_t fflags = FMESSAGE_OPT_COMPRESSED;
	uchr_t *write_data;
	bool_t store_result;

	// Compress the message.
	if (!(reduced = compress_lzo(message))) {
		log_error("An error occurred while attempting to compress a message with %zu bytes.", st_length_get(message));
		return 0;
	}

	// Next, encrypt the message if necessary.
	if (pubkey) {
		*status |= MAIL_STATUS_ENCRYPTED;

		if (!(encrypted = ecies_encrypt(pubkey, ECIES_PUBLIC_BINARY, reduced, compress_total_length(reduced)))) {
			log_pedantic("Unable to decrypt mail message.");
			compress_free(reduced);
			return 0;
		}

		compress_free(reduced);
		write_data = (uchr_t *)encrypted;
		write_len = cryptex_total_length(encrypted);
		fflags |= FMESSAGE_OPT_ENCRYPTED;
	} else {
		write_data = (uchr_t *)reduced;
		write_len = compress_total_length(reduced);
	}

	// Begin the transaction.
	if ((transaction = tran_start()) < 0) {
		log_error("Could not start a transaction. {start = %li}", transaction);

		if (encrypted) {
			cryptex_free(encrypted);
		} else {
			compress_free(reduced);
		}

		return 0;
	}

	// Insert a record into the database.
	if ((messagenum = mail_db_insert_message(usernum, foldernum, *status, st_length_int(message), signum, sigkey, transaction)) == 0) {
		log_pedantic("Could not create a record in the database. mail_db_insert_message = 0");
		tran_rollback(transaction);

		if (encrypted) {
			cryptex_free(encrypted);
		} else {
			compress_free(reduced);
		}

		return 0;
	}

	// Now attempt to save everything to disk.
	store_result = mail_store_message_data(messagenum, fflags, write_data, write_len, &path);

	if (encrypted) {
		cryptex_free(encrypted);
	} else {
		compress_free(reduced);
	}

	// If storage failed, fail out.
	if (!store_result || !path) {
		log_pedantic("Failed to store user's message to disk.");
		tran_rollback(transaction);

		if (path) {
			unlink(path);
			ns_free(path);
		}

		return 0;
	}

	// Commit the transaction.
	if ((ret = tran_commit(transaction))) {
		log_error("Could not commit the transaction. { commit = %li }", ret);
		unlink(path);
		ns_free(path);
		return 0;
	}

	ns_free(path);
	return messagenum;
}
// QUESTION: Any reason we're not just passing around the meta_message_t * ?
uint64_t mail_copy_message(uint64_t usernum, uint64_t original, chr_t *server, uint32_t size, uint64_t foldernum, uint32_t status, uint64_t signum, uint64_t sigkey, uint64_t created) {

	int_t fd, state;
	uint64_t messagenum;
	int64_t transaction, ret;
	chr_t *origpath, *copypath;

	// Build the original message path.
	if (!(origpath = mail_message_path(original, server))) {
		log_error("Could not build the message path.");
		return 0;
	}

	// Verify the message still exists by opening the file.
	if ((fd = open(origpath, O_RDONLY)) < 0) {
		log_pedantic("Could not open a file descriptor for the message %s.", origpath);
		ns_free(origpath);
		return 0;
	}

	close(fd);

	// Begin the transaction.
	if ((transaction = tran_start()) < 0) {
		log_error("Could not start a transaction. {start = %li}", transaction);
		ns_free(origpath);
		return 0;
	}

	// Insert a record into the database.
	if (!(messagenum = mail_db_insert_duplicate_message(usernum, foldernum, status, size, signum, sigkey, created, transaction))) {
		log_pedantic("Could not create a record in the database. mail_db_insert_message = 0");
		tran_rollback(transaction);
		ns_free(origpath);
		return 0;
	}

	// Build the message path.
	if (!(copypath = mail_message_path(messagenum, NULL))) {
		log_error("Could not build the message path.");
		tran_rollback(transaction);
		ns_free(origpath);
		return 0;
	}

	// Create a hard link between the old message path and the new one.
	if ((state = link(origpath, copypath)) != 0) {

		if (mail_create_directory(messagenum, NULL)) {
			state = link(origpath, copypath);
		}

	}

	// Make sure the link was created.
	if (state != 0) {
		log_error("Could not create a hard link between two messages. link = %i", state);
		tran_rollback(transaction);
		ns_free(origpath);
		ns_free(copypath);
		return 0;
	}

	// Commit the transaction.
	if ((ret = tran_commit(transaction))) {
		log_error("Could not commit the transaction. { commit = %li }", ret);
		ns_free(origpath);
		ns_free(copypath);
		return 0;
	}

	ns_free(origpath);
	ns_free(copypath);

	return messagenum;
}
void fixedSelection(PlotRayUtils &plt, RayTracer &rayt, tf::Point &best_start, tf::Point &best_end)
{
  int index;
  double bestIG = 0;
  tf::Point tf_start;
  tf::Point tf_end;
  bestIG = 0;
  std::random_device rd;
  std::uniform_real_distribution<double> rand(0, 1);
  std::uniform_int_distribution<> int_rand(0, 3);
  Eigen::Vector3d start;
  Eigen::Vector3d end;
  double state[6] = {0.3, 0.3, 0.3, 0.5, 0.7, 0.5};
  Eigen::Matrix3d rotationC;
  rotationC << cos(state[5]), -sin(state[5]), 0,
               sin(state[5]), cos(state[5]), 0,
               0, 0, 1;
  Eigen::Matrix3d rotationB;
  rotationB << cos(state[4]), 0 , sin(state[4]),
               0, 1, 0,
               -sin(state[4]), 0, cos(state[4]);
  Eigen::Matrix3d rotationA;
  rotationA << 1, 0, 0 ,
               0, cos(state[3]), -sin(state[3]),
               0, sin(state[3]), cos(state[3]);
  Eigen::Matrix3d rotationM = rotationC * rotationB * rotationA;
  Eigen::Vector3d displaceV(state[0], state[1], state[2]);
  for(int i=0; i<500; i++){
    index = int_rand(rd);
    if (index == 0)
    {
      double y = rand(rd) * 0.31 - 0.35;
      double z = rand(rd) * 0.18 + 0.03;
      start << 2, y, z;
      end << -1, y, z;

    }
    else if (index == 1)
    {
      double x = rand(rd) * 1.1 + 0.1;
      double z = rand(rd) * 0.18 + 0.03;
      start << x, -1, z;
      end << x, 1, z;
    }
    else if (index == 2)
    {
      double x = rand(rd) * 1.1 + 0.1;
      double y = rand(rd) * 0.01 - 0.02;
      start << x, y, 1;
      end << x, y, -1;
    }
    else
    {
      double x = rand(rd) * 0.02 + 0.33;
      double y = rand(rd) * 0.2 - 0.35;
      start << x, y, 1;
      end << x, y, -1;
    }
    
    Eigen::Vector3d tran_start = rotationM * start + displaceV;
    Eigen::Vector3d tran_end = rotationM * end + displaceV;

    tf_start.setValue(tran_start(0, 0), tran_start(1, 0), tran_start(2, 0));
    tf_end.setValue(tran_end(0, 0), tran_end(1, 0), tran_end(2, 0));
    Ray measurement(tf_start, tf_end);
    // auto timer_begin = std::chrono::high_resolution_clock::now();
    double IG = rayt.getIG(measurement, 0.01, 0.002);
    // plt.plotRay(measurement);
    // plt.labelRay(measurement, IG);
    // auto timer_end = std::chrono::high_resolution_clock::now();
    // auto timer_dur = timer_end - timer_begin;
    // cout << "IG: " << IG << endl;
    // cout << "Elapsed time for ray: " << std::chrono::duration_cast<std::chrono::milliseconds>(timer_dur).count() << endl;
    // double IG = plt.getIG(start, end, 0.01, 0.002);
    if (IG > bestIG){
      bestIG = IG;
      best_start = tf_start;
      best_end = tf_end;
    }
  }
  // plt.plotCylinder(best_start, best_end, 0.01, 0.002, true);
  ROS_INFO("Ray is: %f, %f, %f.  %f, %f, %f", 
     best_start.getX(), best_start.getY(), best_start.getZ(),
     best_end.getX(), best_end.getY(), best_end.getZ());
  plt.plotRay(Ray(best_start, best_end));
}