package jetbrains.mps.vcs.diff.changes;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import org.jetbrains.mps.openapi.language.SConcept;
import org.jetbrains.annotations.NotNull;
import jetbrains.mps.vcs.diff.ChangeSet;
import org.jetbrains.mps.openapi.model.SNodeId;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.model.SModel;
import org.jetbrains.mps.openapi.language.SProperty;
import jetbrains.mps.internal.collections.runtime.Sequence;
import org.jetbrains.mps.openapi.model.SReference;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SModelOperations;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import java.util.List;
import jetbrains.mps.baseLanguage.tuples.runtime.Tuples;
import jetbrains.mps.errors.messageTargets.MessageTarget;
import jetbrains.mps.internal.collections.runtime.LinkedListSequence;
import java.util.LinkedList;
import jetbrains.mps.baseLanguage.tuples.runtime.MultiTuple;
import jetbrains.mps.errors.messageTargets.NodeMessageTarget;
import java.util.Objects;

@GeneratedClass(node = "r:9b4a89e1-ec38-42c4-b1bd-96ab47ffcb3f(jetbrains.mps.vcs.diff.changes)/4133404009533074929", model = "r:9b4a89e1-ec38-42c4-b1bd-96ab47ffcb3f(jetbrains.mps.vcs.diff.changes)")
public class SetConceptChange extends NodeChange {

  private SConcept myOldConcept;
  private SConcept myNewConcept;
  private final String myDescription;


  public SetConceptChange(@NotNull ChangeSet changeSet, @NotNull SNodeId nodeId, SConcept newValue) {
    this(changeSet, nodeId, getOldConcept(changeSet, nodeId), newValue);
  }

  public SetConceptChange(@NotNull ChangeSet changeSet, @NotNull SNodeId nodeId, SConcept oldValue, SConcept newValue) {
    super(changeSet, nodeId, nodeId);
    myNewConcept = newValue;
    myOldConcept = oldValue;
    myDescription = createDescription();
  }


  public SConcept getNewValue() {
    return myNewConcept;
  }

  private static SConcept getOldConcept(@NotNull ChangeSet changeSet, @NotNull SNodeId nodeId) {
    SNode node = changeSet.getOldModel().getNode(nodeId);
    assert node != null;
    return node.getConcept();
  }

  @Override
  public void apply(@NotNull SModel model, @NotNull NodeCopier nodeCopier) {
    final SNode node = model.getNode(getAffectedNodeId());
    assert node != null;
    final SNode newNode = new jetbrains.mps.smodel.SNode(myNewConcept, node.getNodeId());

    // copy all the properties, links and move children
    for (SProperty prop : Sequence.fromIterable(node.getProperties())) {
      newNode.setProperty(prop, node.getProperty(prop));
    }
    for (Object key : node.getUserObjectKeys()) {
      newNode.putUserObject(key, node.getUserObject(key));
    }
    for (SReference ref : ListSequence.fromList(SNodeOperations.getReferences(node))) {
      SModelReference targetModelReference = ref.getTargetSModelReference();
      if (targetModelReference == null) {
        targetModelReference = SModelOperations.getPointer(model);
      }
      SNodeId targetNodeId = ref.getTargetNodeId();
      SReferenceLink link = SLinkOperations.getRefLink(ref);
      if (targetNodeId == null) {
        String string = SLinkOperations.getResolveInfo(ref);
        newNode.setReference(link, jetbrains.mps.util.SNodeOperations.qualifiedResolveInfo(link, targetModelReference, string));
      } else {
        newNode.setReference(link, ref.describeTarget());
      }
    }
    for (SNode child : ListSequence.fromList(SNodeOperations.getChildren(node))) {
      newNode.addChild(SNodeOperations.getContainingLink(child), SNodeOperations.deleteNode(child));
    }
    // fix references to the node itself
    ListSequence.fromList(SNodeOperations.getNodeDescendants(newNode, null, true, new SAbstractConcept[]{})).translate((n) -> SNodeOperations.getReferences(n)).where((r) -> SLinkOperations.getTargetNode(r) == node).visitAll((ref) -> ref.getSourceNode().setReferenceTarget(ref.getLink(), newNode));

    SNodeOperations.replaceWithAnother(node, newNode);
  }

  @NotNull
  @Override
  protected ModelChange createOppositeChange() {
    return new SetConceptChange(getChangeSet().getOppositeChangeSet(), getAffectedNodeId(true), myNewConcept, myOldConcept);
  }

  @Override
  public String toString() {
    return String.format("Set concept %s to %s in node %s", myOldConcept, myNewConcept, getAffectedNodeId(false));
  }

  private String createDescription() {
    return String.format("Changed concept of #%s from\n'%s'\nto\n'%s'", getAffectedNodeId(false), getChangeSet().getOldModel().getNode(getAffectedNodeId(false)).getConcept(), getChangeSet().getNewModel().getNode(getAffectedNodeId(true)).getConcept());
  }

  @Override
  public String getShortDescription() {
    return "Changed concept";
  }

  @Override
  public String getDescription() {
    return myDescription;
  }

  @Override
  public List<Tuples._2<SNodeId, MessageTarget>> createMessageTargetsWithIds(boolean isNewModel) {
    return LinkedListSequence.fromListAndArray(new LinkedList<Tuples._2<SNodeId, MessageTarget>>(), MultiTuple.<SNodeId,MessageTarget>from(getAffectedNodeId(isNewModel), ((MessageTarget) new NodeMessageTarget())));
  }
  @Override
  public boolean conflictsWith(@NotNull ModelChange otherChange) {
    if (super.conflictsWith(otherChange)) {
      return true;
    }
    return otherChange instanceof SetConceptChange && Objects.equals(this.getAffectedNodeId(), as_iibrez_a0b0a1a82(otherChange, SetConceptChange.class).getAffectedNodeId());
  }

  @Override
  public boolean isSymmetricWith(@NotNull ModelChange otherChange) {
    return otherChange instanceof SetConceptChange && Objects.equals(this.getNewValue(), as_iibrez_a0b0a0a03(otherChange, SetConceptChange.class).getNewValue());
  }
  private static <T> T as_iibrez_a0b0a1a82(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
  private static <T> T as_iibrez_a0b0a0a03(Object o, Class<T> type) {
    return (type.isInstance(o) ? (T) o : null);
  }
}
