package jetbrains.mps.vcs.platform.mergedriver;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.logging.Logger;
import java.io.File;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.vcs.platform.util.PluginUtil;
import java.util.List;
import com.intellij.openapi.vcs.VcsRoot;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.Iterator;
import jetbrains.mps.util.NameUtil;
import com.intellij.openapi.ui.Messages;
import jetbrains.mps.internal.collections.runtime.Sequence;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import git4idea.GitVcs;
import com.intellij.openapi.vcs.VcsException;
import jetbrains.mps.vcs.core.mergedriver.MergeDriverMain;

@GeneratedClass(node = "r:36539f52-7ec3-4937-98bf-1fbc1fbe99fc(jetbrains.mps.vcs.platform.mergedriver)/572687202609936946", model = "r:36539f52-7ec3-4937-98bf-1fbc1fbe99fc(jetbrains.mps.vcs.platform.mergedriver)")
/*package*/ class GitMergeDriverInstaller extends AbstractInstaller {
  private static final Logger LOG = Logger.getLogger(GitMergeDriverInstaller.class);
  private static final String MERGE_DRIVER = "merge.mps.driver";
  private static final String MERGE_DRIVER_NAME = "merge.mps.name";

  private static final String MERGE_DRIVER_TITLE = "MPS merge driver";
  private final File myScriptFile = new File(PathManager.getConfigPath(), "mps-merger.sh");
  private final String mpsMergeDriver;

  public GitMergeDriverInstaller(Project project) {
    super(project);
    String scriptPath = myScriptFile.getAbsolutePath();
    if (SystemInfo.isWindows) {
      scriptPath = CommandLineGenerator.adaptPathForMsysGit(scriptPath);
    }
    mpsMergeDriver = String.format("\"%s\" %%O %%A %%B %%L %%P", scriptPath);
  }

  @NotNull
  @Override
  protected AbstractInstaller.State install(boolean dryRun) {
    if (!(PluginUtil.isGitPluginEnabled())) {
      return AbstractInstaller.State.NOT_ENABLED;
    }

    List<VcsRoot> gitRoots = getGitRoots();
    // check if we need something to install
    List<AbstractInstaller.State> states = getAllStates(gitRoots);

    if (dryRun) {
      if (ListSequence.fromList(states).all((s) -> s == AbstractInstaller.State.INSTALLED)) {
        return AbstractInstaller.State.INSTALLED;
      }
      return (ListSequence.fromList(states).any((s) -> s == AbstractInstaller.State.NOT_INSTALLED) ? AbstractInstaller.State.NOT_INSTALLED : AbstractInstaller.State.OUTDATED);
    }

    // copy driver files to the proper place (only once)
    MergeDriverPacker.getInstance().pack(myProject);
    // install settings for each repository
    int failed = 0;
    int installed = 0;
    String failedRoots = "";
    {
      Iterator<VcsRoot> root_it = ListSequence.fromList(gitRoots).iterator();
      Iterator<AbstractInstaller.State> s_it = ListSequence.fromList(states).iterator();
      VcsRoot root_var;
      AbstractInstaller.State s_var;
      while (root_it.hasNext() && s_it.hasNext()) {
        root_var = root_it.next();
        s_var = s_it.next();
        if (s_var == AbstractInstaller.State.INSTALLED) {
          continue;
        }
        if (installForRoot(root_var, false) == AbstractInstaller.State.INSTALLED) {
          installed++;
        } else {
          failedRoots += "\n" + root_var.getPath();
          failed++;
        }
      }
    }
    // show message about results
    String resultMessage = "merge driver" + ((installed > 0 ? " and settings for " + NameUtil.formatNumericalString(installed, "repository") : "")) + " updated.";
    if (failed > 0) {
      resultMessage += "\n" + NameUtil.formatNumericalString(failed, "repository") + " not updated:" + failedRoots;
    }
    Messages.showInfoMessage(myProject, resultMessage, "Git Merge Driver Installer");

    return (failed == 0 ? AbstractInstaller.State.INSTALLED : AbstractInstaller.State.NOT_INSTALLED);
  }

  private List<VcsRoot> getGitRoots() {
    return Sequence.fromIterable(Sequence.fromArray(ProjectLevelVcsManager.getInstance(myProject).getAllVcsRoots())).where((it) -> it.getVcs() instanceof GitVcs).toList();
  }
  private List<AbstractInstaller.State> getAllStates(List<VcsRoot> roots) {
    return ListSequence.fromList(roots).select((r) -> installForRoot(r, true)).toList();
  }
  private List<AbstractInstaller.State> getAllStates() {
    return getAllStates(getGitRoots());
  }

  private AbstractInstaller.State installForRoot(VcsRoot vcsRoot, boolean dryRun) {
    try {
      String currentDriver = GitConfigUtilWrapped.getValue(myProject, vcsRoot.getPath(), MERGE_DRIVER);
      if ((currentDriver == null || currentDriver.length() == 0) && dryRun) {
        return AbstractInstaller.State.NOT_INSTALLED;
      }
      if (!(mpsMergeDriver.equals(currentDriver)) && dryRun) {
        return AbstractInstaller.State.OUTDATED;
      }
    } catch (VcsException e) {
      if (LOG.isWarningLevel()) {
        LOG.warning("Can't get value", e);
      }
      Messages.showErrorDialog(myProject, "Can't get current Git merge driver settings: " + e.getMessage(), "Git Merge Driver Settings");
      return AbstractInstaller.State.NOT_INSTALLED;
    }

    AbstractInstaller.State createScriptResult = ScriptGenerator.generateScript(myProject, MergeDriverMain.NO_FILETYPE, ScriptGenerator.GIT, myScriptFile, dryRun);
    if (dryRun || createScriptResult != AbstractInstaller.State.INSTALLED) {
      return createScriptResult;
    }

    try {
      GitConfigUtilWrapped.setValue(myProject, vcsRoot.getPath(), MERGE_DRIVER_NAME, MERGE_DRIVER_TITLE);
      GitConfigUtilWrapped.setValue(myProject, vcsRoot.getPath(), MERGE_DRIVER, mpsMergeDriver);
      return AbstractInstaller.State.INSTALLED;
    } catch (VcsException e) {
      if (LOG.isWarningLevel()) {
        LOG.warning("Can't set value", e);
      }
      Messages.showErrorDialog(myProject, "Can't set up Git merge driver: " + e.getMessage(), "Git Merge Driver Installer");
      return AbstractInstaller.State.NOT_INSTALLED;
    }
  }

  @Override
  public String getActionTitle() {
    List<AbstractInstaller.State> statuses = getAllStates();
    return "Git merge driver and settings for " + ListSequence.fromList(statuses).where((it) -> it != AbstractInstaller.State.INSTALLED).count() + " out of " + NameUtil.formatNumericalString(ListSequence.fromList(statuses).count(), "repository");
  }
  @Override
  public String getAffectedVcsName() {
    return "Git";
  }
}
