package jetbrains.mps.ide.refactoring.plugin;

/*Generated by MPS */

import jetbrains.mps.ide.actions.ModelCreationActionsBaseExecutor;
import jetbrains.mps.ide.dialogs.project.creation.NewModelDialogSettings;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.project.MPSProject;
import org.jetbrains.mps.openapi.module.SModule;
import java.util.List;
import org.jetbrains.mps.openapi.module.SModuleReference;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import jetbrains.mps.smodel.ModelAccessHelper;
import jetbrains.mps.refactoring.participant.RefactoringProcessor;
import jetbrains.mps.refactoring.participant.plugin.DefaultRefactoringUI;
import jetbrains.mps.ide.dialogs.project.creation.NewModelDialogDefaultSettings;
import jetbrains.mps.ide.ui.tree.module.StereotypeProvider;
import jetbrains.mps.ide.IdeBundle;
import jetbrains.mps.refactoring.participant.RefactoringParticipant;
import jetbrains.mps.smodel.structure.ExtensionPoint;
import jetbrains.mps.refactoring.participant.MoveModelRefactoringParticipant;
import org.jetbrains.mps.openapi.model.EditableSModel;
import jetbrains.mps.ide.dialogs.project.creation.NewModelDialog;
import jetbrains.mps.project.AbstractModule;
import jetbrains.mps.refactoring.participant.RefactoringSession;
import java.util.Collection;
import jetbrains.mps.project.dependency.GlobalModuleDependenciesManager;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.smodel.ModelDependencyScanner;
import jetbrains.mps.workbench.actions.model.DeleteModelHelper;
import jetbrains.mps.ide.dialogs.project.creation.ModelCreateHelper;

public class MoveModelActionExecutor extends ModelCreationActionsBaseExecutor {

  protected final NewModelDialogSettings.Factory myDialogSettingsFactory;
  protected final SModel myOriginalModel;

  public MoveModelActionExecutor(MPSProject project, SModel originalModel) {
    this(project, originalModel, getDefaultSettingsFactoryForMoved(originalModel));
  }

  public MoveModelActionExecutor(MPSProject project, SModel originalModel, NewModelDialogSettings.Factory dialogSettingsFactory) {
    super(project);
    myOriginalModel = originalModel;
    myDialogSettingsFactory = dialogSettingsFactory;
  }

  @Override
  protected final SModule selectModule() {
    final List<SModuleReference> modules = ListSequence.fromList(new ArrayList<SModuleReference>());
    myProject.getRepository().getModelAccess().runReadAction(() -> {
      for (SModule module : ListSequence.fromList(myProject.getProjectModulesWithGenerators())) {
        ListSequence.fromList(modules).addElement(module.getModuleReference());
      }
    });
    final SModuleReference selectedModule = new MoveModelDialog(myProject, getTitle(), modules).showAndGetSelected();
    if (selectedModule == null) {
      return null;
    }

    return new ModelAccessHelper(myProject.getModelAccess()).runReadAction(() -> selectedModule.resolve(myProject.getRepository()));
  }


  @Override
  protected SModel showDialog(final SModule module) {
    MoveModelRefactoringBody refactoringBody = new MoveModelRefactoringBody(module);

    RefactoringProcessor.performRefactoringInProject(myProject, new DefaultRefactoringUI(myProject, refactoringBody.getRefactoringName()), refactoringBody);

    return refactoringBody.getNewModel();
  }


  public static NewModelDialogSettings.Factory getDefaultSettingsFactoryForMoved(SModel originalModel) {
    return NewModelDialogDefaultSettings.getFactory(getNameForMoved(originalModel), getStereotypeForMoved(originalModel));
  }

  public static String getNameForMoved(SModel originalModel) {
    return originalModel.getName().getLongName();
  }

  public static StereotypeProvider getStereotypeForMoved(final SModel model) {
    return StereotypeProvider.create(model.getName().getStereotype(), true);
  }

  private static String getTitle() {
    return IdeBundle.message("actions.model.move.title");
  }

  private class MoveModelRefactoringBody implements RefactoringProcessor.RefactoringBody<SModel, SModel> {
    private final Iterable<? extends RefactoringParticipant<?, ?, SModel, SModel>> myParticipants = new ExtensionPoint<MoveModelRefactoringParticipant<?, ?>>("jetbrains.mps.refactoring.participant.MoveModelParticipantEP").getObjects();
    private final SModule myModule;
    private EditableSModel myNewModel = null;

    public MoveModelRefactoringBody(SModule module) {
      myModule = module;
    }

    public String getRefactoringName() {
      return getTitle();
    }

    public Iterable<? extends RefactoringParticipant<?, ?, SModel, SModel>> getAllAvailableParticipants() {
      return myParticipants;
    }

    public List<SModel> findInitialStates() {
      return ListSequence.fromListAndArray(new ArrayList<SModel>(), myOriginalModel);
    }

    public void prepareRefactoring() {
      NewModelDialog dialog = new NewModelDialog(myProject, (AbstractModule) myModule, getTitle(), myDialogSettingsFactory);
      dialog.show();
      myNewModel = check_lf1t34_a0c0m22(check_lf1t34_a0a2a21w(dialog.getResultHelper(), myOriginalModel));
    }

    public void doRefactor(Iterable<RefactoringParticipant.ParticipantApplied<?, ?, SModel, SModel, SModel, SModel>> participantStates, RefactoringSession refactoringSession) {
      if (myNewModel == null) {
        return;
      }

      if (myNewModel.getModule() instanceof AbstractModule) {
        Collection<SModule> oldModuleDependencies = new GlobalModuleDependenciesManager(myOriginalModel.getModule()).getModules(GlobalModuleDependenciesManager.Deptype.VISIBLE);
        Collection<SModule> exisingModuleDependencies = new GlobalModuleDependenciesManager(myNewModel.getModule()).getModules(GlobalModuleDependenciesManager.Deptype.VISIBLE);
        for (SModelReference dependency : SetSequence.fromSet(new ModelDependencyScanner().crossModelReferences(true).usedLanguages(false).usedConcepts(false).walk(myNewModel).getCrossModelReferences())) {
          SModule depModule = dependency.resolve(myProject.getRepository()).getModule();
          if (!(exisingModuleDependencies.contains(depModule)) && oldModuleDependencies.contains(depModule)) {
            ((AbstractModule) myNewModel.getModule()).addDependency(depModule.getModuleReference(), false);
          }
        }
      }
      UpdateDependentModelsRefactoringParticipant.updateUsages(myNewModel, myOriginalModel.getReference(), myNewModel.getReference());
      DeleteModelHelper.delete(myOriginalModel.getModule(), myOriginalModel, true);
    }

    public SModel getFinalStateFor(SModel initialState) {
      return myNewModel;
    }

    public void doCleanup() {
      // do nothing
    }

    public EditableSModel getNewModel() {
      return myNewModel;
    }
  }
  private static EditableSModel check_lf1t34_a0c0m22(ModelCreateHelper checkedDotOperand) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.createModelHandleExceptions();
    }
    return null;
  }
  private static ModelCreateHelper check_lf1t34_a0a2a21w(ModelCreateHelper checkedDotOperand, SModel myOriginalModel) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.setClone(myOriginalModel, true);
    }
    return null;
  }
}
