package jetbrains.mps.vcs;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import com.intellij.openapi.components.ProjectComponent;
import jetbrains.mps.logging.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.FileStatus;
import com.intellij.util.messages.MessageBusConnection;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vcs.ProjectLevelVcsManager;
import com.intellij.openapi.vcs.changes.VcsDirtyScopeImpl;
import com.intellij.openapi.vcs.actions.VcsContextFactory;
import com.intellij.openapi.vcs.changes.ChangeProvider;
import com.intellij.openapi.progress.EmptyProgressIndicator;
import com.intellij.openapi.vcs.VcsException;
import jetbrains.mps.vcs.platform.mergedriver.MergeDriverNotification;
import com.intellij.openapi.vcs.VcsListener;
import com.intellij.openapi.vcs.FileStatusManager;
import com.intellij.openapi.application.Application;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.openapi.ui.Messages;
import com.intellij.openapi.vcs.AbstractVcsHelper;
import java.util.Arrays;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.vcs.changes.ChangeListManagerGate;
import java.util.List;
import com.intellij.openapi.vcs.changes.LocalChangeList;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import com.intellij.openapi.vcs.FilePath;
import com.intellij.openapi.vcs.changes.EmptyChangelistBuilder;
import com.intellij.openapi.vcs.changes.Change;
import com.intellij.openapi.vcs.changes.ChangeList;
import com.intellij.openapi.vcs.VcsKey;
import com.intellij.openapi.vcs.changes.ContentRevision;
import com.intellij.openapi.vcs.FileStatusListener;

@GeneratedClass(node = "r:cd7c9d90-25b3-4a54-a510-a0bcc7072c1d(jetbrains.mps.vcs)/3613324658897711340", model = "r:cd7c9d90-25b3-4a54-a510-a0bcc7072c1d(jetbrains.mps.vcs)")
public class MPSVcsManager implements ProjectComponent {
  private static final Logger LOG = Logger.getLogger(MPSVcsManager.class);
  private final Project myProject;
  private FileStatus myLastProjectStatus;

  private MyFileStatusListener myFileStatusListener = new MyFileStatusListener();
  private MessageBusConnection myMessageBusConnection;

  public MPSVcsManager(Project project) {
    myProject = project;
  }

  /**
   * This method is not in use now, but hold your temptation to remove it.
   * First, get some historic insight, read comment in problemsDetected() of ModelMergeConflictTracker.
   * Then, please explain me the difference between use of FileStatusManager in ConflictsUtil vs 
   * use of ChangeProvider in this method. Which one is right?
   */
  public boolean isInConflict(final VirtualFile vfile) {
    AbstractVcs vcs = ProjectLevelVcsManager.getInstance(myProject).getVcsFor(vfile);
    if (vcs == null) {
      return false;
    }
    VcsDirtyScopeImpl scope = new VcsDirtyScopeImpl(vcs);
    scope.addDirtyFile(VcsContextFactory.SERVICE.getInstance().createFilePathOn(vfile));
    ChangeProvider changeProvider = vcs.getChangeProvider();
    if (changeProvider == null) {
      return false;
    }
    final MyChangelistBuilder builder = new MyChangelistBuilder(vfile);
    try {
      changeProvider.getChanges(scope, builder, new EmptyProgressIndicator(), new StubChangeListManagerGate());
    } catch (VcsException e) {
      if (LOG.isErrorLevel()) {
        LOG.error("", e);
      }
    }
    return builder.isInConflict();
  }

  @Override
  public void projectOpened() {
    if (projectOpenCloseEventIgnored()) {
      return;
    }
    // XXX could be StartupActivity 
    final MergeDriverNotification mergeDriverNotification = MergeDriverNotification.getInstance(myProject);
    mergeDriverNotification.showNotificationIfNeeded();
    // XXX Could be <projectListener>
    myMessageBusConnection = myProject.getMessageBus().connect();
    VcsListener vcsListener = () -> mergeDriverNotification.showNotificationIfNeeded();
    myMessageBusConnection.subscribe(ProjectLevelVcsManager.VCS_CONFIGURATION_CHANGED, vcsListener);
    // nah, this one has to be in code; although its use to detect merge conflict of a single project file is dubious
    // TODO: Verify correct Disposable used
    FileStatusManager.getInstance(myProject).addFileStatusListener(myFileStatusListener, myProject);
  }

  @Override
  public void projectClosed() {
    if (projectOpenCloseEventIgnored()) {
      return;
    }
    myMessageBusConnection.disconnect();
  }

  private boolean projectOpenCloseEventIgnored() {
    Application app = ApplicationManager.getApplication();
    return app.isUnitTestMode() || app.isHeadlessEnvironment() || myProject.isDefault();
  }

  private void checkIfProjectIsConflicting() {
    VirtualFile projectFile = myProject.getProjectFile();
    if (projectFile == null) {
      return;
    }
    FileStatus currentStatus = FileStatusManager.getInstance(myProject).getStatus(projectFile);
    if (currentStatus != myLastProjectStatus) {
      if (currentStatus == FileStatus.MERGED_WITH_CONFLICTS || currentStatus == FileStatus.MERGED_WITH_BOTH_CONFLICTS) {
        int answer = Messages.showYesNoDialog(myProject, "You have your project file unmerged. It is strongly recommended to merge it before continuing. " + "\nDo you want to merge it now?", "Unmerged Project File", Messages.getWarningIcon());
        if (answer == 0) {
          AbstractVcsHelper.getInstance(myProject).showMergeDialog(Arrays.asList(projectFile));
        }
      }
      myLastProjectStatus = currentStatus;
    }
  }

  public static MPSVcsManager getInstance(@NotNull Project project) {
    return project.getComponent(MPSVcsManager.class);
  }

  public static class StubChangeListManagerGate implements ChangeListManagerGate {
    public StubChangeListManagerGate() {
    }
    @Override
    public void setDefaultChangeList(@NotNull String string) {
    }
    @Override
    public List<LocalChangeList> getListsCopy() {
      return null;
    }
    @Nullable
    @Override
    public LocalChangeList findChangeList(String name) {
      return null;
    }
    @Override
    public LocalChangeList addChangeList(String name, String comment) {
      return null;
    }
    @Override
    public LocalChangeList findOrCreateList(String name, String comment) {
      return null;
    }
    @Override
    public void editComment(String name, String comment) {
    }
    @Override
    public void editName(String oldName, String newName) {
    }
    @Override
    public void setListsToDisappear(Collection<String> names) {
    }
    @Override
    public FileStatus getStatus(VirtualFile virtualFile) {
      return FileStatus.NOT_CHANGED;
    }
    @Nullable
    public FileStatus getStatus(@NotNull FilePath path) {
      return FileStatus.NOT_CHANGED;
    }
  }

  private static class MyChangelistBuilder extends EmptyChangelistBuilder {
    private final VirtualFile myVirtualFile;
    private boolean myIsMergedWithConflict;
    public MyChangelistBuilder(VirtualFile vfile) {
      myVirtualFile = vfile;
    }
    @Override
    public void processChangeInList(Change change, @Nullable ChangeList changeList, VcsKey vcsKey) {
      processChange(change, vcsKey);
    }
    @Override
    public void processChangeInList(Change change, String changeListName, VcsKey vcsKey) {
      processChange(change, vcsKey);
    }
    @Override
    public void processChange(Change change, VcsKey vcsKey) {
      if (change.getFileStatus().equals(FileStatus.MERGED_WITH_CONFLICTS)) {
        ContentRevision contentRevision = change.getAfterRevision();
        if (contentRevision != null) {
          if (contentRevision.getFile().getPresentableUrl().equals(myVirtualFile.getPresentableUrl())) {
            myIsMergedWithConflict = true;
          }
        }
      }
    }
    public boolean isInConflict() {
      return myIsMergedWithConflict;
    }
  }

  public class MyFileStatusListener implements FileStatusListener {
    private MyFileStatusListener() {
    }
    @Override
    public void fileStatusesChanged() {
      checkIfProjectIsConflicting();
    }
    @Override
    public void fileStatusChanged(@NotNull VirtualFile file) {
      if (file.equals(myProject.getProjectFile())) {
        checkIfProjectIsConflicting();
      }
    }
  }
}
