package jetbrains.mps.make.dependencies;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.logging.Logger;
import jetbrains.mps.make.MakeSession;
import jetbrains.mps.smodel.language.LanguageRegistry;
import jetbrains.mps.make.resources.IResource;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.smodel.resources.MResource;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.generator.impl.plan.ModelContentUtil;
import jetbrains.mps.extapi.model.SModelBase;
import org.jetbrains.mps.openapi.language.SLanguage;
import org.jetbrains.mps.openapi.module.SModule;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import jetbrains.mps.smodel.SLanguageHierarchy;
import jetbrains.mps.smodel.language.GeneratorRuntime;
import java.util.Queue;
import jetbrains.mps.internal.collections.runtime.QueueSequence;
import java.util.LinkedList;
import jetbrains.mps.smodel.language.LanguageRuntime;
import jetbrains.mps.generator.runtime.TemplateModule;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;

@GeneratedClass(node = "r:d357a980-6a2b-481f-acb3-29792a9d3728(jetbrains.mps.make.dependencies)/1077540970775387654", model = "r:d357a980-6a2b-481f-acb3-29792a9d3728(jetbrains.mps.make.dependencies)")
public class ModulesClusterizer {
  private static Logger LOG = Logger.getLogger(ModulesCluster.class);
  private static boolean NEW_CLUSTERS = Boolean.getBoolean("mps.make.newclusters");
  private final MakeSession mySession;
  private final LanguageRegistry myLanguageRegistry;

  /*package*/ ModulesClusterizer(MakeSession makeSession) {
    mySession = makeSession;
    myLanguageRegistry = mySession.getProject().getComponent(LanguageRegistry.class);
  }

  public Iterable<Cluster> clusterize(Iterable<? extends IResource> input) {
    List<Cluster> result = ListSequence.fromList(new ArrayList<Cluster>());
    List<MResource> emptyInput = Sequence.fromIterable(input).ofType(MResource.class).where((it) -> Sequence.fromIterable(it.models()).all((m) -> {
      boolean usedLangsEmpty = ModelContentUtil.getUsedLanguages(m).isEmpty();
      if (!(usedLangsEmpty)) {
        return false;
      }
      if (m instanceof SModelBase) {
        boolean importedLangsEmpty = ((SModelBase) m).importedLanguageIds().isEmpty();
        boolean importedDevkitsEmpty = ((SModelBase) m).importedDevkits().isEmpty();
        if (!(importedLangsEmpty) || !(importedDevkitsEmpty)) {
          return false;
        }
      }
      return true;

    })).toList();

    List<MResource> mres = Sequence.fromIterable(input).ofType(MResource.class).subtract(ListSequence.fromList(emptyInput)).toList();
    if (ListSequence.fromList(mres).isEmpty()) {
      return result;
    }

    Iterable<IResource> rest = Sequence.fromIterable(((Iterable<IResource>) input)).subtract(ListSequence.fromList(mres)).subtract(ListSequence.fromList(emptyInput));
    // FIXME use ProgressMonitor as graph ordering may take some time
    Iterable<Cluster> mr2cluster = (NEW_CLUSTERS ? moduleResourcesToClustersNew(mres) : moduleResourcesToClustersOld(mres));
    ListSequence.fromList(result).addSequence(Sequence.fromIterable(mr2cluster));

    if (Sequence.fromIterable(rest).isNotEmpty()) {
      ListSequence.fromList(result).addElement(new Cluster(rest, ListSequence.fromList(new ArrayList<SLanguage>())));
    }

    return result;
  }

  private Iterable<Cluster> moduleResourcesToClustersOld(final List<MResource> mres) {
    Iterable<SModule> mods = ListSequence.fromList(mres).select((r) -> r.module());

    final ModulesCluster clst = new ModulesCluster(myLanguageRegistry);
    Iterable<List<SModule>> moduleBuildOrder = clst.buildOrder(mods);
    Iterable<? extends Iterable<MResource>> mresBuildOrder = Sequence.fromIterable(moduleBuildOrder).select((final List<SModule> cl) -> ListSequence.fromList(mres).where((r) -> ListSequence.fromList(cl).contains(r.module())));

    return Sequence.fromIterable(mresBuildOrder).select((s) -> {

      return new Cluster(s, allLanguagesToActivateFacets(s, clst));
    });
  }

  private Iterable<Cluster> moduleResourcesToClustersNew(List<MResource> mres) {
    ModuleBuildSequence mbs = new ModuleBuildSequence();
    mbs.buildOrder(ListSequence.fromList(mres).select((r) -> r.module()));
    return Sequence.fromIterable(mods2cluster(mres, mbs.phaseOne())).concat(Sequence.fromIterable(mods2cluster(mres, mbs.phaseTwo()))).concat(Sequence.fromIterable(mods2cluster(mres, mbs.phaseThree())));
  }

  private Iterable<Cluster> mods2cluster(List<MResource> mres, List<List<SModule>> phase) {
    List<Cluster> rv = ListSequence.fromList(new ArrayList<Cluster>());
    System.out.println("PHASE:>>>");
    for (List<SModule> l : ListSequence.fromList(phase)) {
      System.out.println("C[");
      ListSequence.fromList(l).visitAll((it) -> System.out.println(it.getModuleName()));
      System.out.println("]");
      final Set<SModule> clusterModules = SetSequence.fromSetWithValues(new HashSet<SModule>(), l);
      List<SLanguage> usedLangs = ListSequence.fromList(l).translate((it) -> it.getUsedLanguages()).toList();
      Set<SLanguage> extended = new SLanguageHierarchy(myLanguageRegistry, usedLangs).getExtended();
      Iterable<SLanguage> l2af = allLanguagesToActivateFacets(extended);
      ListSequence.fromList(rv).addElement(new Cluster(ListSequence.fromList(mres).where((r) -> SetSequence.fromSet(clusterModules).contains(r.module())), l2af));
    }
    System.out.println("<<<<");
    return rv;
  }

  private Iterable<SLanguage> allLanguagesToActivateFacets(Iterable<MResource> cluster, final ModulesCluster builder) {
    return allLanguagesToActivateFacets(Sequence.fromIterable(cluster).translate((r) -> builder.usedLanguage(r.module())));
  }

  private Iterable<SLanguage> allLanguagesToActivateFacets(Iterable<SLanguage> usedLanguages) {
    Set<SLanguage> namespaces = SetSequence.fromSet(new HashSet<SLanguage>());
    Set<GeneratorRuntime> seen = SetSequence.fromSet(new HashSet<GeneratorRuntime>());
    Queue<SLanguage> nsq = QueueSequence.fromQueue(new LinkedList<SLanguage>());
    QueueSequence.fromQueue(nsq).addSequence(Sequence.fromIterable(usedLanguages).distinct());
    // We need to care about used languages of employed generators as we need to respect
    // all facets of all languages that may appear during generation of a model/module in the make script
    while (QueueSequence.fromQueue(nsq).isNotEmpty()) {
      SLanguage ns = QueueSequence.fromQueue(nsq).removeFirstElement();
      if (SetSequence.fromSet(namespaces).contains(ns)) {
        continue;
      }
      SetSequence.fromSet(namespaces).addElement(ns);
      LanguageRuntime lr = myLanguageRegistry.getLanguage(ns);
      if (lr == null) {
        LOG.debug("Deployed language not found for namespace " + ns);
        continue;
      }
      for (GeneratorRuntime gr : lr.getGenerators()) {
        if (gr instanceof TemplateModule == false) {
          continue;
        }
        if (!(SetSequence.fromSet(seen).contains(gr))) {
          QueueSequence.fromQueue(nsq).addSequence(CollectionSequence.fromCollection(((TemplateModule) gr).getTargetLanguages()));
          SetSequence.fromSet(seen).addElement(gr);
        }
      }
    }
    return namespaces;
  }

}
