package jetbrains.mps.ide.mpsmigration.v_2022_3;

/*Generated by MPS */

import jetbrains.mps.migration.global.BaseProjectMigration;
import jetbrains.mps.project.Project;
import org.jetbrains.mps.openapi.persistence.PersistenceFacade;
import jetbrains.mps.persistence.PersistenceRegistry;
import jetbrains.mps.smodel.SModelStereotype;
import jetbrains.mps.smodel.LanguageID;
import org.jetbrains.mps.openapi.module.SModuleReference;
import org.jetbrains.mps.openapi.module.SModule;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.project.AbstractModule;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.internal.collections.runtime.Sequence;
import java.util.Set;
import org.jetbrains.mps.openapi.model.SModelReference;
import java.util.HashSet;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SNodeUtil;
import org.jetbrains.mps.openapi.model.SReference;
import jetbrains.mps.smodel.StaticReference;
import jetbrains.mps.smodel.ModelImports;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import jetbrains.mps.project.structure.modules.ModuleDescriptor;
import jetbrains.mps.project.structure.modules.Dependency;

public class SplitMPSCoreStub2 extends BaseProjectMigration {
  public SplitMPSCoreStub2() {
    super(SplitMPSCoreStub2.class.getName(), 223);
  }

  @Override
  public String getDescription() {
    return "Reroute stub references to gnu.trove and org.jdom";
  }


  @Override
  public boolean doExecute(Project project) {
    final PersistenceFacade pf = project.getComponent(PersistenceRegistry.class);
    final String javaStubStereotype = SModelStereotype.getStubStereotypeForId(LanguageID.JAVA);
    final SModuleReference mpsCoreStubModule = PersistenceFacade.getInstance().createModuleReference("6ed54515-acc8-4d1e-a16c-9fd6cfe951ea(MPS.Core)");
    final SModuleReference newGnuTroveModule = PersistenceFacade.getInstance().createModuleReference("5a9ccb4c-d683-45a8-bc1d-ecfdfb8366f0(gnu.trove)");
    final SModuleReference newOrgJdomModule = PersistenceFacade.getInstance().createModuleReference("f647e48e-4568-4f4c-b48a-1546492c6a2e(org.jdom)");

    for (SModule pm : ListSequence.fromList(project.getProjectModulesWithGenerators())) {
      if (pm.isReadOnly()) {
        continue;
      }
      if (false == pm instanceof AbstractModule || ((AbstractModule) pm).getModuleDescriptor() == null) {
        continue;
      }
      boolean dependencyGnuTrove = false;
      boolean dependencyOrgJdom = false;

      for (SModel m : Sequence.fromIterable(pm.getModels())) {
        if (m.isReadOnly()) {
          continue;
        }
        Set<SModelReference> importsToDrop = new HashSet<>();
        Set<SModelReference> importsToAdd = new HashSet<>();

        for (SNode n : Sequence.fromIterable(SNodeUtil.getDescendants(m))) {
          for (SReference ref : Sequence.fromIterable(n.getReferences())) {
            if (false == ref instanceof StaticReference) {
              continue;
            }
            SModelReference refTarget = ref.getTargetSModelReference();
            if (refTarget == null) {
              continue;
            }
            if (!(refTarget.getName().hasStereotype(javaStubStereotype))) {
              continue;
            }
            if (!(mpsCoreStubModule.equals(refTarget.getModuleReference()))) {
              continue;
            }
            if (refTarget.getName().getValue().startsWith("gnu.trove")) {
              int length = "gnu.trove".length();
              String longName = refTarget.getName().getLongName();
              if (longName.length() == length || longName.length() > length && longName.charAt(length) == '.') {
                SModelReference newTarget = pf.createModelReference(newGnuTroveModule, refTarget.getModelId(), refTarget.getName());
                importsToDrop.add(refTarget);
                importsToAdd.add(newTarget);
                ((StaticReference) ref).setTargetSModelReference(newTarget);
                dependencyGnuTrove = true;
              }
              continue;
            }
            if (refTarget.getName().getValue().startsWith("org.jdom")) {
              int length = "org.jdom".length();
              String longName = refTarget.getName().getLongName();
              if (longName.length() == length || longName.length() > length && longName.charAt(length) == '.') {
                SModelReference newTarget = pf.createModelReference(newOrgJdomModule, refTarget.getModelId(), refTarget.getName());
                importsToDrop.add(refTarget);
                importsToAdd.add(newTarget);
                ((StaticReference) ref).setTargetSModelReference(newTarget);
                dependencyOrgJdom = true;
              }
              continue;
            }
          }
          // foreach reference
        }
        // foreach node
        //  
        if (!(importsToAdd.isEmpty()) || !(importsToDrop.isEmpty())) {
          ModelImports mi = new ModelImports(m);
          // drop first, as there might be a resurrection with importsToAdd
          for (SModelReference i : SetSequence.fromSet(importsToDrop)) {
            mi.removeModelImport(i);
          }
          for (SModelReference i : SetSequence.fromSet(importsToAdd)) {
            mi.addModelImport(i);
          }
        }
        // SplitMPSCoreStub didn't model.save here
      }
      if (dependencyOrgJdom || dependencyGnuTrove) {
        AbstractModule am = (AbstractModule) pm;
        ModuleDescriptor md = am.getModuleDescriptor();
        assert md != null;
        if (dependencyOrgJdom) {
          md.getDependencies().add(new Dependency(newOrgJdomModule, false));
        }
        if (dependencyGnuTrove) {
          md.getDependencies().add(new Dependency(newGnuTroveModule, false));
        }
        am.setModuleDescriptor(md);
        // SplitMPSCoreStub doesn't care to model.save()/module.save() 
      }
    }
    return true;
  }


  @Override
  public boolean isRerunnable() {
    return true;
  }

}
