package jetbrains.mps.generator.impl;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.generator.plan.PlanIdentity;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import jetbrains.mps.generator.GenerationPlanBuilder;
import java.util.ArrayList;
import org.jetbrains.mps.openapi.module.SModuleReference;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import org.jetbrains.mps.openapi.language.SLanguage;
import jetbrains.mps.smodel.behaviour.BHReflection;
import jetbrains.mps.core.aspects.behaviour.SMethodIdV2;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.generator.plan.CheckpointIdentity;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SEnumOperations;
import org.jetbrains.mps.openapi.language.SConcept;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SInterfaceConcept;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SReferenceLink;

/**
 * Mediator between a model with Plan node and {@link jetbrains.mps.generator.ModelGenerationPlan } run-time presentation.
 * Translates sequence of plan steps from model into sequence of {@link jetbrains.mps.generator.GenerationPlanBuilder } calls.
 * I use {@link jetbrains.mps.generator.GenerationPlanBuilder } although it seems like an excessive mediator to hide implementation details and to facilitate
 * plan creation not from a model-backed description.
 */
@GeneratedClass(node = "r:ab837574-aa54-4b18-9762-b783ef089263(jetbrains.mps.generator.impl)/2214485068936293220", model = "r:ab837574-aa54-4b18-9762-b783ef089263(jetbrains.mps.generator.impl)")
public final class GenPlanTranslator {
  private final SNode myPlanDeclaration;
  private final PlanIdentity myPlanIdentity;

  /**
   * 
   * 
   * @return plan translator instance for the fist {@code node<Plan>} found in the model, or {@code null} if no matching node found
   */
  @Nullable
  public static GenPlanTranslator fromGenPlanModel(@NotNull SModel gpm) {
    SNode pn = ListSequence.fromList(SModelOperations.roots(gpm, CONCEPTS.Plan$X1)).first();
    return (pn == null ? null : new GenPlanTranslator(pn));
  }

  public GenPlanTranslator(@NotNull SNode planDeclaration) {
    myPlanDeclaration = planDeclaration;
    myPlanIdentity = new PlanIdentity(SPropertyOperations.getString(myPlanDeclaration, PROPS.name$MnvL));
  }

  public PlanIdentity getPlanIdentity() {
    return myPlanIdentity;
  }

  /**
   * Doesn't wrap the planBuilder, it's up to caller.
   * 
   * @return {@code this} for convenience
   */
  public GenPlanTranslator feed(@NotNull GenerationPlanBuilder planBuilder) {
    ArrayList<SModuleReference> generators = new ArrayList<SModuleReference>();

    for (SNode stepNode : SLinkOperations.getChildren(myPlanDeclaration, LINKS.steps$Xwbb)) {
      if (SNodeOperations.isInstanceOf(stepNode, CONCEPTS.Checkpoint$ZV)) {
        planBuilder.recordCheckpoint(cpIdentity(SLinkOperations.getTarget(SNodeOperations.as(stepNode, CONCEPTS.Checkpoint$ZV), LINKS.cpSpec$v7$t)));
      } else if (SNodeOperations.isInstanceOf(stepNode, CONCEPTS.Transform$a_)) {
        SNode stepTransform = SNodeOperations.as(stepNode, CONCEPTS.Transform$a_);
        if (ListSequence.fromList(SLinkOperations.getChildren(stepTransform, LINKS.languages$AUhz)).isNotEmpty() && ListSequence.fromList(SLinkOperations.getChildren(stepTransform, LINKS.entries$T03u)).isEmpty()) {
          // FIXME compatibility code to facilitate transition. Now `transform <language>` means
          // its generators are applied regardless of actual language use/presence in the model
          // (i.e. generator may decide itself not to produce anything in case of empty model, or to produce
          // some output anyway). However, I don't feel it's desired behavior - using empty model as in input is
          // not quite good (see @descriptor model provider and the dance around used/engaged languages)
          // and would like to have a statement to apply language reductions only if language present
          // However, can't switch to a new stmt right away as need to deal with structure aspect generator first - now 
          // any structure model get some files generated.
          ArrayList<SLanguage> ll = new ArrayList<SLanguage>();
          for (SNode lid : SLinkOperations.getChildren(stepTransform, LINKS.languages$AUhz)) {
            ll.add(((SLanguage) BHReflection.invoke0(lid, CONCEPTS.LanguageIdentity$cN, SMethodIdV2.create("getLanguage", 3542851458883438739L, 0xf9dadaaf9d633926L))));
          }
          planBuilder.transformLanguage(ll.toArray(new SLanguage[0]));
        } else {
          GenerationPlanBuilder.TransformStepBuilder stepBuilder = planBuilder.transform(SPropertyOperations.getBoolean(stepTransform, PROPS.individualStepPerGenerator$EG46));
          for (SNode lid : SLinkOperations.getChildren(stepTransform, LINKS.languages$AUhz)) {
            stepBuilder.include(((SLanguage) BHReflection.invoke0(lid, CONCEPTS.LanguageIdentity$cN, SMethodIdV2.create("getLanguage", 3542851458883438739L, 0xf9dadaaf9d633926L))), GenerationPlanBuilder.BuilderOption.None);
          }
          for (SNode le : SLinkOperations.getChildren(stepTransform, LINKS.entries$T03u)) {
            stepBuilder.include(((SLanguage) BHReflection.invoke0(SLinkOperations.getTarget(le, LINKS.language$pqOb), CONCEPTS.LanguageIdentity$cN, SMethodIdV2.create("getLanguage", 3542851458883438739L, 0xf9dadaaf9d633926L))), option(le));
          }
          stepBuilder.complete();
        }
      } else if (SNodeOperations.isInstanceOf(stepNode, CONCEPTS.ApplyGenerators$PQ)) {
        SNode applyGeneratorsStep = SNodeOperations.as(stepNode, CONCEPTS.ApplyGenerators$PQ);
        final boolean withExtended = SPropertyOperations.getBoolean(applyGeneratorsStep, PROPS.withExtended$Vq9q);
        for (SNode generator : Sequence.fromIterable(SNodeOperations.ofConcept(SLinkOperations.getChildren(applyGeneratorsStep, LINKS.generator$bWty), CONCEPTS.GeneratorModulePointer$49))) {
          SModuleReference mr = ((SModuleReference) BHReflection.invoke0(SLinkOperations.getTarget(generator, LINKS.module$u1do), CONCEPTS.ModuleIdentity$U1, SMethodIdV2.create("getModuleReference", 427659576753753658L, 0xf9dadaaf9d633926L)));
          if (mr == null) {
            continue;
          }
          generators.add(mr);
        }
        if (withExtended) {
          final boolean withPriorityRules = SPropertyOperations.getBoolean(applyGeneratorsStep, PROPS.withPriorityRules$G6xp);
          planBuilder.applyGenerators(generators, GenerationPlanBuilder.BuilderOption.WithExtendedGenerators, (withPriorityRules ? GenerationPlanBuilder.BuilderOption.WithPriorityRules : GenerationPlanBuilder.BuilderOption.None));
        } else {
          planBuilder.applyGenerators(generators);
        }
        generators.clear();
      } else if (SNodeOperations.isInstanceOf(stepNode, CONCEPTS.CheckpointSynchronization$DV)) {
        planBuilder.synchronizeWithCheckpoint(cpIdentity(SLinkOperations.getTarget(SNodeOperations.as(stepNode, CONCEPTS.CheckpointSynchronization$DV), LINKS.checkpoint$18uq)));
      } else if (SNodeOperations.isInstanceOf(stepNode, CONCEPTS.CheckpointDeclaration$4L)) {
        SNode cpDecl = SNodeOperations.as(stepNode, CONCEPTS.CheckpointDeclaration$4L);
        planBuilder.declareCheckpoint(new CheckpointIdentity(myPlanIdentity, SPropertyOperations.getString(cpDecl, PROPS.name$MnvL)));
      } else if (SNodeOperations.isInstanceOf(stepNode, CONCEPTS.IncludePlan$sw)) {
        SNode includedPlan = SLinkOperations.getTarget(SNodeOperations.as(stepNode, CONCEPTS.IncludePlan$sw), LINKS.plan$8HNy);
        new GenPlanTranslator(includedPlan).feed(planBuilder);
      } else if (SNodeOperations.isInstanceOf(stepNode, CONCEPTS.Fork$9c)) {
        SNode branchPlan = SLinkOperations.getTarget(SNodeOperations.as(stepNode, CONCEPTS.Fork$9c), LINKS.plan$IYKb);
        GenerationPlanBuilder branchBuilder = planBuilder.fork();
        new GenPlanTranslator(branchPlan).feed(branchBuilder);
        branchBuilder.wrapUp(new PlanIdentity(SPropertyOperations.getString(branchPlan, PROPS.name$MnvL)));
      }
    }
    return this;
  }

  private CheckpointIdentity cpIdentity(SNode cpSpec) {
    if (SNodeOperations.isInstanceOf(cpSpec, CONCEPTS.InPlaceCheckpointSpec$pM)) {
      return new CheckpointIdentity(myPlanIdentity, SPropertyOperations.getString(SNodeOperations.as(cpSpec, CONCEPTS.InPlaceCheckpointSpec$pM), PROPS.name$MnvL));
    }
    if (SNodeOperations.isInstanceOf(cpSpec, CONCEPTS.DeclaredCheckpointSpec$HD)) {
      SNode cpDecl = SLinkOperations.getTarget(SNodeOperations.as(cpSpec, CONCEPTS.DeclaredCheckpointSpec$HD), LINKS.cpDecl$4hsH);
      return new CheckpointIdentity(new PlanIdentity(SPropertyOperations.getString(SNodeOperations.as(SNodeOperations.getParent(cpDecl), CONCEPTS.Plan$X1), PROPS.name$MnvL)), SPropertyOperations.getString(cpDecl, PROPS.name$MnvL));
    }
    if (SNodeOperations.isInstanceOf(cpSpec, CONCEPTS.InPlaceCheckpointRefSpec$H6)) {
      SNode cpStep = SLinkOperations.getTarget(SNodeOperations.as(cpSpec, CONCEPTS.InPlaceCheckpointRefSpec$H6), LINKS.checkpoint$4Q6y);
      SNode inPlaceCPSpec = SNodeOperations.as(SLinkOperations.getTarget(cpStep, LINKS.cpSpec$v7$t), CONCEPTS.InPlaceCheckpointSpec$pM);
      if (inPlaceCPSpec == null) {
        throw new IllegalStateException();
      }
      return new CheckpointIdentity(new PlanIdentity(SPropertyOperations.getString(SNodeOperations.as(SNodeOperations.getParent(cpStep), CONCEPTS.Plan$X1), PROPS.name$MnvL)), SPropertyOperations.getString(inPlaceCPSpec, PROPS.name$MnvL));
    }
    throw new IllegalStateException("Unsupported checkpoint specification " + SNodeOperations.getConcept(cpSpec));
  }

  private static GenerationPlanBuilder.BuilderOption option(SNode le) {
    if (SEnumOperations.isMember(SPropertyOperations.getEnum(le, PROPS.kind$xL6K), 0x100024c0a63c4817L)) {
      return GenerationPlanBuilder.BuilderOption.Extend;
    }
    if (SEnumOperations.isMember(SPropertyOperations.getEnum(le, PROPS.kind$xL6K), 0x100024c0a63c4814L)) {
      return GenerationPlanBuilder.BuilderOption.TargetTo;
    }
    return GenerationPlanBuilder.BuilderOption.None;
  }

  private static final class CONCEPTS {
    /*package*/ static final SConcept Plan$X1 = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a20717fbL, "jetbrains.mps.lang.generator.plan.structure.Plan");
    /*package*/ static final SConcept Checkpoint$ZV = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a2071801L, "jetbrains.mps.lang.generator.plan.structure.Checkpoint");
    /*package*/ static final SConcept Transform$a_ = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a2071802L, "jetbrains.mps.lang.generator.plan.structure.Transform");
    /*package*/ static final SInterfaceConcept LanguageIdentity$cN = MetaAdapterFactory.getInterfaceConcept(0x7866978ea0f04cc7L, 0x81bc4d213d9375e1L, 0x312abca18ab8c318L, "jetbrains.mps.lang.smodel.structure.LanguageIdentity");
    /*package*/ static final SConcept ApplyGenerators$PQ = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x73246de9adeca171L, "jetbrains.mps.lang.generator.plan.structure.ApplyGenerators");
    /*package*/ static final SInterfaceConcept ModuleIdentity$U1 = MetaAdapterFactory.getInterfaceConcept(0x7866978ea0f04cc7L, 0x81bc4d213d9375e1L, 0x5ef5a1e85338e1eL, "jetbrains.mps.lang.smodel.structure.ModuleIdentity");
    /*package*/ static final SConcept GeneratorModulePointer$49 = MetaAdapterFactory.getConcept(0x7866978ea0f04cc7L, 0x81bc4d213d9375e1L, 0x73246de9adecb80dL, "jetbrains.mps.lang.smodel.structure.GeneratorModulePointer");
    /*package*/ static final SConcept CheckpointSynchronization$DV = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0xc11e5088a794d07L, "jetbrains.mps.lang.generator.plan.structure.CheckpointSynchronization");
    /*package*/ static final SConcept CheckpointDeclaration$4L = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x56d679ca1f4b53ceL, "jetbrains.mps.lang.generator.plan.structure.CheckpointDeclaration");
    /*package*/ static final SConcept IncludePlan$sw = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x336c2500000e1b2fL, "jetbrains.mps.lang.generator.plan.structure.IncludePlan");
    /*package*/ static final SConcept Fork$9c = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x2bf683196b12a3cbL, "jetbrains.mps.lang.generator.plan.structure.Fork");
    /*package*/ static final SConcept InPlaceCheckpointSpec$pM = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x340cd07aed7ca161L, "jetbrains.mps.lang.generator.plan.structure.InPlaceCheckpointSpec");
    /*package*/ static final SConcept DeclaredCheckpointSpec$HD = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x340cd07aed7cb2fdL, "jetbrains.mps.lang.generator.plan.structure.DeclaredCheckpointSpec");
    /*package*/ static final SConcept InPlaceCheckpointRefSpec$H6 = MetaAdapterFactory.getConcept(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x340cd07aed7cb32cL, "jetbrains.mps.lang.generator.plan.structure.InPlaceCheckpointRefSpec");
  }

  private static final class PROPS {
    /*package*/ static final SProperty name$MnvL = MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name");
    /*package*/ static final SProperty individualStepPerGenerator$EG46 = MetaAdapterFactory.getProperty(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a2071802L, 0x1ea8fc1842b8cc6aL, "individualStepPerGenerator");
    /*package*/ static final SProperty withExtended$Vq9q = MetaAdapterFactory.getProperty(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x73246de9adeca171L, 0xc11e5088a799353L, "withExtended");
    /*package*/ static final SProperty withPriorityRules$G6xp = MetaAdapterFactory.getProperty(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x73246de9adeca171L, 0xf738996443c35afL, "withPriorityRules");
    /*package*/ static final SProperty kind$xL6K = MetaAdapterFactory.getProperty(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x100024c0a63c480fL, 0x100024c0a63c5feeL, "kind");
  }

  private static final class LINKS {
    /*package*/ static final SContainmentLink cpSpec$v7$t = MetaAdapterFactory.getContainmentLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a2071801L, 0x340cd07aed7cb2d2L, "cpSpec");
    /*package*/ static final SContainmentLink languages$AUhz = MetaAdapterFactory.getContainmentLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a2071802L, 0x28dd6d5a7549fa8dL, "languages");
    /*package*/ static final SContainmentLink entries$T03u = MetaAdapterFactory.getContainmentLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a2071802L, 0x100024c0a63c5ff6L, "entries");
    /*package*/ static final SContainmentLink language$pqOb = MetaAdapterFactory.getContainmentLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x100024c0a63c480fL, 0x100024c0a63c4810L, "language");
    /*package*/ static final SContainmentLink module$u1do = MetaAdapterFactory.getContainmentLink(0x7866978ea0f04cc7L, 0x81bc4d213d9375e1L, 0x73246de9adecb80dL, 0x73246de9adecb874L, "module");
    /*package*/ static final SContainmentLink generator$bWty = MetaAdapterFactory.getContainmentLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x73246de9adeca171L, 0x73246de9adf5a45cL, "generator");
    /*package*/ static final SContainmentLink checkpoint$18uq = MetaAdapterFactory.getContainmentLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0xc11e5088a794d07L, 0x340cd07aedd21238L, "checkpoint");
    /*package*/ static final SReferenceLink plan$8HNy = MetaAdapterFactory.getReferenceLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x336c2500000e1b2fL, 0x336c2500000e1b32L, "plan");
    /*package*/ static final SReferenceLink plan$IYKb = MetaAdapterFactory.getReferenceLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x2bf683196b12a3cbL, 0x2bf683196b12a3ccL, "plan");
    /*package*/ static final SContainmentLink steps$Xwbb = MetaAdapterFactory.getContainmentLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x19443180a20717fbL, 0x19443180a2071807L, "steps");
    /*package*/ static final SReferenceLink cpDecl$4hsH = MetaAdapterFactory.getReferenceLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x340cd07aed7cb2fdL, 0x340cd07aed7cb300L, "cpDecl");
    /*package*/ static final SReferenceLink checkpoint$4Q6y = MetaAdapterFactory.getReferenceLink(0x7ab1a6fa0a114b95L, 0x9e4875f363d6cb00L, 0x340cd07aed7cb32cL, 0x340cd07aed7cb32fL, "checkpoint");
  }
}
