package org.spoofax.jsglr.client.imploder;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

import org.metaborg.util.functions.Function1;
import org.spoofax.interpreter.terms.IStrategoAppl;
import org.spoofax.interpreter.terms.IStrategoConstructor;
import org.spoofax.interpreter.terms.IStrategoList;
import org.spoofax.interpreter.terms.IStrategoTerm;
import org.spoofax.interpreter.terms.IStrategoTuple;
import org.spoofax.interpreter.terms.ITermFactory;

import com.google.common.collect.ImmutableList;

public class Injections {

    private final ITermFactory factory;
    private final Function1<String, String> mangle;

    public Injections(ITermFactory factory) {
        this(factory, x -> x);
    }

    public Injections(ITermFactory factory, Function1<String, String> mangle) {
        this.factory = factory;
        this.mangle = mangle;
    }

    public IStrategoTerm explicate(final IStrategoTerm term) {
        final List<String> injections = ImmutableList.copyOf(ImploderAttachment.get(term).getInjections()).reverse();

        IStrategoTerm result;
        String sort;

        switch(term.getTermType()) {
            case IStrategoTerm.APPL: {
                final IStrategoAppl appl = (IStrategoAppl) term;
                final IStrategoTerm[] subterms = Arrays.stream(appl.getAllSubterms()).map(this::explicate)
                        .collect(Collectors.toList()).toArray(new IStrategoTerm[appl.getSubtermCount()]);
                result = factory.makeAppl(appl.getConstructor(), subterms, appl.getAnnotations());
                sort = ImploderAttachment.get(term).getSort();
                break;
            }
            case IStrategoTerm.TUPLE: {
                final IStrategoTuple tuple = (IStrategoTuple) term;
                final IStrategoTerm[] subterms = Arrays.stream(tuple.getAllSubterms()).map(this::explicate)
                        .collect(Collectors.toList()).toArray(new IStrategoTerm[tuple.getSubtermCount()]);
                result = factory.makeTuple(subterms, tuple.getAnnotations());
                final List<String> componentSorts = Arrays.stream(term.getAllSubterms())
                        .map(t -> ImploderAttachment.get(t).getSort()).collect(Collectors.toList());
                sort = "Tuple_" + String.join("_", componentSorts) + "_";
                break;
            }
            case IStrategoTerm.LIST: {
                final IStrategoList list = (IStrategoList) term;
                final IStrategoTerm[] subterms = Arrays.stream(list.getAllSubterms()).map(this::explicate)
                        .collect(Collectors.toList()).toArray(new IStrategoTerm[list.getSubtermCount()]);
                result = factory.makeList(subterms, list.getAnnotations());
                final String elementSort = ImploderAttachment.get(term).getElementSort();
                sort = "List_" + elementSort + "_";
                break;
            }
            default:
                result = term;
                sort = ImploderAttachment.get(term).getSort();
                break;
        }
        result = factory.copyAttachments(term, result);
        ImploderAttachment.get(result).clearInjections();

        for(String injection : injections) {
            final String name = mangle.apply(sort) + "2" + mangle.apply(injection);
            final IStrategoConstructor cons = factory.makeConstructor(name, 1);
            ImploderAttachment ia = ImploderAttachment.get(result);
            result = factory.makeAppl(cons, result);
            sort = injection;
            if(ia != null) {
                ImploderAttachment.putImploderAttachment(result, false, sort, ia.getLeftToken(), ia.getRightToken(),
                        false, false, false, false);
            }
        }

        return result;
    }

}
@@ -165,17 +165,17 @@
 Function
-1
+2
 ;%0Aimport
@@ -622,33 +622,33 @@
 e final Function
-1
+2
 %3CString, String%3E
@@ -650,101 +650,26 @@
 ring
-%3E mangle;%0A%0A    public Injections(ITermFactory factory) %7B%0A        this(factory, x -%3E x);%0A    %7D
+, String%3E injName;
 %0A%0A  
@@ -722,10 +722,18 @@
 tion
-1%3C
+2%3CString, 
 Stri
@@ -744,21 +744,22 @@
 String%3E 
-mangl
+injNam
 e) %7B%0A   
@@ -804,22 +804,24 @@
 his.
-mangle = mangl
+injName = injNam
 e;%0A 
@@ -3227,21 +3227,22 @@
  name = 
-mangl
+injNam
 e.apply(
@@ -3249,31 +3249,10 @@
 sort
-) + %222%22 + mangle.apply(
+, 
 inje

Allow user complete control over injection name.