#include "sandbox.h"

#include "utils.h"
#include "entry.h"
#include "disk.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/resource.h>

const Command COMMANDS[] = {
	{command_help, "help", "[command]", "Prints the help message.",
	 "Prints the help message for the given command. If no command is given, prints the help message for all commands."},
	{command_version, "version", "", "Prints the version.",
	 "Prints the version of the program."},
	{},
	{command_add_entry, "add-entry", "<entry id> [--root|-r <size>] [--backed|-b <backing id>]", "Adds an entry to the entry pool.",
	 "Adds an entry to the entry pool with the given id. The entry can either be a root entry or a backed entry. If the entry is a root entry, the size of the entry must be specified. If the entry is a backed entry, the backing id must be specified. By default, the entry is backed by the latest backing disk available."},
	{command_remove_entry, "remove-entry", "<entry id>", "Removes an entry from the entry pool.",
	 "Removes the entry with the given id from the entry pool."},
	{command_list_entries, "list-entries", "", "Lists the entries in the entry pool.",
	 "Lists the entries in the entry pool."},
	{command_clear_entries, "clear-entries", "", "Removes all the entries from the entry pool.",
	 "Removes all the entries from the entry pool."},
	{command_reset_entry, "reset-entry", "<entry id>", "Resets an entry to its initial state.",
	 "Resets the entry with the given id to its initial state. Automatic entries will be backed by the latest backing disk available."},
	{},
	{command_add_backing, "add-backing", "<entry id> [<backing id>]", "Adds a backing disk to the backing pool, from the given entry.",
	 "TODO"},
};

int main(int argc, char* argv[]) {
	if (argc < 2)
		return command_help(0, NULL);

	size_t input_length = strlen(argv[1]);

	const Command* command = NULL;
	for (int i = 0; i < ARRAY_SIZE(COMMANDS); i++) {
		if (COMMANDS[i].name == NULL)
			continue;

		// Check that the length of the input is equal or less than the length of the command name
		if (input_length > strlen(COMMANDS[i].name))
			continue;

		// Check that the input matches the command name
		if (strncmp(argv[1], COMMANDS[i].name, input_length) == 0) {
			// Check for multiple matches
			if (command != NULL) {
				log_message(LOG_LEVEL_ERROR, "Ambiguous command '%s'.", argv[1]);
				return EXIT_FAILURE;
			}

			command = &COMMANDS[i];
		}
	}

	// Check if the command is NULL (no matches)
	if (command == NULL) {
		log_message(LOG_LEVEL_ERROR, "Unknown command '%s'.", argv[1]);
		return EXIT_FAILURE;
	}

	return command->handler(argc - 2, argv + 2);
}

int command_help(int argc, char* argv[]) {
	// Check the number of arguments
	if (argc == 0) {
		fprintf(stdout, "Usage: sandbox [command] [arguments]\n\n");
		fprintf(stdout, "Commands:\n");

		for (int i = 0; i < ARRAY_SIZE(COMMANDS); i++)
			if (COMMANDS[i].name == NULL)
				printf("\n");
			else
				fprintf(stdout, "	%s %s - %s\n", COMMANDS[i].name, COMMANDS[i].arguments, COMMANDS[i].description);

		fprintf(stdout, "\nRun 'sandbox help [command]' for more information on a command.\n");
		return EXIT_SUCCESS;
	} else if (argc == 1) {
		// Find the command that matches the given name
		const Command* command = NULL;

		for (int i = 0; i < ARRAY_SIZE(COMMANDS); i++) {
			if (COMMANDS[i].name == NULL)
				continue;

			if (strcmp(argv[0], COMMANDS[i].name) == 0) {
				command = &COMMANDS[i];
				break;
			}
		}

		// Check if the command is NULL (no matches)
		if (command == NULL) {
			log_message(LOG_LEVEL_ERROR, "Unknown command '%s'.", argv[0]);
			return EXIT_FAILURE;
		}

		// Print the help message for the command
		fprintf(stdout, "Usage: sandbox %s %s\n\n", command->name, command->arguments);
		fprintf(stdout, "	%s\n", command->details);

		return EXIT_SUCCESS;
	} else {
		log_message(LOG_LEVEL_ERROR, "Too many arguments for 'help' command.");
		return EXIT_FAILURE;
	}
}

int command_version(int argc, char* argv[]) {
	fprintf(stdout, "Sandbox manager v%s\n", VERSION);
	return EXIT_SUCCESS;
}

int command_add_entry(int argc, char* argv[]) {
	if (argc < 1) {
		log_message(LOG_LEVEL_ERROR, "Missing entry id.");
		return EXIT_FAILURE;
	}

	// TODO: Parse options

	// TODO: Call add_root_entry or add_backed_entry depending on the options

	return EXIT_SUCCESS;
}

int command_remove_entry(int argc, char* argv[]) {
	if (argc < 1) {
		log_message(LOG_LEVEL_ERROR, "Missing entry id.");
		return EXIT_FAILURE;
	}

	// TODO: Call remove_entry

	return EXIT_SUCCESS;
}

int command_list_entries(int argc, char* argv[]) {
	// TODO: Call list_entries, and display the result in a nice way

	return EXIT_SUCCESS;
}

int command_clear_entries(int argc, char* argv[]) {
	// TODO: Ask for confirmation

	// TODO: Call clear_entries

	return EXIT_SUCCESS;
}

int command_reset_entry(int argc, char* argv[]) {
	if (argc < 1) {
		log_message(LOG_LEVEL_ERROR, "Missing entry id.");
		return EXIT_FAILURE;
	}

	// TODO: Call reset_entry

	return EXIT_SUCCESS;
}

int command_add_backing(int argc, char* argv[]) {
	if (argc < 1) {
		log_message(LOG_LEVEL_ERROR, "Missing entry id.");
		return EXIT_FAILURE;
	}

	// TODO: Call add_backing

	return EXIT_SUCCESS;
}