/*
Copyright 2010-2011 SourceGear, LLC

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

//	Import todo.txt items to Veracity.
//  Paul Roub <paul.roub@sourcegear.com>
//  Questions? Join the Veracity mailing list at:
//  http://sourcegear.com/veracity/mailinglist.html
//

var usage =
	"vscript todo2vv.js /path/of/todo.txt veracity@user.name repository\n" +
	"\n" +
	"Looks for items in todo.txt with a context of @veracity, adds any that it\n" +
	"finds to the veracity repo named by repository, and marks them done in\n" +
	"todo.txt.\n" +
	"\n" +
	"Tasks are marked as assigned-to, verified-by and reported-by the named user,\n" +
	"and stamped 'todo'.\n";

if (arguments.length != 3)
	showUsage();

var fn = arguments[0];
var username = arguments[1];
var reponame = arguments[2];
var repo = null;

if (! sg.fs.exists(fn))
	showUsage("todo list file " + fn + " does not exist");

try
{
	repo = sg.open_repo(reponame);
}
catch (e)
{
	showUsage("Unable to open repo " + reponame + ": " + e.message);
}

var userid = lookupUser(username, repo);

if (! userid)
	showUsage("No user named " + username + " found in " + reponame);

moveTodos(fn, repo, userid);

if (repo)
	repo.close();

quit(0);


function moveTodos(todofn, repo, userid)
{
	var tmpfn = sg.fs.tmpdir() + "/" + sg.gid() + "-todo.txt";
	var outlines = [];
	var backupfn = todofn + "-vvbackup";
	var ztx = null;
	var db = new zingdb(repo, sg.dagnum.WORK_ITEMS);

	try
	{
		var txt = sg.file.read(todofn);
		var lines = txt.split(/[\r\n]+/);

		for ( var i = 0; i < lines.length; ++i )
		{
			var orig = lines[i];
			var item = parsetodo(orig);

			if (item.done || (item.context != 'veracity'))
			{
				outlines.push(orig);
				continue;
			}

			var line = item.text;

			print(line);
			outlines.push("x " + orig);   // mark it done in todo.txt

			// have we started a transaction yet?
			if (! ztx)
			{
				ztx = db.begin_tx(null, userid);
			}

			var workitem = ztx.new_record('item');
			workitem.title = line.substring(0, 140);

			// if it didn't fit in the title, leave the full version in the description.
			// if we outrun that (16348 chars), you're using todo.txt in an interesting manner.
			if (line.length > 140)
				workitem.description = line;

			workitem.assignee = userid;
			workitem.reporter = userid;
			workitem.verifier = userid;
			workitem.status = 'open';

			if (item.priority)
				workitem.priority = item.priority;

			setCurrentMilestone(repo, workitem);

			for ( var stamp in item.stamps )
			{
				var stamprec = ztx.new_record('stamp');
				stamprec.name = stamp;
				stamprec.item = workitem.recid;
			}
		}

		if (ztx)
		{
			for ( i = 0; i < outlines.length; ++i )
				sg.file.append(tmpfn, outlines[i] + "\n");

			var ct = ztx.commit();
			if (ct.errors)
				throw(sg.to_json(ct));
			ztx = null;

			print("Backing up todo.txt...");
			if (sg.fs.exists(backupfn))
				sg.fs.remove(backupfn);
			print("Updating todo.txt...");
			sg.fs.move(todofn, backupfn);
			sg.fs.move(tmpfn, todofn);
			print("Done.");
		}
		else
		{
			print("Nothing to do.");
		}
	}
	catch (e)
	{
		if (ztx)
			ztx.abort();

		print( e.message.replace(/[\r\n].+/, '') );
	}
}


function parsetodo(line)
{
	var result = {
		stamps: { 'todo':true },
		context: null,
		priority: null,
		text: '',
		done: false
	};
	var matches;

	if (matches = line.match(/^x[ \t]+(.*)/)) // done
	{
		result.done = true;
		line = matches[1];
	}

	// make sure its context is @veracity
	matches = line.match(/(.*)@veracity\b(.*)/);

	if (matches)
	{
		result.context = 'veracity';
		line = matches[1] + matches[2];
	}

	// turn todo.txt +project tags into stamps
	while (matches = line.match(/(.*[ \t])\+([a-z0-9]+)?\b(.*)/))
	{
		line = matches[1] + matches[3];
		var stamp = matches[2];

		result.stamps[stamp] = true;
	}

	// leading "(A) ...", etc. denotes priority
	matches = line.match(/^\(([A-E])\)[ \t]*(.+)/i);

	if (matches)
	{
		line = matches[2];
		var prio = matches[1];

		if (prio.toUpperCase() == 'A')
			result.priority = 'High';
		else if (prio.toUpperCase() == 'E')
			result.priority = 'Low';
		else
			result.priority = 'Medium';
	}

	result.text = line.trim();

	return(result);
}


function lookupUser(username, repo)
{
	try
	{
		var db = new zingdb(repo, sg.dagnum.USERS);

		var qname = username.replace(/\\/g, "\\\\").replace(/'/g, "''");

		var recs = db.query('user', ['recid'], "name == '" + qname + "'", null, 1);

		if (recs && (recs.length == 1))
			return(recs[0].recid);
	}
	catch(e)
	{
		// just fail quietly
	}

	return(null);
}


// set the current milestone for this repo (if any) on the work item
//
function setCurrentMilestone(repo, workitem)
{
	var cfg = new Configuration(repo);
	var cs = cfg.get('current_sprint');

	if (cs && cs.value)
		workitem.milestone = cs.value;
}


function showUsage(msg)
{
	if (repo)
		repo.close();

	if (msg)
	{
		msg = msg.replace(/[\r\n].+/, '') + "\n";   // trim stack trace
		print(msg);
	}

	print(usage);

	quit(1);
}


