package mb.nabl2.sets;

import static mb.nabl2.terms.matching.TermMatch.M;

import java.util.Optional;

import org.metaborg.util.iterators.Iterables2;

import io.usethesource.capsule.Set;
import io.usethesource.capsule.SetMultimap;
import mb.nabl2.terms.matching.TermMatch.IMatcher;

public class SetEvaluator {

    public static <T> IMatcher<ISetProducer<T>> matcher(IMatcher<ISetProducer<T>> elemMatcher) {
        // @formatter:off
        return M.<ISetProducer<T>>casesFix(m -> Iterables2.from(
            elemMatcher,
            M.appl0("EmptySet", (t) -> () -> Set.Immutable.of()),
            M.appl2("Union", m, m, (t, leftSet, rightSet) -> () -> {
                return Set.Immutable.union(leftSet.apply(), rightSet.apply());
            }),
            M.appl3("Isect", m, SetTerms.projectionMatcher(), m, (t, leftSet, proj, rightSet) -> () -> {
                SetMultimap.Immutable<Object,IElement<T>> leftProj = project(leftSet.apply(), proj);
                SetMultimap.Immutable<Object,IElement<T>> rightProj = project(rightSet.apply(), proj);
                Set.Transient<IElement<T>> result = Set.Transient.of();
                for(Object key : leftProj.keySet()) {
                    if(rightProj.containsKey(key)) {
                        result.__insertAll(leftProj.get(key));
                        result.__insertAll(rightProj.get(key));
                    }
                }
                return result.freeze();
            }),
            M.appl3("Lsect", m, SetTerms.projectionMatcher(), m, (t, leftSet, proj, rightSet) -> () -> {
                SetMultimap.Immutable<Object,IElement<T>> leftProj = project(leftSet.apply(), proj);
                SetMultimap.Immutable<Object,IElement<T>> rightProj = project(rightSet.apply(), proj);
                Set.Transient<IElement<T>> result = Set.Transient.of();
                for(Object key : leftProj.keySet()) {
                    if(rightProj.containsKey(key)) {
                        result.__insertAll(leftProj.get(key));
                    }
                }
                return result.freeze();
            }),
            M.appl3("Diff", m, SetTerms.projectionMatcher(), m, (t, leftSet, proj, rightSet) -> () -> {
                SetMultimap.Immutable<Object,IElement<T>> leftProj = project(leftSet.apply(), proj);
                SetMultimap.Immutable<Object,IElement<T>> rightProj = project(rightSet.apply(), proj);
                Set.Transient<IElement<T>> result = Set.Transient.of();
                for(Object key : leftProj.keySet()) {
                    if(!rightProj.containsKey(key)) {
                        result.__insertAll(rightProj.get(key));
                    }
                }
                return result.freeze();
            })
        ));
        // @formatter:on
    }

    public static <T> SetMultimap.Immutable<Object, IElement<T>> project(Set<IElement<T>> elems,
            Optional<String> proj) {
        SetMultimap.Transient<Object, IElement<T>> result = SetMultimap.Transient.of();
        for(IElement<T> elem : elems) {
            result.__insert(proj.map(p -> elem.project(p)).orElseGet(() -> elem.getValue()), elem);
        }
        return result.freeze();
    }

}
@@ -670,22 +670,21 @@
         
-return
+final
  Set.Imm
@@ -693,171 +693,470 @@
 able
-.union(leftSet.apply(), rightSet.apply());%0A            %7D),%0A            M.appl3(%22Isect%22, m, SetTerms.projectionMatcher(), m, (t, leftSet, proj, rightSet) -%3E () -%3E %7B
+%3CIElement%3CT%3E%3E _leftSet = leftSet.apply();%0A                final Set.Immutable%3CIElement%3CT%3E%3E _rightSet = rightSet.apply();%0A                return Set.Immutable.union(_leftSet, _rightSet);%0A            %7D),%0A            M.appl3(%22Isect%22, m, SetTerms.projectionMatcher(), m, (t, leftSet, proj, rightSet) -%3E () -%3E %7B%0A                final Set.Immutable%3CIElement%3CT%3E%3E _leftSet = leftSet.apply();%0A                final Set.Immutable%3CIElement%3CT%3E%3E _rightSet = rightSet.apply();
 %0A   
@@ -1225,39 +1225,32 @@
 j = project(
+_
 leftSet
-.apply()
 , proj);%0A   
@@ -1320,32 +1320,33 @@
 j = project(
+_
 rightSet
 .apply(), pr
@@ -1329,32 +1329,24 @@
 ct(_rightSet
-.apply()
 , proj);%0A   
@@ -1841,32 +1841,188 @@
 Set) -%3E () -%3E %7B%0A
+                final Set.Immutable%3CIElement%3CT%3E%3E _leftSet = leftSet.apply();%0A                final Set.Immutable%3CIElement%3CT%3E%3E _rightSet = rightSet.apply();%0A
                 
@@ -2078,39 +2078,32 @@
 j = project(
+_
 leftSet
-.apply()
 , proj);%0A   
@@ -2173,32 +2173,33 @@
 j = project(
+_
 rightSet
 .apply(), pr
@@ -2182,32 +2182,24 @@
 ct(_rightSet
-.apply()
 , proj);%0A   
@@ -2629,32 +2629,188 @@
 Set) -%3E () -%3E %7B%0A
+                final Set.Immutable%3CIElement%3CT%3E%3E _leftSet = leftSet.apply();%0A                final Set.Immutable%3CIElement%3CT%3E%3E _rightSet = rightSet.apply();%0A
                 
@@ -2870,31 +2870,24 @@
 project(
+_
 leftSet
-.apply()
 , proj);
@@ -2965,24 +2965,25 @@
 project(
+_
 rightSet
 .apply()
@@ -2974,24 +2974,16 @@
 rightSet
-.apply()
 , proj);
@@ -3198,36 +3198,35 @@
 ult.__insertAll(
-righ
+lef
 tProj.get(key));

Fix set operation.