#!/usr/bin/dmd -run

import std.stdio, std.file, std.string, std.regexp, std.c.stdio, std.gc;

int main(string[] argv) {

	// Disable the garbage collector to keep it from slowing the program
	// down.
	std.gc.disable;

	if (argv.length != 3) {
		writefln("Usage: %s oldchecksums newchecksums", argv[0]);
		return 1;
	}

	// Read the checksum files.

	string oldfile = strip(cast(string)read(argv[1]));
	string newfile = strip(cast(string)read(argv[2]));

	string[] oldlines = std.regexp.split(oldfile,"\n").sort;
	string[] newlines = std.regexp.split(newfile,"\n").sort;

	// Parse the data.

	// Map paths to checksums and vice versa.
	string[string] oldpaths;
	string[string] newpaths;
	string[string] oldsums;
	string[string] newsums;

	foreach(string line; oldlines) {
		line = strip(line);
		int space = std.regexp.find(line, r"\s");
		string checksum = strip(line[0..space]);
		string path = strip(line[space..$]);
		oldpaths[path] = checksum;
		oldsums[checksum] = path;
	}

	foreach(string line; newlines) {
		line = strip(line);
		int space = std.regexp.find(line, r"\s");
		string checksum = strip(line[0..space]);
		string path = strip(line[space..$]);
		newpaths[path] = checksum;
		newsums[checksum] = path;
	}

	// Correlate the data.

	string[] newfiles;
	string[] deletedfiles;
	string[] movedfiles;
	string[] modifiedfiles;

	// Look for new files.
	foreach(string path, string checksum; newpaths) {
		if(!(path in oldpaths) && !(checksum in oldsums))
			newfiles ~= path;
	}

	// Look for deleted, modified or moved files.
	foreach(string path, string checksum; oldpaths) {
		if(!(path in newpaths) && !(checksum in newsums))
			deletedfiles ~= path;
		else if(!(path in newpaths) && (checksum in newsums)) {
			string newpath = newsums[checksum];
			movedfiles ~= path ~ " → " ~ newpath;
		}
		else if((path in newpaths) && !(checksum in newsums))
			modifiedfiles ~= path;
	}

	// Present the data.

	newfiles = newfiles.sort;
	deletedfiles = deletedfiles.sort;
	movedfiles = movedfiles.sort;
	modifiedfiles = modifiedfiles.sort;

	writefln;

	writefln("Modified files:\n");
	foreach(string path; modifiedfiles)
		printf("%.*s\n",path);

	writefln;
	writefln;

	writefln("Moved files:\n");
	foreach(string path; movedfiles)
		printf("%.*s\n",path);

	writefln;
	writefln;

	writefln("Deleted files:\n");
	foreach(string path; deletedfiles)
		printf("%.*s\n",path);

	writefln;
	writefln;

	writefln("New files:\n");
	foreach(string path; newfiles)
		printf("%.*s\n",path);

	writefln;

	// Re-enabling the GC seems to do no harm, but then again, we're exiting
	// the program anyway.
	// std.gc.enable;

	return 0;
}
