package ua.ansymo.hintco

import java.util.Deque
import java.util.List
import java.util.Set
import org.eclipse.core.runtime.Assert
import java.util.HashSet

class ModelQuery {
	
	def static precedes(HintConfiguration cs, String srcId, String trgId){
		val nSrc = findIded(srcId, cs) as PrecendenceNode
		Assert.isTrue(nSrc !== null)
		val nTrg = findIded(trgId, cs) as PrecendenceNode 
		Assert.isTrue(nTrg !== null)
		nSrc.precedes.contains(nTrg)
	}
	
	def static boolean recPathExists(PrecendenceNode src,
						PrecendenceNode trg, 
						ConstraintsStack constraints,
						Set<PrecendenceNode> visited, 
						HintConfiguration cs){
		/*
		 * If src has been visited, return.
		 * If src has not been visited, mark it as visited, and 
		 * 		compute reachable nodes for its successors, and 
		 * 		link src to every node that is reachable from each of its successors
		 */
		
		// Base case: already visited
		if (visited.contains(src)) {
			return false
		}
		
		visited.add(src)
		
		var found = false
		
		// Recursion step
		try {
			for (suc : src.precedes) {
				// This is our goal, so no point continuing recursive execution
				if (suc === trg){
					throw new InterruptedException() 
				}
				if (recPathExists(suc, trg, constraints, visited, cs)) {
					throw new InterruptedException() 
				}
			}	
		} catch (InterruptedException e) {
			// found trg
			found = true
		} 
		
		// Optimization: store reachability results in the graph. Not done because it clutters the resulting models (bad for debugging).
		// link src to every to every node that is reachable from each of its successors
		// Use intermediate list to avoid concurrent modification exception
//		val toAdd = newLinkedList()
//		for (suc : src.precedes) {
//			for (sucsuc : suc.precedes) {
//				// check if constraint already exists.
//				if (!constraints.stackContains(src, sucsuc) && 
//				  		!src.precedes.contains(sucsuc)) {
//				  	toAdd.add(sucsuc)
//				}
//			}
//		}
//		for (sucsuc : toAdd) {
//			constraints.safeAdd(src, sucsuc)
//			src.precedes.add(sucsuc)
//		}
		
		return found
	}
	
	def static findRootScenario(Deque<VariantDiagram> ns){
		// Locate scenario where computation order should be generated. Search ns and it's parents for a CandidateScenario alternative.
		val scenarios = ns.filter[n | n.alternative instanceof RootCandidateScenario]
		Assert.isTrue(1 == scenarios.size)
		val scenario = scenarios.head.alternative as RootCandidateScenario
		return scenario
	}
	
	def static findIded(String id, HintConfiguration cs){
		var IDed res = null
		
		for (o : cs.eAllContents.filter(IDed).toIterable) {
			if (((o instanceof UnitInstance) || (o instanceof Scenario) || (o instanceof PortInstance)) && (o.identifier == id)){
				return o
			}
		}
		Assert.isTrue(false, "Id not found.")
		return res
	}
	
	def static comesBefore(String a, String b, List<PrecendenceNode> nodes, HintConfiguration cs) {
		val nA = findIded(a, cs) as PrecendenceNode
		val nB = findIded(b, cs) as PrecendenceNode
		val indexList = nodes.indexed
		val indexOfA = indexList.findFirst([p | p.value===nA]).key
		val indexOfB = indexList.findFirst([p | p.value===nB]).key
		indexOfA <= indexOfB
	}
	
	def static findLeastAncestorScenarioTriple(PortInstance p1, PortInstance p2) {
		Assert.isTrue(p1 !== p2 && p1.unit !== p2.unit)
		val ancestors1 = p1.unit.ancestorsTopDown()
		val ancestors2 = p2.unit.ancestorsTopDown()
		var resScenario = ancestors1.head
		Assert.isTrue(ancestors1.head === ancestors2.head)
		while (ancestors1.head === ancestors2.head && (ancestors1.head !== null)){
			resScenario = ancestors1.pop
			ancestors2.pop
		}
		
		val unitContainingP1 = findLeastAncestorUnitContainingPort(p1, ancestors1.head)
		val unitContainingP2 = findLeastAncestorUnitContainingPort(p2, ancestors2.head)
		
		#[resScenario, unitContainingP1, unitContainingP2]
	}
	
	def static findLeastAncestorUnitContainingPort(PortInstance p, Scenario s) {
		var UnitInstance resUnit = null
		if (s===null){
			resUnit = p.unit
		} else {
			Assert.isTrue(s instanceof HierarchicalCosimUnit)
			resUnit = s as UnitInstance
		}
		resUnit
	}
	
	def static Deque<Scenario> ancestorsTopDown(UnitInstance u){
		val res = newLinkedList
		val parentScenario = u.scenario
		if (parentScenario instanceof UnitInstance){
			res.addAll(parentScenario.ancestorsTopDown())	
		}
		res.add(parentScenario)
		res
	}
	
	def static pathExists(PrecendenceNode src,
						PrecendenceNode trg, 
						ConstraintsStack constraints,
						HintConfiguration cs) {
		recPathExists(src, trg, constraints, new HashSet(), cs)
	}
	
}
