package jetbrains.mps.lang.structure.pluginSolution.plugin;

/*Generated by MPS */

import jetbrains.mps.refactoring.participant.RefactoringParticipantBase;
import org.jetbrains.mps.openapi.model.SNodeReference;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.refactoring.participant.MoveNodeRefactoringParticipant;
import jetbrains.mps.smodel.structure.Extension;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import jetbrains.mps.refactoring.participant.RefactoringSession;
import jetbrains.mps.smodel.Language;
import java.util.Map;
import org.jetbrains.mps.openapi.module.SModuleReference;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.util.NameUtil;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.smodel.LanguageAspect;
import jetbrains.mps.smodel.SModelInternal;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.smodel.SModelOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.lang.migration.util.NodeReferenceUtil;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.smodel.builder.SNodeBuilder;
import jetbrains.mps.refactoring.participant.RefactoringParticipant;
import org.jetbrains.mps.openapi.module.SRepository;
import org.jetbrains.mps.openapi.module.SearchScope;
import org.jetbrains.mps.openapi.util.ProgressMonitor;
import jetbrains.mps.lang.core.pluginSolution.plugin.UpdateModelImports;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPointerOperations;
import jetbrains.mps.ide.findusages.model.SearchResults;
import jetbrains.mps.ide.findusages.model.SearchResult;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SConcept;

public class LanguageStructureMigrationParticipant<I, F> extends RefactoringParticipantBase<SNodeReference, SNodeReference, SNode, SNode> implements MoveNodeRefactoringParticipant<SNodeReference, SNodeReference> {

  private StructureSpecialization<I, F> myStructureSpecialization;
  public LanguageStructureMigrationParticipant(StructureSpecialization<I, F> structureSpecialization) {
    myStructureSpecialization = structureSpecialization;
  }

  public static class MoveConceptMigration_extension extends Extension.Default<MoveNodeRefactoringParticipant<?, ?>> {
    public MoveConceptMigration_extension() {
      super("jetbrains.mps.refactoring.participant.MoveNodeParticipantEP");
    }
    public MoveNodeRefactoringParticipant<?, ?> get() {
      return new LanguageStructureMigrationParticipant<SAbstractConcept, SAbstractConcept>(new MoveAbstractConceptSpecialization());
    }
  }
  public static class MovePropertyMigration_extension extends Extension.Default<MoveNodeRefactoringParticipant<?, ?>> {
    public MovePropertyMigration_extension() {
      super("jetbrains.mps.refactoring.participant.MoveNodeParticipantEP");
    }
    public MoveNodeRefactoringParticipant<?, ?> get() {
      return new LanguageStructureMigrationParticipant<SProperty, SProperty>(new MovePropertySpecialization());
    }
  }
  public static class MoveContainmentLinkMigration_extension extends Extension.Default<MoveNodeRefactoringParticipant<?, ?>> {
    public MoveContainmentLinkMigration_extension() {
      super("jetbrains.mps.refactoring.participant.MoveNodeParticipantEP");
    }
    public MoveNodeRefactoringParticipant<?, ?> get() {
      return new LanguageStructureMigrationParticipant<SContainmentLink, SContainmentLink>(new MoveContainmentLinkSpecialization());
    }
  }
  public static class MoveReferenceLinkMigration_extension extends Extension.Default<MoveNodeRefactoringParticipant<?, ?>> {
    public MoveReferenceLinkMigration_extension() {
      super("jetbrains.mps.refactoring.participant.MoveNodeParticipantEP");
    }
    public MoveNodeRefactoringParticipant<?, ?> get() {
      return new LanguageStructureMigrationParticipant<SReferenceLink, SReferenceLink>(new MoveReferenceLinkSpecialization());
    }
  }
  public MoveNodeRefactoringParticipant.MoveNodeRefactoringDataCollector<SNodeReference, SNodeReference> getDataCollector() {
    return myStructureSpecialization.getMigrationDataCollector();
  }

  public static class MigrationBuilder {
    private static final String myId = "refactoringSession.migrationBuilder";
    public static MigrationBuilder getBuilder(RefactoringSession session, Language module) {
      Map<SModuleReference, MigrationBuilder> moduleBuilders = (Map<SModuleReference, MigrationBuilder>) session.getObject(MigrationBuilder.myId);
      if (moduleBuilders == null) {
        moduleBuilders = MapSequence.fromMap(new HashMap<SModuleReference, MigrationBuilder>());
        session.putObject(MigrationBuilder.myId, moduleBuilders);
      }

      MigrationBuilder builder = MapSequence.fromMap(moduleBuilders).get(module.getModuleReference());
      if (builder == null) {
        builder = new MigrationBuilder(session, module);
        MapSequence.fromMap(moduleBuilders).put(module.getModuleReference(), builder);
      }
      return builder;
    }
    private SNode myRefactoringStep;
    public final MigrationDescription myDescription = new MigrationDescription();
    public static class MigrationDescription {
      public String description;
      public final List<String> conceptArgs = ListSequence.fromList(new ArrayList<String>());
      public final List<String> memberArgs = ListSequence.fromList(new ArrayList<String>());
    }
    public MigrationBuilder(RefactoringSession session, final Language language) {
      final int languageVersion = language.getLanguageVersion();
      String refactoringName = session.getRefactoringName();
      String migrationScriptName = (refactoringName == null ? "MigrationScript" : "Migrate_" + NameUtil.toValidCamelIdentifier(refactoringName));
      myRefactoringStep = createPureMigrationScript_kz6lmo_a0d0f01(languageVersion, migrationScriptName + "_" + languageVersion);
      session.registerChange(() -> {
        SModel migrationModel = LanguageAspect.MIGRATION.getOrCreate(language);
        SModelInternal sm = (SModelInternal) (SModel) migrationModel;
        for (SModelReference reference : ListSequence.fromList(SNodeOperations.getNodeDescendants(myRefactoringStep, null, true, new SAbstractConcept[]{})).translate((it) -> SNodeOperations.getReferences(it)).select((it) -> it.getTargetSModelReference()).distinct()) {
          if (!(SModelOperations.getImportedModelUIDs(migrationModel).contains(reference))) {
            sm.addModelImport(reference);
          }
        }
        SPropertyOperations.assign(myRefactoringStep, PROPS.description$l$Pt, myDescription.description);
        jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations.addRootNode(migrationModel, myRefactoringStep);
        language.setLanguageVersion(languageVersion + 1);
      });
    }
    public void addPart(@NotNull SNode initialStateNode, @NotNull SNode finalStateNode, SNode specialization) {
      addPart(createMoveNodeMigrationPart_kz6lmo_a0a0g01(NodeReferenceUtil.makeReflection(initialStateNode), NodeReferenceUtil.makeReflection(finalStateNode), specialization));
    }
    public void addPart(SNode migrationPart) {
      ListSequence.fromList(SLinkOperations.getChildren(myRefactoringStep, LINKS.part$ITsP)).addElement(migrationPart);
    }
    private static SNode createPureMigrationScript_kz6lmo_a0d0f01(int p0, String p1) {
      SNodeBuilder n0 = new SNodeBuilder().init(CONCEPTS.PureMigrationScript$hG);
      n0.setProperty(PROPS.fromVersion$hHKK, "" + (p0));
      n0.setProperty(PROPS.name$MnvL, p1);
      n0.setProperty(PROPS.description$l$Pt, null);
      return n0.getResult();
    }
    private static SNode createMoveNodeMigrationPart_kz6lmo_a0a0g01(SNode p0, SNode p1, SNode p2) {
      SNodeBuilder n0 = new SNodeBuilder().init(CONCEPTS.MoveNodeMigrationPart$zn);
      n0.forChild(LINKS.fromNode$UG1d).initNode(p0, CONCEPTS.AbstractNodeReference$bQ, true);
      n0.forChild(LINKS.toNode$UGvf).initNode(p1, CONCEPTS.AbstractNodeReference$bQ, true);
      {
        SNodeBuilder n1 = n0.forChild(LINKS.specialization$dM5g).initNode(p2, CONCEPTS.MoveNodeSpecialization$AM, false);
      }
      return n0.getResult();
    }
  }

  @Override
  public List<RefactoringParticipant.Option> getAvailableOptions(SNodeReference initialState, SRepository repository) {
    if (initialState != null) {
      return ListSequence.fromListAndArray(new ArrayList<RefactoringParticipant.Option>(), OPTION);
    } else {
      return ListSequence.fromList(new ArrayList<RefactoringParticipant.Option>());
    }
  }
  public static final RefactoringParticipant.Option OPTION = new RefactoringParticipant.Option("moveNode.options.writeMigrationScript", "Write migration script");

  @Override
  public List<List<RefactoringParticipant.Change<SNodeReference, SNodeReference>>> getChanges(final List<SNodeReference> initialStates, final SRepository repository, final List<RefactoringParticipant.Option> selectedOptions, SearchScope searchScope, ProgressMonitor progressMonitor) {
    if (!(ListSequence.fromList(selectedOptions).contains(OPTION))) {
      return ListSequence.fromList(initialStates).select((it) -> {
        List<RefactoringParticipant.Change<SNodeReference, SNodeReference>> list = ListSequence.fromList(new ArrayList<RefactoringParticipant.Change<SNodeReference, SNodeReference>>());
        return list;
      }).toList();
    }
    final boolean updateModelImports = ListSequence.fromList(selectedOptions).contains(UpdateModelImports.OPTION);

    return ListSequence.fromList(initialStates).select((final SNodeReference initialState) -> {
      final Language sourceModule = as_kz6lmo_a0a0a0a0a3a51(check_kz6lmo_a0a0a0a0a3a51(check_kz6lmo_a0a0a0a0a0d0p(SPointerOperations.resolveNode(initialState, repository))), Language.class);

      // todo: write guard migration with 'execute after'
      RefactoringParticipant.Change<SNodeReference, SNodeReference> change = new MoveNodeRefactoringParticipant.ChangeBase<SNodeReference, SNodeReference>() {
        public SearchResults getSearchResults() {
          SearchResults results = new SearchResults();
          return (sourceModule == null ? results : results.addSearchResult(new SearchResult<SModule>(sourceModule, "migration script")));
        }
        public RefactoringParticipant.KeepOldNodes needsToPreserveOldNode() {
          return RefactoringParticipant.KeepOldNodes.KEEP;
        }
        public void confirm(SNodeReference finalState, SRepository repository, RefactoringSession refactoringSession) {
          SNode sourceNode = SPointerOperations.resolveNode(initialState, repository);
          Language sourceModule = as_kz6lmo_a0a1a2a0a0d0a0a0d0p(check_kz6lmo_a0a1a2a0a0d0a0a0d0p(check_kz6lmo_a0a0b0c0a0a3a0a0a3a51(sourceNode)), Language.class);
          SNode targetNode = SPointerOperations.resolveNode(finalState, repository);
          Language targetModule = as_kz6lmo_a0a3a2a0a0d0a0a0d0p(check_kz6lmo_a0a3a2a0a0d0a0a0d0p(check_kz6lmo_a0a0d0c0a0a3a0a0a3a51(targetNode)), Language.class);
          if (sourceModule != null && targetModule != null) {
            MigrationBuilder logBuilder = MigrationBuilder.getBuilder(refactoringSession, sourceModule);
            myStructureSpecialization.updateMigrationDescription(sourceNode, targetNode, logBuilder);
            myStructureSpecialization.confirm(selectedOptions, initialState, finalState, repository, logBuilder, updateModelImports);
          }
        }
      };
      return (List<RefactoringParticipant.Change<SNodeReference, SNodeReference>>) ListSequence.fromListAndArray(new ArrayList<RefactoringParticipant.Change<SNodeReference, SNodeReference>>(), change);
    }).toList();
  }
  private static SModule check_kz6lmo_a0a0a0a0a3a51(SModel checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.getModule();
    }
    return null;
  }
  private static SModel check_kz6lmo_a0a0a0a0a0d0p(SNode checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.getModel();
    }
    return null;
  }
  private static SModule check_kz6lmo_a0a1a2a0a0d0a0a0d0p(SModel checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.getModule();
    }
    return null;
  }
  private static SModel check_kz6lmo_a0a0b0c0a0a3a0a0a3a51(SNode checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.getModel();
    }
    return null;
  }
  private static SModule check_kz6lmo_a0a3a2a0a0d0a0a0d0p(SModel checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.getModule();
    }
    return null;
  }
  private static SModel check_kz6lmo_a0a0d0c0a0a3a0a0a3a51(SNode checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.getModel();
    }
    return null;
  }
  private static <T> T as_kz6lmo_a0a0a0a0a3a51(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_kz6lmo_a0a1a2a0a0d0a0a0d0p(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_kz6lmo_a0a3a2a0a0d0a0a0d0p(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }

  private static final class PROPS {
    /*package*/ static final SProperty description$l$Pt = MetaAdapterFactory.getProperty(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5836cabbL, 0x1ddaf57d7b0038e8L, "description");
    /*package*/ static final SProperty fromVersion$hHKK = MetaAdapterFactory.getProperty(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5836cabbL, 0x67236d4a5836cabcL, "fromVersion");
    /*package*/ static final SProperty name$MnvL = MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name");
  }

  private static final class LINKS {
    /*package*/ static final SContainmentLink part$ITsP = MetaAdapterFactory.getContainmentLink(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5836cabbL, 0x67236d4a5836d7f3L, "part");
    /*package*/ static final SContainmentLink fromNode$UG1d = MetaAdapterFactory.getContainmentLink(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5830221eL, 0x67236d4a58343d15L, "fromNode");
    /*package*/ static final SContainmentLink toNode$UGvf = MetaAdapterFactory.getContainmentLink(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5830221eL, 0x67236d4a58343d17L, "toNode");
    /*package*/ static final SContainmentLink specialization$dM5g = MetaAdapterFactory.getContainmentLink(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5830221eL, 0x2b3f57492c165c59L, "specialization");
  }

  private static final class CONCEPTS {
    /*package*/ static final SConcept PureMigrationScript$hG = MetaAdapterFactory.getConcept(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5836cabbL, "jetbrains.mps.lang.migration.structure.PureMigrationScript");
    /*package*/ static final SConcept MoveNodeMigrationPart$zn = MetaAdapterFactory.getConcept(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x67236d4a5830221eL, "jetbrains.mps.lang.migration.structure.MoveNodeMigrationPart");
    /*package*/ static final SConcept AbstractNodeReference$bQ = MetaAdapterFactory.getConcept(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x2b3f57492c1648ccL, "jetbrains.mps.lang.migration.structure.AbstractNodeReference");
    /*package*/ static final SConcept MoveNodeSpecialization$AM = MetaAdapterFactory.getConcept(0x9074634404fd4286L, 0x97d5b46ae6a81709L, 0x2b3f57492c165c5dL, "jetbrains.mps.lang.migration.structure.MoveNodeSpecialization");
  }
}
