package jetbrains.mps.build.util;

/*Generated by MPS */

import org.jetbrains.mps.openapi.model.SNode;
import java.util.Map;
import java.util.HashMap;
import java.util.List;
import java.util.ArrayList;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import jetbrains.mps.generator.TransientModelsModule;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SConcept;

public class VisibleArtifacts {
  protected final SNode project;
  protected final Map<SNode, SNode> parentMap = new HashMap<SNode, SNode>();
  private final List<SNode> visibleArtifacts = new ArrayList<SNode>();
  private final List<SNode> visibleLayouts = new ArrayList<SNode>();
  private ArtifactLookup myLookup;

  public VisibleArtifacts(SNode project) {
    this.project = project;
  }

  public void collect(boolean localProjectOnly) {
    if (!(localProjectOnly)) {
      collectOnlyExternal();
    }
    // FIXME why do we need artifacts from this very project?! It dates back to 6404ac9c with no clear explanation why do we need it there.
    //      I assume now we need modules of this project only in cases when localProjectOnly == true.
    collectProjectArtifacts();
  }

  public void collectOnlyExternal() {
    for (SNode layoutDependency : SNodeOperations.ofConcept(SLinkOperations.getChildren(project, LINKS.dependencies$redY), CONCEPTS.BuildExternalLayoutDependency$oL)) {
      SNode target = SLinkOperations.getTarget(layoutDependency, LINKS.layout$GC7_);
      collectInExternalLayout(layoutDependency, target);
    }
    for (SNode projectDependency : SNodeOperations.ofConcept(SLinkOperations.getChildren(project, LINKS.dependencies$redY), CONCEPTS.BuildProjectDependency$sN)) {
      SNode target = SLinkOperations.getTarget(projectDependency, LINKS.script$6Ehy);
      collectInProject(projectDependency, target);
    }
  }

  public void collectProjectArtifacts() {
    collectInProject(SLinkOperations.getTarget(project, LINKS.layout$r7bw), project);
  }

  private void collectInProject(SNode parent, SNode target) {
    if (target == null) {
      return;
    }

    ListSequence.fromList(visibleLayouts).addElement(SLinkOperations.getTarget(target, LINKS.layout$r7bw));
    if (SLinkOperations.getTarget(target, LINKS.layout$r7bw) != parent) {
      parentMap.put(SLinkOperations.getTarget(target, LINKS.layout$r7bw), parent);
    }
    for (SNode node : SLinkOperations.getChildren(SLinkOperations.getTarget(target, LINKS.layout$r7bw), LINKS.children$aMRO)) {
      collectInLayout(parent, node);
    }
  }

  private void collectInExternalLayout(SNode parent, SNode target) {
    ListSequence.fromList(visibleLayouts).addElement(target);
    parentMap.put(target, parent);
    for (SNode node : SLinkOperations.getChildren(target, LINKS.children$aMRO)) {
      collectInLayout(parent, node);
    }
  }

  protected void collectInLayout(SNode parent, SNode node) {
    if (node == null) {
      return;
    }
    if (parentMap.containsKey(node)) {
      return;
    }

    ListSequence.fromList(visibleArtifacts).addElement(node);
    parentMap.put(node, parent);

    if (SNodeOperations.isInstanceOf(node, CONCEPTS.BuildLayout_AbstractContainer$zG)) {
      for (SNode child : SLinkOperations.getChildren(SNodeOperations.as(node, CONCEPTS.BuildLayout_AbstractContainer$zG), LINKS.children$aMRO)) {
        collectInLayout(node, child);
      }
    } else if (SNodeOperations.isInstanceOf(node, CONCEPTS.BuildLayout_ImportContent$wC)) {
      for (SNode child : ListSequence.fromList(SLinkOperations.getChildren(SLinkOperations.getTarget(SNodeOperations.as(node, CONCEPTS.BuildLayout_ImportContent$wC), LINKS.target$HFO4), LINKS.children$aMRO))) {
        collectInLayout(parent, child);
      }
    } else if (SNodeOperations.isInstanceOf(node, CONCEPTS.BuildLayout_Import$wO)) {
      collectInLayout(parent, SLinkOperations.getTarget(SNodeOperations.as(node, CONCEPTS.BuildLayout_Import$wO), LINKS.target$AFU4));
    }
  }
  public SNode getProject() {
    return project;
  }
  public Iterable<SNode> getArtifacts() {
    return ListSequence.fromList(visibleArtifacts).asUnmodifiable();
  }
  public Iterable<SNode> getLayouts() {
    return ListSequence.fromList(visibleLayouts).asUnmodifiable();
  }
  public boolean contains(SNode node) {
    return parentMap.containsKey(node);
  }
  public SNode parent(SNode node) {
    SNode result = parentMap.get(node);
    if (result == node) {
      throw new IllegalStateException();
    }
    return result;
  }

  protected ArtifactLookup createLookup() {
    return new ArtifactLookup(this);
  }

  /**
   * Logic to look artifacts up kept separate to facilitate caching during generation.
   * Clients that are capable of caching (has access to DependenciesHelper) shall override {@link jetbrains.mps.build.util.VisibleArtifacts#createLookup() }
   */
  public ArtifactLookup getLookup() {
    if (myLookup == null) {
      myLookup = createLookup();
    }
    return myLookup;
  }

  /**
   * FIXME check why BL_PathElement is return value, why not BL_Node
   */
  public SNode findArtifact(Object id) {
    return getLookup().findArtifact(id);
  }

  @NotNull
  public Tuples._2<SNode, String> getResource(SNode path) {
    return getLookup().getResource(path);
  }

  public static VisibleArtifacts createExternalFor(SNode project) {
    assert !(SNodeOperations.getModel(project).getModule() instanceof TransientModelsModule);
    VisibleArtifacts artifacts = new VisibleArtifacts(project);
    artifacts.collectOnlyExternal();
    return artifacts;
  }

  private static final class LINKS {
    /*package*/ static final SReferenceLink layout$GC7_ = MetaAdapterFactory.getReferenceLink(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x63a87b9320d3d0a4L, 0x63a87b9320d3d0a7L, "layout");
    /*package*/ static final SContainmentLink dependencies$redY = MetaAdapterFactory.getContainmentLink(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x4df58c6f18f84a13L, 0x4df58c6f18f84a25L, "dependencies");
    /*package*/ static final SReferenceLink script$6Ehy = MetaAdapterFactory.getReferenceLink(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x454b730dd908c220L, 0x4df58c6f18f84a24L, "script");
    /*package*/ static final SContainmentLink layout$r7bw = MetaAdapterFactory.getContainmentLink(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x4df58c6f18f84a13L, 0x4df58c6f18f84a1cL, "layout");
    /*package*/ static final SContainmentLink children$aMRO = MetaAdapterFactory.getContainmentLink(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x4140393b234482c3L, 0x668c6cfbafac4c8eL, "children");
    /*package*/ static final SReferenceLink target$HFO4 = MetaAdapterFactory.getReferenceLink(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x4ddcec86af9fdb53L, 0x4ddcec86af9fdb55L, "target");
    /*package*/ static final SReferenceLink target$AFU4 = MetaAdapterFactory.getReferenceLink(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0xbabdfbeee1350f2L, 0xbabdfbeee1350f4L, "target");
  }

  private static final class CONCEPTS {
    /*package*/ static final SConcept BuildExternalLayoutDependency$oL = MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x63a87b9320d3d0a4L, "jetbrains.mps.build.structure.BuildExternalLayoutDependency");
    /*package*/ static final SConcept BuildProjectDependency$sN = MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x454b730dd908c220L, "jetbrains.mps.build.structure.BuildProjectDependency");
    /*package*/ static final SConcept BuildLayout_ImportContent$wC = MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x4ddcec86af9fdb53L, "jetbrains.mps.build.structure.BuildLayout_ImportContent");
    /*package*/ static final SConcept BuildLayout_Import$wO = MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0xbabdfbeee1350f2L, "jetbrains.mps.build.structure.BuildLayout_Import");
    /*package*/ static final SConcept BuildLayout_AbstractContainer$zG = MetaAdapterFactory.getConcept(0x798100da4f0a421aL, 0xb99171f8c50ce5d2L, 0x668c6cfbafabcf0cL, "jetbrains.mps.build.structure.BuildLayout_AbstractContainer");
  }
}
