package jetbrains.mps.baseLanguage.actions;

/*Generated by MPS */

import jetbrains.mps.openapi.actions.descriptor.PastePostProcessor;
import org.jetbrains.mps.openapi.language.SAbstractConcept;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.baseLanguage.behavior.IClassifierMember__BehaviorDescriptor;
import java.util.List;
import java.util.ArrayList;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import org.jetbrains.mps.openapi.model.SReference;
import jetbrains.mps.internal.collections.runtime.Sequence;
import jetbrains.mps.baseLanguage.scopes.Members;
import jetbrains.mps.baseLanguage.behavior.IClassifier__BehaviorDescriptor;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import jetbrains.mps.baseLanguage.behavior.BaseMethodDeclaration__BehaviorDescriptor;
import org.jetbrains.mps.openapi.language.SConcept;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SInterfaceConcept;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SProperty;

public final class BL_CopyPasteHandlers_PastePostProcessor_1 implements PastePostProcessor {
  @Override
  public SAbstractConcept getApplicableConcept() {
    return CONCEPTS.ThisExpression$$o;
  }
  @Override
  public void postProcessNode(SNode pastedNode) {

    if (ListSequence.fromList(SNodeOperations.getNodeAncestors(pastedNode, CONCEPTS.ClassifierMember$At, false)).where((it) -> !((boolean) IClassifierMember__BehaviorDescriptor.isStatic_id6r77ob2USS8.invoke(it))).isEmpty()) {
      return;
    }
    SNode containingClass = SNodeOperations.getNodeAncestor(pastedNode, CONCEPTS.ClassConcept$bK, false, false);
    if (containingClass == null) {
      return;
    }

    if (SNodeOperations.isInstanceOf(SNodeOperations.getParent(pastedNode), CONCEPTS.DotExpression$yW)) {
      // Collecting possible classConcepts (this. targets)
      List<SNode> possibleClassConcepts = new ArrayList<SNode>();
      for (SNode clazz = containingClass; clazz != null; clazz = ((boolean) IClassifierMember__BehaviorDescriptor.isStatic_id6r77ob2USS8.invoke(clazz) ? null : SNodeOperations.getNodeAncestor(clazz, CONCEPTS.ClassConcept$bK, false, false))) {
        ListSequence.fromList(possibleClassConcepts).addElement(clazz);
      }

      if (SNodeOperations.getReference(pastedNode, LINKS.classConcept$zzjZ) != null && ListSequence.fromList(possibleClassConcepts).contains(SNodeOperations.as(SLinkOperations.getTarget(pastedNode, LINKS.classConcept$zzjZ), CONCEPTS.ClassConcept$bK))) {
        // if we paste qualified this, don't care to check if any other class than the referenced one got a field that matches the name
        // E.g. if we got classes A and B, both with field f, and anonymous B subclass inside A has A.this.f copied and pasted, it's odd
        // to replace it with simply this.f as it's completely different field.
        if (SLinkOperations.getTarget(pastedNode, LINKS.classConcept$zzjZ) == containingClass) {
          // we still have to clear qualification when we paste qualified reference right into the class with access to the field.
          SLinkOperations.setTarget(pastedNode, LINKS.classConcept$zzjZ, null);
        }
        return;
      }

      SNode parentDotExpression = (SNode) SNodeOperations.getParent(pastedNode);
      if (SNodeOperations.isInstanceOf(SLinkOperations.getTarget(parentDotExpression, LINKS.operation$gs9E), CONCEPTS.FieldReferenceOperation$fU)) {
        SNode fieldReferenceOperation = (SNode) SLinkOperations.getTarget(parentDotExpression, LINKS.operation$gs9E);
        SReference fieldDeclarationReference = SNodeOperations.getReference(fieldReferenceOperation, LINKS.fieldDeclaration$H7Ag);

        // External reference
        final String resolveInfo = SLinkOperations.getResolveInfo(fieldDeclarationReference);

        for (SNode nextClassConcept : ListSequence.fromList(possibleClassConcepts)) {
          if (Sequence.fromIterable(Members.visibleInstanceFields(IClassifier__BehaviorDescriptor.getThisType_id6r77ob2UWbY.invoke(nextClassConcept), pastedNode)).where((it) -> SPropertyOperations.getString(it, PROPS.name$MnvL).equals(resolveInfo)).isNotEmpty()) {
            if (nextClassConcept != containingClass) {
              SLinkOperations.setTarget(pastedNode, LINKS.classConcept$zzjZ, nextClassConcept);
            } else if (SNodeOperations.getReference(pastedNode, LINKS.classConcept$zzjZ) != null) {
              SLinkOperations.setTarget(pastedNode, LINKS.classConcept$zzjZ, null);
            }
            break;
          }
        }
      }

      if (SNodeOperations.isInstanceOf(SLinkOperations.getTarget(parentDotExpression, LINKS.operation$gs9E), CONCEPTS.InstanceMethodCallOperation$uu)) {
        final SNode methodCallOperation = (SNode) SLinkOperations.getTarget(parentDotExpression, LINKS.operation$gs9E);
        SReference methodDeclarationReference = SNodeOperations.getReference(methodCallOperation, LINKS.baseMethodDeclaration$pyYw);

        // External reference
        final String resolveInfo = SLinkOperations.getResolveInfo(methodDeclarationReference);

        SNode byNameMatch = null;
        SNode exactSignatureMatch = null;
        for (SNode nextClassConcept : ListSequence.fromList(possibleClassConcepts)) {
          Iterable<SNode> visibleInstanceMethods = Members.visibleInstanceMethods(IClassifier__BehaviorDescriptor.getThisType_id6r77ob2UWbY.invoke(nextClassConcept), pastedNode);
          if (Sequence.fromIterable(visibleInstanceMethods).where((it) -> SPropertyOperations.getString(it, PROPS.name$MnvL).equals(resolveInfo) && (boolean) BaseMethodDeclaration__BehaviorDescriptor.hasSameSignature_idhEwIB0z.invoke(it, SLinkOperations.getTarget(methodCallOperation, LINKS.baseMethodDeclaration$pyYw))).isNotEmpty()) {
            exactSignatureMatch = nextClassConcept;
            break;
          } else {
            if (byNameMatch == null && Sequence.fromIterable(visibleInstanceMethods).where((it) -> SPropertyOperations.getString(it, PROPS.name$MnvL).equals(resolveInfo)).isNotEmpty()) {
              byNameMatch = nextClassConcept;
            }
          }
        }
        SNode match = (exactSignatureMatch != null ? exactSignatureMatch : byNameMatch);
        if (match != containingClass) {
          SLinkOperations.setTarget(pastedNode, LINKS.classConcept$zzjZ, match);
        } else if (SNodeOperations.getReference(pastedNode, LINKS.classConcept$zzjZ) != null) {
          SLinkOperations.setTarget(pastedNode, LINKS.classConcept$zzjZ, null);
        }
      }
    }
  }

  private static final class CONCEPTS {
    /*package*/ static final SConcept ThisExpression$$o = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf93d4da00cL, "jetbrains.mps.baseLanguage.structure.ThisExpression");
    /*package*/ static final SInterfaceConcept ClassifierMember$At = MetaAdapterFactory.getInterfaceConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x112574373bdL, "jetbrains.mps.baseLanguage.structure.ClassifierMember");
    /*package*/ static final SConcept ClassConcept$bK = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8c108ca66L, "jetbrains.mps.baseLanguage.structure.ClassConcept");
    /*package*/ static final SConcept FieldReferenceOperation$fU = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x116b483d77aL, "jetbrains.mps.baseLanguage.structure.FieldReferenceOperation");
    /*package*/ static final SConcept InstanceMethodCallOperation$uu = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x118154a6332L, "jetbrains.mps.baseLanguage.structure.InstanceMethodCallOperation");
    /*package*/ static final SConcept DotExpression$yW = MetaAdapterFactory.getConcept(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x116b46a08c4L, "jetbrains.mps.baseLanguage.structure.DotExpression");
  }

  private static final class LINKS {
    /*package*/ static final SReferenceLink classConcept$zzjZ = MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf93d4da00cL, 0x1136d9d21b3L, "classConcept");
    /*package*/ static final SContainmentLink operation$gs9E = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x116b46a08c4L, 0x116b46b36c4L, "operation");
    /*package*/ static final SReferenceLink fieldDeclaration$H7Ag = MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x116b483d77aL, 0x116b484a653L, "fieldDeclaration");
    /*package*/ static final SReferenceLink baseMethodDeclaration$pyYw = MetaAdapterFactory.getReferenceLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x11857355952L, 0xf8c78301adL, "baseMethodDeclaration");
  }

  private static final class PROPS {
    /*package*/ static final SProperty name$MnvL = MetaAdapterFactory.getProperty(0xceab519525ea4f22L, 0x9b92103b95ca8c0cL, 0x110396eaaa4L, 0x110396ec041L, "name");
  }
}
