package mb.statix.concurrent.solver;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.metaborg.util.log.ILogger;
import org.metaborg.util.log.LoggerUtils;
import org.metaborg.util.unit.Unit;

import com.google.common.collect.Iterables;

import io.usethesource.capsule.Set;
import mb.nabl2.terms.ITerm;
import mb.nabl2.terms.matching.Transform.T;
import mb.statix.concurrent.actors.futures.CompletableFuture;
import mb.statix.concurrent.actors.futures.IFuture;
import mb.statix.concurrent.p_raffrayi.ITypeCheckerContext;
import mb.statix.scopegraph.IScopeGraph;
import mb.statix.scopegraph.terms.Scope;
import mb.statix.solver.log.IDebugContext;
import mb.statix.spec.Spec;

public class LibraryTypeChecker extends AbstractTypeChecker<Unit> {

    private static final ILogger logger = LoggerUtils.logger(LibraryTypeChecker.class);

    private final IStatixLibrary library;

    public LibraryTypeChecker(IStatixLibrary library, Spec spec, IDebugContext debug) {
        super(spec, debug);
        this.library = library;
    }

    @Override public IFuture<Unit> run(ITypeCheckerContext<Scope, ITerm, ITerm> context, List<Scope> rootScopes) {
        final IScopeGraph.Immutable<Scope, ITerm, ITerm> scopeGraph = library.scopeGraph();
        final Set.Immutable<ITerm> labels = scopeGraph.getLabels();

        final Map<Scope, Scope> scopeMap = new HashMap<>();

        if(library.rootScopes().size() != rootScopes.size()) {
            throw new IllegalArgumentException("Number of root scopes does not match.");
        }
        for(int i = 0; i < rootScopes.size(); i++) {
            final Scope scope = library.rootScopes().get(i);
            if(scopeMap.containsKey(scope)) {
                continue;
            }
            final Scope newScope = rootScopes.get(i);
            scopeMap.put(scope, newScope);
            context.initScope(newScope, labels, false);
        }

        for(Scope scope : library.ownScopes()) {
            if(scopeMap.containsKey(scope)) {
                throw new IllegalStateException("Scope already initialized.");
            }
            final Scope newScope =
                    context.freshScope(scope.getName(), labels, scopeGraph.getData(scope).isPresent(), false);
            scopeMap.put(scope, newScope);
        }

        for(Scope scope : Iterables.concat(rootScopes, library.ownScopes())) {
            final Scope newScope = scopeMap.get(scope);

            final ITerm datum;
            if((datum = scopeGraph.getData(scope).orElse(null)) != null) {
                context.setDatum(newScope, subtituteScopes(datum, scopeMap));
            }

            for(ITerm label : labels) {
                for(Scope target : scopeGraph.getEdges(scope, label)) {
                    final Scope newTarget = scopeMap.get(target);
                    context.addEdge(newScope, label, newTarget);
                }
                context.closeEdge(newScope, label);
            }
        }

        return CompletableFuture.completedFuture(Unit.unit);
    }

    private ITerm subtituteScopes(ITerm datum, Map<Scope, Scope> substitution) {
        return T.sometd(Scope.matcher().map(s -> (ITerm) substitution.get(s))::match).apply(datum);
    }

}
@@ -1630,25 +1630,32 @@
 final Scope 
-s
+libRootS
 cope = libra
@@ -1706,33 +1706,40 @@
 Map.containsKey(
-s
+libRootS
 cope)) %7B%0A       
@@ -1787,35 +1787,36 @@
     final Scope 
-new
+root
 Scope = rootScop
@@ -1847,34 +1847,42 @@
 copeMap.put(
-s
+libRootS
 cope, 
-new
+root
 Scope);%0A    
@@ -1907,19 +1907,20 @@
 itScope(
-new
+root
 Scope, l
@@ -1959,25 +1959,28 @@
   for(Scope 
-s
+libS
 cope : libra
@@ -2033,17 +2033,20 @@
 ainsKey(
-s
+libS
 cope)) %7B
@@ -2159,28 +2159,25 @@
 final Scope 
-newS
+s
 cope =%0A     
@@ -2210,17 +2210,20 @@
 shScope(
-s
+libS
 cope.get
@@ -2253,25 +2253,28 @@
 aph.getData(
-s
+libS
 cope).isPres
@@ -2317,19 +2317,19 @@
 put(
-s
+libS
 cope, 
-newS
+s
 cope
@@ -2356,25 +2356,28 @@
   for(Scope 
-s
+libS
 cope : Itera
@@ -2388,16 +2388,24 @@
 .concat(
+library.
 rootScop
@@ -2406,16 +2406,18 @@
 otScopes
+()
 , librar
@@ -2458,20 +2458,17 @@
 l Scope 
-newS
+s
 cope = s
@@ -2479,17 +2479,20 @@
 Map.get(
-s
+libS
 cope);%0A%0A
@@ -2565,17 +2565,20 @@
 getData(
-s
+libS
 cope).or
@@ -2633,20 +2633,17 @@
 etDatum(
-newS
+s
 cope, su
@@ -2756,17 +2756,20 @@
 r(Scope 
-t
+libT
 arget : 
@@ -2788,17 +2788,20 @@
 etEdges(
-s
+libS
 cope, la
@@ -2840,20 +2840,17 @@
 l Scope 
-newT
+t
 arget = 
@@ -2862,17 +2862,20 @@
 Map.get(
-t
+libT
 arget);%0A
@@ -2906,28 +2906,25 @@
 ext.addEdge(
-newS
+s
 cope, label,
@@ -2928,12 +2928,9 @@
 el, 
-newT
+t
 arge
@@ -2989,12 +2989,9 @@
 dge(
-newS
+s
 cope

Fix scope mapping.