package jetbrains.mps.vcs.annotate;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.vcs.history.CommitsGraphNode;
import jetbrains.mps.errors.messageTargets.MessageTarget;
import jetbrains.mps.vcs.diff.changes.ChangeType;
import java.util.List;
import org.jetbrains.mps.openapi.model.SNodeId;
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 java.util.Collection;
import org.jetbrains.mps.openapi.model.SModel;
import jetbrains.mps.internal.collections.runtime.CollectionSequence;
import jetbrains.mps.errors.messageTargets.NodeMessageTarget;
import jetbrains.mps.vcs.diff.changes.StructureChange;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import jetbrains.mps.errors.messageTargets.DeletedNodeMessageTarget;
import java.util.Collections;
import jetbrains.mps.vcs.diff.changes.NodeGroupMoveChange;
import jetbrains.mps.vcs.diff.changes.NodeGroupWrapChange;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.errors.messageTargets.PropertyMessageTarget;
import java.util.Objects;
import jetbrains.mps.errors.messageTargets.ReferenceMessageTarget;

@GeneratedClass(node = "r:f509a650-cbd9-47e7-b2a0-79f49c562c0b(jetbrains.mps.vcs.annotate)/5555299221747429994", model = "r:f509a650-cbd9-47e7-b2a0-79f49c562c0b(jetbrains.mps.vcs.annotate)")
/*package*/ class RevisionNodeChange {

  @NotNull
  private final CommitsGraphNode myRevisionGraphNode;
  @NotNull
  private final MessageTarget myMessageTarget;
  @NotNull
  private final ChangeType myChangeType;
  private final String myMessage;
  @NotNull
  private final List<SNodeId> myMovedChildIds = ListSequence.fromList(new ArrayList<SNodeId>());
  @NotNull
  private final Set<SNodeId> myNodeIds = SetSequence.fromSet(new HashSet<SNodeId>());


  private RevisionNodeChange(@NotNull final CommitsGraphNode graphNode, @NotNull ChangeType changeType, String description, final MessageTarget messageTarget, Collection<SNodeId> nodeIds, final SModel model, @NotNull final Set<SNodeId> movedNodesIds) {

    myMessage = description;
    myChangeType = changeType;
    myRevisionGraphNode = graphNode;
    myMessageTarget = messageTarget;

    CollectionSequence.fromCollection(nodeIds).visitAll((id) -> {
      SetSequence.fromSet(myNodeIds).addSequence(SetSequence.fromSet(graphNode.getCurrentNodeIds(id)));
      if (messageTarget instanceof NodeMessageTarget) {
        collectChildNodeIds(model.getNode(id), movedNodesIds);
      }
    });
  }

  private RevisionNodeChange(@NotNull final CommitsGraphNode graphNode, ChangeType changeType, String description, MessageTarget messageTarget, Collection<SNodeId> nodesIds, SModel model) {

    myMessage = description;
    myChangeType = changeType;
    myRevisionGraphNode = graphNode;
    myMessageTarget = messageTarget;

    CollectionSequence.fromCollection(nodesIds).visitAll((id) -> SetSequence.fromSet(myNodeIds).addSequence(SetSequence.fromSet(graphNode.getCurrentNodeIds(id))));
    // this constructor is for move changes and for move parts of wrap changes
    collectMovedChildIds(nodesIds, model);
  }

  private static RevisionNodeChange createMoveNodeChange(@NotNull CommitsGraphNode graphNode, ChangeType changeType, String description, MessageTarget messageTarget, Collection<SNodeId> nodesIds, SModel model) {
    return new RevisionNodeChange(graphNode, changeType, description, messageTarget, nodesIds, model);
  }

  private static RevisionNodeChange createNotMoveNodeChange(@NotNull CommitsGraphNode graphNode, @NotNull ChangeType changeType, String description, MessageTarget messageTarget, Collection<SNodeId> nodeIds, SModel model, @NotNull Set<SNodeId> movedNodesIds) {
    return new RevisionNodeChange(graphNode, changeType, description, messageTarget, nodeIds, model, movedNodesIds);
  }


  @NotNull
  /*package*/ static Collection<RevisionNodeChange> createRevisionNodeChanges(@NotNull final CommitsGraphNode graphNode, @NotNull StructureChange modelChange, @NotNull Set<SNodeId> movedNodesIds) {

    List<Tuples._2<SNodeId, MessageTarget>> targetIds = modelChange.createMessageTargetsWithIds(true);
    final MessageTarget messageTarget = ListSequence.fromList(targetIds).first()._1();

    if (messageTarget instanceof DeletedNodeMessageTarget) {
      return Collections.emptyList();
    }

    List<SNodeId> nodeIds = ListSequence.fromList(targetIds).select((it) -> it._0()).toList();
    final SModel model = modelChange.getChangeSet().getNewModel();
    final String description = modelChange.getShortDescription();
    final ChangeType changeType = modelChange.getType();

    if (modelChange instanceof NodeGroupMoveChange) {
      return Collections.singletonList(createMoveNodeChange(graphNode, changeType, description, messageTarget, ((NodeGroupMoveChange) modelChange).getIds(true), model));
    }

    if (!(modelChange instanceof NodeGroupWrapChange)) {
      return Collections.singletonList(createNotMoveNodeChange(graphNode, changeType, description, messageTarget, nodeIds, model, movedNodesIds));
    }

    NodeGroupWrapChange wrapChange = as_uhcuom_a0a51a81(modelChange, NodeGroupWrapChange.class);
    final List<RevisionNodeChange> changes = ListSequence.fromList(new ArrayList<RevisionNodeChange>());

    if (wrapChange.isWrap()) {
      Collection<SNodeId> wrappingIds = Collections.singletonList(wrapChange.getWrappingGroup().getWrappingNodeId());
      ListSequence.fromList(changes).addElement(createNotMoveNodeChange(graphNode, changeType, description, messageTarget, wrappingIds, model, movedNodesIds));
    } else {
      ListSequence.fromList(wrapChange.getWrappingGroup().getWrappedGroups()).where((it) -> it.isWrappedMove()).visitAll((it) -> ListSequence.fromList(changes).addElement(createMoveNodeChange(graphNode, changeType, description, messageTarget, it.getIds(), model)));
    }
    return changes;
  }


  @NotNull
  /*package*/ MessageTarget getMessageTarget() {
    return myMessageTarget;
  }

  @NotNull
  /*package*/ Set<SNodeId> getNodeIds() {
    return myNodeIds;
  }

  @NotNull
  /*package*/ List<SNodeId> getMovedChildIds() {
    return myMovedChildIds;
  }

  @NotNull
  /*package*/ CommitsGraphNode getCommitsGraphNode() {
    return myRevisionGraphNode;
  }

  /*package*/ String getMessage() {
    return myMessage;
  }

  @NotNull
  /*package*/ ChangeType getChangeType() {
    return myChangeType;
  }

  private void collectMovedChildIds(Collection<SNodeId> movedNodeIds, final SModel model) {
    CollectionSequence.fromCollection(movedNodeIds).select((it) -> (SNode) model.getNode(it)).translate((movedNode) -> SNodeOperations.getChildren(movedNode)).visitAll((child) -> ListSequence.fromList(myMovedChildIds).addSequence(SetSequence.fromSet(myRevisionGraphNode.getCurrentNodeIds(child.getNodeId()))));
  }

  private void collectChildNodeIds(SNode node, Set<SNodeId> movedNodesIds) {
    for (SNode child : ListSequence.fromList(SNodeOperations.getChildren(node))) {
      SNodeId childId = child.getNodeId();
      if (!(SetSequence.fromSet(movedNodesIds).contains(childId))) {
        SetSequence.fromSet(myNodeIds).addSequence(SetSequence.fromSet(myRevisionGraphNode.getCurrentNodeIds(childId)));
        collectChildNodeIds(child, movedNodesIds);
      } else {
        ListSequence.fromList(myMovedChildIds).addSequence(SetSequence.fromSet(myRevisionGraphNode.getCurrentNodeIds(childId)));
      }
    }
  }

  /*package*/ boolean sameAs(@NotNull RevisionNodeChange otherChange) {
    MessageTarget otherMessageTarget = otherChange.getMessageTarget();
    if (otherMessageTarget.getClass() != myMessageTarget.getClass()) {
      return false;
    }
    if (myMessageTarget instanceof PropertyMessageTarget) {
      return Objects.equals(getNodeIds(), otherChange.getNodeIds()) && ((PropertyMessageTarget) myMessageTarget).getRole().equals(((PropertyMessageTarget) otherMessageTarget).getRole());
    }
    if (myMessageTarget instanceof ReferenceMessageTarget) {
      return Objects.equals(getNodeIds(), otherChange.getNodeIds()) && ((ReferenceMessageTarget) myMessageTarget).getRole().equals(((ReferenceMessageTarget) otherMessageTarget).getRole());
    }
    return false;
  }
  private static <T> T as_uhcuom_a0a51a81(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
}
