package org.metaborg.runtime.task.evaluation;

import java.util.LinkedHashSet;
import java.util.Set;

import org.metaborg.runtime.task.ITaskEngine;
import org.metaborg.runtime.task.Task;
import org.metaborg.runtime.task.collection.BidirectionalSetMultimap;
import org.metaborg.runtime.task.util.Debug;
import org.spoofax.interpreter.terms.IStrategoTerm;

public final class TaskEvaluationDebugging {
	/**
	 * Produces debug information to debug unevaluated task problems.
	 */
	public static void debugUnevaluated(ITaskEngine taskEngine, Iterable<IStrategoTerm> unevaluated,
		BidirectionalSetMultimap<IStrategoTerm, IStrategoTerm> toRuntimeDependency) {
		if(Debug.DEBUGGING) {
			// Find cycles.
			final Set<IStrategoTerm> cycle = findCycle(toRuntimeDependency, unevaluated);
			if(cycle != null) {
				System.err.println("Cycle found: " + cycle);
				for(IStrategoTerm taskID : cycle) {
					final Task task = taskEngine.getTask(taskID);
					System.err.println("	" + taskID + ": " + task + " - " + toRuntimeDependency.get(taskID));
				}
				System.err.println();
			}

			// Print out runtime dependencies.
			for(IStrategoTerm taskID : unevaluated) {
				final Task task = taskEngine.getTask(taskID);
				final Set<IStrategoTerm> dependencies = toRuntimeDependency.get(taskID);
				if(!dependencies.isEmpty()) {
					System.err.println(taskID + ": " + task + " still depends on: ");
				}
				for(IStrategoTerm dependencyID : dependencies) {
					final Task dependency = taskEngine.getTask(dependencyID);
					if(!dependency.solved()) {
						System.err.println("	" + dependencyID + ": " + dependency);
					}
				}
			}
			System.err.println();
			System.err.println();
		}
	}

	private static Set<IStrategoTerm> findCycle(
		BidirectionalSetMultimap<IStrategoTerm, IStrategoTerm> toRuntimeDependency, Iterable<IStrategoTerm> tasks) {
		// Use LinkedHashSet because it preserves order.
		return findCycle(toRuntimeDependency, tasks, new LinkedHashSet<IStrategoTerm>());
	}

	private static Set<IStrategoTerm> findCycle(
		BidirectionalSetMultimap<IStrategoTerm, IStrategoTerm> toRuntimeDependency, Iterable<IStrategoTerm> tasks,
		Set<IStrategoTerm> seen) {
		for(IStrategoTerm taskID : tasks) {
			final Set<IStrategoTerm> newSeen = new LinkedHashSet<IStrategoTerm>(seen);
			if(!newSeen.add(taskID))
				return newSeen;
			final Set<IStrategoTerm> rec = findCycle(toRuntimeDependency, toRuntimeDependency.get(taskID), newSeen);
			if(rec != null)
				return rec;
		}
		return null;
	}

	public static void debugDelayedDependecy(ITaskEngine taskEngine, IStrategoTerm taskID,
		Iterable<IStrategoTerm> dependencies) {
		final Task task = taskEngine.getTask(taskID);
		for(IStrategoTerm dependencyID : dependencies) {
			final Task dependencyTask = taskEngine.getTask(dependencyID);
			if(dependencyTask.solved()) {
				System.err.println("Task " + taskID + ": " + task + " reported a dynamic dependency on " + dependencyID
					+ ": " + dependencyTask + " but it is already solved!");
			}
		}
	}
}

@@ -348,16 +348,61 @@
 oTerm;%0A%0A
+import com.google.common.collect.Iterables;%0A%0A
 public f
@@ -2664,16 +2664,41 @@
 cies) %7B%0A
+%09%09if(Debug.DEBUGGING) %7B%0A%09
 %09%09final 
@@ -2737,16 +2737,163 @@
 askID);%0A
+%0A%09%09%09if(Iterables.size(dependencies) == 0)%0A%09%09%09%09System.err.println(%22Task %22 + taskID + %22: %22 + task + %22 reported a dynamic dependency on nothing.%22);%0A%0A%09
 %09%09for(IS
@@ -2934,24 +2934,25 @@
 ncies) %7B%0A%09%09%09
+%09
 final Task d
@@ -3004,16 +3004,17 @@
 ID);%0A%09%09%09
+%09
 if(depen
@@ -3031,24 +3031,25 @@
 solved()) %7B%0A
+%09
 %09%09%09%09System.e
@@ -3128,17 +3128,23 @@
 ncy on %22
- 
+%0A%09%09%09%09%09%09
 + depend
@@ -3149,22 +3149,17 @@
 ndencyID
-%0A%09%09%09%09%09
+ 
 + %22: %22 +
@@ -3207,16 +3207,22 @@
 ved!%22);%0A
+%09%09%09%09%7D%0A
 %09%09%09%7D%0A%09%09%7D

Print error if delaying a task with 0 dependencies.