package jetbrains.mps.vcs.history;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import org.jetbrains.annotations.NotNull;
import com.intellij.openapi.vcs.history.VcsFileRevision;
import java.util.List;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import java.util.Set;
import jetbrains.mps.internal.collections.runtime.SetSequence;
import java.util.HashSet;
import org.jetbrains.mps.openapi.model.SModel;
import java.util.Map;
import org.jetbrains.mps.openapi.model.SNodeId;
import jetbrains.mps.internal.collections.runtime.MapSequence;
import java.util.HashMap;
import com.intellij.openapi.vcs.history.CurrentRevision;
import jetbrains.mps.vcs.diff.changes.ModelChange;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.vcs.diff.changes.NodeIdChange;
import java.util.Collection;
import com.intellij.openapi.vcs.history.VcsRevisionNumber;
import git4idea.GitRevisionNumber;
import org.jetbrains.annotations.Nullable;
import com.intellij.openapi.vcs.VcsException;
import java.io.IOException;
import jetbrains.mps.vcspersistence.VCSPersistenceUtil;
import com.intellij.diff.contents.DiffContent;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vcs.AbstractVcs;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.diff.contents.EmptyContent;
import jetbrains.mps.vcs.platform.actions.VcsActionsUtil;
import com.intellij.openapi.vcs.history.VcsFileRevisionEx;
import java.util.Date;
import git4idea.GitFileRevision;
import com.intellij.openapi.application.ApplicationManager;
import com.intellij.vcs.log.impl.VcsLogApplicationSettings;
import com.intellij.vcs.log.impl.CommonUiProperties;
import com.intellij.openapi.vcs.annotate.AnnotationTooltipBuilder;
import git4idea.log.GitCommitTooltipLinkHandler;
import com.intellij.openapi.vcs.VcsBundle;
import com.intellij.util.text.DateFormatUtil;
import org.jetbrains.idea.svn.history.SvnFileRevision;

@GeneratedClass(node = "r:2897a5d4-aed7-4a4e-ac07-fbc830f9ed9b(jetbrains.mps.vcs.history)/7498720847979280337", model = "r:2897a5d4-aed7-4a4e-ac07-fbc830f9ed9b(jetbrains.mps.vcs.history)")
public final class CommitsGraphNode implements Comparable {
  @NotNull
  private final VcsFileRevision myRevision;
  private final List<CommitsGraphNode> myParents = ListSequence.fromList(new ArrayList<CommitsGraphNode>());
  private final Set<CommitsGraphNode> myChildren = SetSequence.fromSet(new HashSet<CommitsGraphNode>());
  private boolean myIsIgnored;
  private final List<CommitsGraphNode> myIgnoredByChildren = ListSequence.fromList(new ArrayList<CommitsGraphNode>());
  private SModel myModel;
  private SModel myLoadedModel;
  private boolean myModelCanBeUnloaded = true;
  private CommitsGraphNode myNodeWithLoadedModel;
  private Map<SNodeId, Set<SNodeId>> myChangedIds = MapSequence.fromMap(new HashMap<SNodeId, Set<SNodeId>>());


  public CommitsGraphNode(@NotNull VcsFileRevision revision) {
    myRevision = revision;
    myNodeWithLoadedModel = this;
  }

  public CommitsGraphNode(@NotNull CurrentRevision revision, SModel localModel) {
    myRevision = revision;
    myModel = localModel;
    myLoadedModel = localModel;
    myNodeWithLoadedModel = this;
  }

  public boolean isLocalRevision() {
    return myRevision instanceof CurrentRevision;
  }

  private Map<SNodeId, Set<SNodeId>> getChangedIds() {
    return myChangedIds;
  }

  private void addChangedIds(SNodeId oldId, Set<SNodeId> newIds) {
    Set<SNodeId> ids = MapSequence.fromMap(myChangedIds).get(oldId);
    if (ids == null) {
      ids = SetSequence.fromSet(new HashSet<SNodeId>());
      MapSequence.fromMap(myChangedIds).put(oldId, ids);
    }
    SetSequence.fromSet(ids).addSequence(SetSequence.fromSet(newIds));
  }

  public void setIdChanges(Iterable<ModelChange> modelChanges) {
    SetSequence.fromSet(myChildren).visitAll((child) -> MapSequence.fromMap(child.getNodeWithLoadedModel().getChangedIds()).visitAll((it) -> addChangedIds(it.key(), it.value())));
    Sequence.fromIterable(modelChanges).ofType(NodeIdChange.class).visitAll((nodeIdChange) -> {
      SNodeId oldId = nodeIdChange.getNodeId(false);
      SNodeId newId = nodeIdChange.getNodeId(true);
      Set<SNodeId> newIds = SetSequence.fromSet(new HashSet<SNodeId>());
      if (MapSequence.fromMap(myChangedIds).containsKey(newId)) {
        SetSequence.fromSet(newIds).addSequence(SetSequence.fromSet(MapSequence.fromMap(myChangedIds).get(newId)));
        MapSequence.fromMap(myChangedIds).removeKey(newId);
      } else {
        SetSequence.fromSet(newIds).addElement(newId);
      }
      addChangedIds(oldId, newIds);
    });
  }

  public Set<SNodeId> getCurrentNodeIds(SNodeId nodeId) {
    Set<SNodeId> currentIds = SetSequence.fromSet(new HashSet<SNodeId>());
    if (MapSequence.fromMap(myChangedIds).containsKey(nodeId)) {
      SetSequence.fromSet(currentIds).addSequence(SetSequence.fromSet(MapSequence.fromMap(myChangedIds).get(nodeId)));
    }
    SetSequence.fromSet(currentIds).addElement(nodeId);
    return currentIds;
  }

  /*package*/ void addParent(CommitsGraphNode parent) {
    ListSequence.fromList(myParents).addElement(parent);
    SetSequence.fromSet(parent.myChildren).addElement(this);
  }

  public List<CommitsGraphNode> getParents() {
    return myParents;
  }

  public List<CommitsGraphNode> getParentsWithRoot() {
    return ListSequence.fromList(myParents).where((it) -> !(it.isIgnored())).toList();
  }

  public Collection<CommitsGraphNode> getChildren() {
    return myChildren;
  }

  @NotNull
  public VcsFileRevision getRevision() {
    return myRevision;
  }

  private static void calcAncestors(CommitsGraphNode node, Set<CommitsGraphNode> result) {
    if (SetSequence.fromSet(result).contains(node)) {
      return;
    }
    SetSequence.fromSet(result).addElement(node);
    for (CommitsGraphNode parent : node.getParents()) {
      calcAncestors(parent, result);
    }
  }

  public boolean isDescendantOf(CommitsGraphNode node) {
    return SetSequence.fromSet(getAncestors()).contains(node);
  }

  private Set<CommitsGraphNode> getAncestors() {
    Set<CommitsGraphNode> result = SetSequence.fromSet(new HashSet<CommitsGraphNode>());
    for (CommitsGraphNode parent : myParents) {
      calcAncestors(parent, result);
    }
    return result;
  }

  @Override
  public String toString() {
    VcsRevisionNumber rn = myRevision.getRevisionNumber();
    if (rn instanceof GitRevisionNumber) {
      GitRevisionNumber grn = as_9s317u_a0a0a1a24(rn, GitRevisionNumber.class);
      return grn.getShortRev() + "/" + myRevision.getCommitMessage();
    }
    return rn.asString();
  }

  /*package*/ void setIgnored() {
    myIsIgnored = true;
  }

  public boolean isIgnored() {
    return myIsIgnored;
  }

  /*package*/ void loadModel(@Nullable CommitsGraphNode childNode, String fileExtension) throws VcsException, IOException {
    if (isModelLoaded()) {
      return;
    }
    try {
      myModel = VCSPersistenceUtil.loadModel(myRevision.loadContent(), fileExtension);
    } finally {
      setLoadedModel(childNode);
    }
  }

  private void setLoadedModel(CommitsGraphNode childNode) {
    if (myModel != null && myModel.isLoaded()) {
      myLoadedModel = myModel;
      myNodeWithLoadedModel = this;
    } else if (childNode != null) {
      myLoadedModel = childNode.getLoadedModel();
      myNodeWithLoadedModel = childNode.getNodeWithLoadedModel();
      childNode.doNotUnload();
    }
  }

  public CommitsGraphNode getNodeWithLoadedModel() {
    return myNodeWithLoadedModel;
  }

  /*package*/ SModel getModel() {
    return myModel;
  }

  @Nullable
  public SModel getLoadedModel() {
    return myLoadedModel;
  }

  /*package*/ void unloadModel() {
    if (isLocalRevision() || (SetSequence.fromSet(myChildren).isNotEmpty() && SetSequence.fromSet(myChildren).first().isLocalRevision())) {
      return;
    }
    if (myModelCanBeUnloaded) {
      check_9s317u_a0a1a85(myLoadedModel);
    }
    myLoadedModel = null;
    myModel = null;
  }

  public void doNotUnload() {
    myModelCanBeUnloaded = false;
  }

  public boolean isModelLoaded() {
    return myLoadedModel != null && myLoadedModel.isLoaded();
  }

  /*package*/ boolean isOwnModelLoaded() {
    return myModel == myLoadedModel;
  }

  public void setIgnoredByChild(CommitsGraphNode child) {
    ListSequence.fromList(myIgnoredByChildren).addElement(child);
  }

  /*package*/ boolean isIgnoredByChild(CommitsGraphNode child) {
    return ListSequence.fromList(myIgnoredByChildren).contains(child);
  }

  public List<DiffContent> createContents(Project ideaProject, AbstractVcs vcs, VirtualFile vFile) throws VcsException {
    List<DiffContent> contents = ListSequence.fromList(new ArrayList<DiffContent>());
    if (ListSequence.fromList(myParents).isNotEmpty()) {
      ListSequence.fromList(contents).addElement(createDiffContent(ListSequence.fromList(myParents).getElement(0), ideaProject, vcs, vFile));
    } else {
      ListSequence.fromList(contents).addElement(new EmptyContent());
    }
    ListSequence.fromList(contents).addElement(createDiffContent(this, ideaProject, vcs, vFile));
    if (ListSequence.fromList(myParents).count() == 2) {
      ListSequence.fromList(contents).addElement(createDiffContent(ListSequence.fromList(myParents).getElement(1), ideaProject, vcs, vFile));
    }
    return contents;
  }

  private static DiffContent createDiffContent(CommitsGraphNode node, Project ideaProject, AbstractVcs vcs, VirtualFile vFile) throws VcsException {
    if (node.isLocalRevision()) {
      return VcsActionsUtil.createCurrentContent(ideaProject, vFile);
    }
    return VcsActionsUtil.createRevisionContent(ideaProject, vcs, node.getRevision().getRevisionNumber(), vFile);
  }

  public List<String> createTitles() {
    List<String> titles = ListSequence.fromList(new ArrayList<String>());
    if (ListSequence.fromList(myParents).isNotEmpty()) {
      ListSequence.fromList(titles).addElement(createTitle(ListSequence.fromList(myParents).getElement(0).getRevision()));
    } else {
      ListSequence.fromList(titles).addElement("");
    }
    ListSequence.fromList(titles).addElement(createTitle(myRevision));
    if (ListSequence.fromList(myParents).count() == 2) {
      ListSequence.fromList(titles).addElement(createTitle(ListSequence.fromList(myParents).getElement(1).getRevision()));
    }
    return titles;
  }

  private static String createTitle(@NotNull VcsFileRevision revision) {
    return revision.getRevisionNumber().asString();
  }

  @Override
  public int compareTo(Object object) {
    if (object == this) {
      return 0;
    }
    if (isLocalRevision()) {
      return 1;
    }
    if (((CommitsGraphNode) object).isLocalRevision()) {
      return -1;
    }
    VcsFileRevision otherRevision = ((CommitsGraphNode) object).getRevision();
    int compareResult = myRevision.getRevisionDate().compareTo(otherRevision.getRevisionDate());
    if (compareResult == 0 && myRevision instanceof VcsFileRevisionEx && otherRevision instanceof VcsFileRevisionEx) {
      Date date1 = ((VcsFileRevisionEx) myRevision).getAuthorDate();
      Date date2 = ((VcsFileRevisionEx) otherRevision).getAuthorDate();
      if (date1 != null && date2 != null) {
        compareResult = date1.compareTo(date2);
      }
    }
    return compareResult;
  }

  public String getRevisionDescription(Project project) {
    if (isLocalRevision()) {
      return "Local Changes";
    }

    String tooltipText = null;
    if (myRevision instanceof GitFileRevision) {
      GitFileRevision gitRevision = (GitFileRevision) myRevision;
      Date date = (Boolean.TRUE.equals(ApplicationManager.getApplication().getService(VcsLogApplicationSettings.class).get(CommonUiProperties.PREFER_COMMIT_DATE)) ? gitRevision.getRevisionDate() : gitRevision.getAuthorDate());

      AnnotationTooltipBuilder atb = new AnnotationTooltipBuilder(project, true);

      atb.appendRevisionLine(myRevision.getRevisionNumber(), (VcsRevisionNumber number) -> GitCommitTooltipLinkHandler.createLink(number.asString(), number));
      atb.appendLine(VcsBundle.message("commit.description.tooltip.author", myRevision.getAuthor()));
      atb.appendLine(VcsBundle.message("commit.description.tooltip.date", DateFormatUtil.formatDate(date)));
      atb.appendCommitMessageBlock(myRevision.getCommitMessage());
      tooltipText = atb.toString();
    } else if (myRevision instanceof SvnFileRevision) {
      tooltipText = AnnotationTooltipBuilder.buildSimpleTooltip(project, true, "Revision", String.valueOf(myRevision.getRevisionNumber()), myRevision.getCommitMessage());
    }
    if (tooltipText != null) {
      tooltipText = tooltipText.replace("\n", "<br>");
    }
    return tooltipText;
  }
  private static void check_9s317u_a0a1a85(SModel checkedDotOperand) {
    if (null != checkedDotOperand) {
      checkedDotOperand.unload();
    }

  }
  private static <T> T as_9s317u_a0a0a1a24(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
}
