#!/usr/bin/env dub
/+dub.sdl:
dependency "dub" path="../.."
+/

import std.algorithm, std.conv, std.format, std.path, std.range, std.stdio;
import dub.commandline;

string italic(string w)
{
	return `\fI` ~ w ~ `\fR`;
}

string bold(string w)
{
	return `\fB` ~ w ~ `\fR`;
}

string header(string heading)
{
	return ".SH " ~ heading;
}

string br(string s)
{
	return ".BR " ~ s;
}

struct Config
{
	import std.datetime;
	SysTime date;

	static Config init(){
		import std.process : environment;
		Config config;
		config.date = Clock.currTime;
		auto diffable = environment.get("DIFFABLE", "0");
		if (diffable == "1")
			config.date = SysTime(DateTime(2018, 01, 01));

		config.cwd = __FILE_FULL_PATH__.dirName;
		return config;
	}
	string cwd;
}

void writeHeader(ref File manFile, string manName, const Config config)
{
	static immutable manHeader =
`.TH %s 1 "%s" "The D Language Foundation" "The D Language Foundation"
.SH NAME`;
	manFile.writefln(manHeader, manName, config.date.toISOExtString.take(10));
}

void writeFooter(ref File manFile, string seeAlso, const Config config)
{
	static immutable manFooter =
`.SH FILES
\fIdub\&.sdl\fR, \fIdub\&.json\fR
.SH AUTHOR
Copyright (c) 1999-%s by The D Language Foundation
.SH "ONLINE DOCUMENTATION"
.UR http://code.dlang.org/docs/commandline
.UE http://code.dlang.org/docs/commandline
.SH "SEE ALSO"
%s`;
	manFile.writefln(manFooter, config.date.year, seeAlso);
}

void writeMainManFile(CommandArgs args, CommandGroup[] commands,
					  string fileName, const Config config)
{
	auto manFile = File(config.cwd.buildPath(fileName), "w");
	manFile.writeHeader("DUB", config);
	auto seeAlso = ["dmd(1)", "rdmd(1)"]
        .chain(commands.map!(a => a.commands).joiner
               .map!(cmd => format("dub-%s(1)", cmd.name)))
        .joiner(", ").to!string.bold;
	scope(exit) manFile.writeFooter(seeAlso, config);

	alias writeln = (m) => manFile.writeln(m);
	writeln(`dub \- Package and build management system for D`);
	writeln("SYNOPSIS".header);
	writeln(`.B dub
[\-\-version]
[\fICOMMAND\fR]
[\fIOPTIONS\&.\&.\&.\fR]
[\-\- [\fIAPPLICATION ARGUMENTS\&.\&.\&.\fR]]`);

	writeln("DESCRIPTION".header);
	writeln(`Manages the DUB project in the current directory\&. DUB can serve as a build
system and a package manager, automatically keeping track of project's
dependencies \- both downloading them and linking them into the application.`);

	writeln(".SH COMMANDS");
	foreach (grp; commands) {
		foreach (cmd; grp.commands) {
			writeln(".TP");
			writeln(cmd.name.bold);
			writeln(cmd.helpText.joiner("\n"));
		}
	}

	writeln("COMMON OPTIONS".header);
	args.writeArgs(manFile);
}

string highlightArguments(string args)
{
	import std.regex : regex, replaceAll;
	static auto re = regex("<([^>]*)>");
	static const reReplacement = "<%s>".format(`$1`.italic);
	return args.replaceAll(re, reReplacement);
}

void writeArgs(CommandArgs args, ref File manFile)
{
	alias write = (m) => manFile.write(m);
	foreach (arg; args.recognizedArgs)
	{
		auto names = arg.names.split("|");
		assert(names.length == 1 || names.length == 2);
		string sarg = names[0].length == 1 ? names[0] : null;
		string larg = names[0].length > 1 ? names[0] : names.length > 1 ? names[1] : null;
		write(".IP ");
		if (sarg !is null) {
			write("-%s".format(sarg));
			if (larg !is null)
				write(", ");
		}
		if (larg !is null) {
			write("--%s".format(larg));
			if (!arg.defaultValue.peek!bool)
				write("=VALUE");
		}
		manFile.writeln;
		manFile.writeln(arg.helpText.join("\n"));
	}
}

void writeManFile(Command command, const Config config)
{
	import std.uni : toUpper;

	auto args = new CommandArgs(null);
	command.prepare(args);
	string fileName = format("dub-%s.1", command.name);
	auto manFile = File(config.cwd.buildPath(fileName), "w");
	auto manName = format("DUB-%s", command.name).toUpper;
	manFile.writeHeader(manName, config);
	static immutable seeAlso = ["dmd(1)", "dub(1)"].map!bold.joiner(", ").to!string;
	scope(exit) manFile.writeFooter(seeAlso, config);

	alias writeln = (m) => manFile.writeln(m);
	writeln(`dub \- Package and build management system for D`);

	writeln("SYNOPSIS".header);
	writeln("dub %s".format(command.name).bold);
	writeln(command.argumentsPattern.highlightArguments);
	writeln(`OPTIONS\&.\&.\&.`.italic);
	if (command.acceptsAppArgs)
	{
		writeln("[-- <%s>]".format("application arguments...".italic));
	}

	writeln("DESCRIPTION".header);
	writeln(command.helpText.joiner("\n\n"));
	writeln("OPTIONS".header);
	args.writeArgs(manFile);
}

void main()
{
	Config config = Config.init;
	auto commands = getCommands();

	// main dub.1
	{
		CommonOptions options;
		auto args = new CommandArgs(null);
		options.prepare(args);
		args.writeMainManFile(commands, "dub.1", config);
	}

	// options for each specific command
	foreach (cmd; commands.map!(a => a.commands).joiner) {
		cmd.writeManFile(config);
	}
}
