/* sbt -- Simple Build Tool
 * Copyright 2008, 2009, 2010  Mark Harrah
 */
package sbt

	import java.io.{File, Writer}
	import inc.Relations

object DotGraph
{
	private def fToString(roots: Iterable[File]): (File => String) =
		(x: File) => sourceToString(roots, x)
	def sources(relations: Relations, outputDirectory: File, sourceRoots: Iterable[File])
	{
		val toString = fToString(sourceRoots)
		apply(relations, outputDirectory, toString, toString)
	}
	def packages(relations: Relations, outputDirectory: File, sourceRoots: Iterable[File])
	{
		val packageOnly = (path: String) =>
		{
			val last = path.lastIndexOf(File.separatorChar)
			val packagePath = (if(last > 0) path.substring(0, last) else path).trim
			if(packagePath.isEmpty) "" else packagePath.replace(File.separatorChar, '.')
		}
		val toString = packageOnly compose fToString(sourceRoots)
		apply(relations, outputDirectory, toString, toString)
	}
	def apply(relations: Relations, outputDir: File, sourceToString: File => String, externalToString: File => String)
	{
		def file(name: String) = new File(outputDir, name)
		IO.createDirectory(outputDir)
		generateGraph(file("int-source-deps"), "dependencies", relations.internalSrcDep, sourceToString, sourceToString)
		generateGraph(file("binary-dependencies"), "externalDependencies", relations.binaryDep, externalToString, sourceToString)
	}

	def generateGraph[Key, Value](file: File, graphName: String, relation: Relation[Key, Value],
		keyToString: Key => String, valueToString: Value => String)
	{
		import scala.collection.mutable.{HashMap, HashSet}
		val mappedGraph = new HashMap[String, HashSet[String]]
		for( (key, values) <- relation.forwardMap; keyString = keyToString(key); value <- values)
			mappedGraph.getOrElseUpdate(keyString, new HashSet[String]) += valueToString(value)

		val mappings =
				for {
					(dependsOn, dependants) <- mappedGraph.toSeq
					dependant <- dependants
					if dependant != dependsOn && !dependsOn.isEmpty && !dependant.isEmpty
				}
				yield "\"" + dependant + "\" -> \"" + dependsOn + "\""

		val lines =
			("digraph " + graphName + " {") +:
			mappings :+
			"}"
			
		IO.writeLines(file, lines)
	}
	def sourceToString(roots: Iterable[File], source: File) =
	{
		val rawName = relativized(roots, source).trim
		if(rawName.endsWith(".scala"))
			rawName.substring(0, rawName.length - ".scala".length)
		else
			rawName
	}
	private def relativized(roots: Iterable[File], path: File): String =
	{
		val relativized = roots.flatMap(root => Path.relativize(root, path))
		val shortest = (Int.MaxValue /: relativized)(_ min _.length)
		relativized.find(_.length == shortest).getOrElse(path.getName)
	}
}