use core:io;
use markdown;
use lang:bs:macro;

// Options when generating documentation.
class Options {
	// Title of the documentation as a whole.
	Str title;

	// Theme to use for the generated HTML.
	Str theme;

	// Root of the input markdown tree.
	Url root;

	// Output location of the generated HTML.
	Url output;

	// Remove numbers from the file names in the final output?
	Bool stripNumbers;

	// Remove the output directory before generating documentation?
	Bool clearOutput;

	// No progress output?
	Bool noProgress;

	// Additional options passed to the theme.
	Str->Str themeOptions;

	// Create an empty options-set, indented to be filled in later.
	init() {
		init {}
	}

	// Create, specify the most common parameters.
	init(Str title, Str theme, Url root, Url output) {
		init {
			title = title;
			theme = theme;
			root = root;
			output = output;
			stripNumbers = true;
			clearOutput = false;
			noProgress = false;
		}
	}

	// Apply an option from the command-line.
	void applyOption(Str option) {
		var eq = option.find('=');
		Str key = option.cut(option.begin, eq);
		Str val = option.cut(eq + 1);

		if (key == "--clear") {
			clearOutput = true;
		} else if (key == "-q" | key == "--quiet") {
			noProgress = true;
		} else if (key == "-n" | key == "--numbers") {
			stripNumbers = false;
		} else if (key.startsWith("--")) {
			themeOptions.put(key.cut(key.begin + 2), val);
		}
	}

	// Apply options from the array. Returns the first non-processed index.
	Nat applyOptions(Str[] options, Nat start) {
		while (start < options.count) {
			if (!options[start].startsWith("-"))
				break;
			applyOption(options[start]);
			start++;
		}
		return start;
	}
}

// Generate documentation programmatically. The `options` object stores all options for the
// generation.
void generate(Options options) {
	Theme theme = loadTheme(options.theme);

	{
		Str->Str themeOptions = options.themeOptions.clone;
		theme.options(themeOptions);
		for (k, v in themeOptions) {
			print("WARNING: Unknown option: --${k}");
		}
	}


	if (!options.noProgress)
		print("Loading documentation from ${options.root}...");

	Moment loadStart;

	Tree tree(options.title, options.root);
	tree.config.stripNumbers = options.stripNumbers;
	tree.load();

	Moment loadEnd;

	if (options.clearOutput) {
		if (!options.noProgress)
			print("Removing contents of ${options.output}...");
		deleteTree(options.output);
	}

	if (!options.noProgress)
		print("Generating output to ${options.output}...");

	Moment outStart;

	tree.output(theme, options.output);

	Moment outEnd;

	if (!options.noProgress) {
		print("Done! Loaded sources in: ${loadEnd - loadStart}, outputted HTML in: ${outEnd - outStart}");
	}
}

// Generate documentation programmatically. Wrapper for the overload that accepts an `Options`
// parameter with explicit parameters. This overload does, however, not allow specifying additional
// options to the documentation.
void generate(Str title, Str theme, Url root, Url output) {
	generate(Options(title, theme, root, output));
}

// Print help.
private void printHelp() {
	print("Usage: <title> <theme> <input> <output>");
	print("--quiet, -q - no output");
	print("--clear     - clear <output> before generating new documentation");
	print("--numbers   - keep numbers in output file names");
}

// Main entry point from the command-line.
Int generate() {
	Options options;
	var args = argv;
	Nat pos = options.applyOptions(args, 0);
	if (pos >= args.count) {
		printHelp();
		return 1;
	}
	options.title = args[pos++];

	pos = options.applyOptions(args, pos);
	if (pos >= args.count) {
		printHelp();
		return 1;
	}
	options.theme = args[pos++];

	pos = options.applyOptions(args, pos);
	if (pos >= args.count) {
		printHelp();
		return 1;
	}
	options.root = parsePath(args[pos++]).makeAbsolute(cwdUrl);

	pos = options.applyOptions(args, pos);
	if (pos >= args.count) {
		printHelp();
		return 1;
	}
	options.output = parsePath(args[pos++]).makeAbsolute(cwdUrl);

	pos = options.applyOptions(args, pos);

	if (pos != args.count) {
		printHelp();
		return 1;
	}

	generate(options);
	return 0;
}

// Convenience function to generate the Storm documentation.
Int generateStormDoc() {
	var root = core:lang:rootPkg;
	if (url = root.url) {
		var docRoot = url / ".." / "doc";
		Options options("Storm", "storm", docRoot, Url());

		var args = argv;
		Nat pos = options.applyOptions(args, 0);
		if (pos >= args.count) {
			print("Error: Expected an output path!");
			return 1;
		}
		options.output = parsePath(args[pos++]).makeAbsolute(cwdUrl);

		pos = options.applyOptions(args, pos);
		if (pos != args.count) {
			print("Error: Too many parameters!");
			return 1;
		}

		generate(options);
		return 0;
	}
	return 2;
}
