#include "base/output_file_requirements.h"
#include "persistent-data/file_utils.h"
#include "thin-provisioning/commands.h"
#include "thin-provisioning/damage_generator.h"
#include "version.h"

#include <getopt.h>
#include <unistd.h>

using namespace persistent_data;
using namespace thin_provisioning;

//----------------------------------------------------------------

namespace {
	struct flags {
		enum damage_operations {
			DAMAGE_OP_NONE,
			DAMAGE_OP_CREATE_METADATA_LEAKS,
			DAMAGE_OP_LAST
		};

		flags()
			: op(DAMAGE_OP_NONE),
			  nr_blocks(0),
			  expected_rc(0),
			  actual_rc(0) {
		}

		bool check_conformance();

		damage_operations op;
		string output;
		block_address nr_blocks;
		ref_t expected_rc;
		ref_t actual_rc;
	};

	bool flags::check_conformance() {
		if (op == DAMAGE_OP_NONE || op >= DAMAGE_OP_LAST) {
			cerr << "Invalid operation." << endl;
			return false;
		}

		if (!output.size()) {
			cerr << "No output file provided." << endl;
			return false;
		}

		if (!nr_blocks) {
			cerr << "Invalid number of blocks" << endl;
			return false;
		}

		if (op == DAMAGE_OP_CREATE_METADATA_LEAKS &&
		    expected_rc == actual_rc) {
			cerr << "Invalid reference count parameters" << endl;
			return false;
		}

		check_output_file_requirements(output);

		return true;
	}

	int generate_damage(flags const &fs) {
		block_manager::ptr bm = open_bm(fs.output, block_manager::READ_WRITE);
		damage_generator::ptr gen = damage_generator::ptr(new damage_generator(bm));

		switch (fs.op) {
		case flags::DAMAGE_OP_CREATE_METADATA_LEAKS:
			gen->create_metadata_leaks(fs.nr_blocks, fs.expected_rc, fs.actual_rc);
			break;
		default:
			break;
		}

		gen->commit();

		return 0;
	}
}

//----------------------------------------------------------------

thin_generate_damage_cmd::thin_generate_damage_cmd()
	: command("thin_generate_damage")
{
}

void
thin_generate_damage_cmd::usage(std::ostream &out) const
{
	out << "Usage: " << get_name() << " [options]\n"
	    << "Options:\n"
	    << "  {-h|--help}\n"
	    << "  {-o|--output} <output device or file>\n"
	    << "  {--create-metadata-leaks}\n"
	    << "  {--nr-blocks} <block counts>\n"
	    << "  {--expected} <expected ref-count>\n"
	    << "  {--actual} <actual ref-count>\n"
	    << "  {-V|--version}" << endl;
}

int
thin_generate_damage_cmd::run(int argc, char **argv)
{
	int c;
	struct flags fs;
	const char *shortopts = "ho:V";
	const struct option longopts[] = {
		{ "help", no_argument, NULL, 'h' },
		{ "output", required_argument, NULL, 'o' },
		{ "create-metadata-leaks", no_argument, NULL, 1 },
		{ "nr-blocks", required_argument, NULL, 1001 },
		{ "expected", required_argument, NULL, 1002 },
		{ "actual", required_argument, NULL, 1003 },
		{ "version", no_argument, NULL, 'V' },
		{ NULL, no_argument, NULL, 0 }
	};

	while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) {
		switch(c) {
		case 'h':
			usage(cout);
			return 0;
		case 'o':
			fs.output = optarg;
			break;
		case 1:
			fs.op = flags::DAMAGE_OP_CREATE_METADATA_LEAKS;
			break;
		case 1001:
			fs.nr_blocks = parse_uint64(optarg, "nr_blocks");
			break;
		case 1002:
			fs.expected_rc = parse_uint64(optarg, "expected");
			break;
		case 1003:
			fs.actual_rc = parse_uint64(optarg, "actual");
			break;
		}
	}

	if (!fs.check_conformance()) {
		usage(cerr);
		return 1;
	}

	return generate_damage(fs);
}

//----------------------------------------------------------------