package jetbrains.mps.baseLanguage.lightweightdsl.typesystem;

/*Generated by MPS */

import org.jetbrains.mps.openapi.model.SNode;
import java.util.Objects;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SPropertyOperations;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SLinkOperations;
import jetbrains.mps.baseLanguage.lightweightdsl.behavior.MethodDescriptor__BehaviorDescriptor;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import jetbrains.mps.baseLanguage.lightweightdsl.behavior.DependentTypeDescriptor__BehaviorDescriptor;
import jetbrains.mps.smodel.SNodeMatcher;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import jetbrains.mps.baseLanguage.lightweightdsl.behavior.ParameterDescriptor__BehaviorDescriptor;
import org.jetbrains.mps.openapi.language.SReferenceLink;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SContainmentLink;
import org.jetbrains.mps.openapi.language.SProperty;
import org.jetbrains.mps.openapi.language.SConcept;

public class ClassLikeMethodChecker {
  public void checkMethod(SNode method, ClassLikeMethodProblemVisitor visitor) {
    try {
      doCheck(method, visitor);
    } catch (StopMethodCheckerException s) {
      // do nothing, this just stops processing
    }
  }

  private boolean doCheck(final SNode method, ClassLikeMethodProblemVisitor visitor) throws StopMethodCheckerException {
    // name
    if (!(Objects.equals(SPropertyOperations.getString(method, PROPS.name$MnvL), SPropertyOperations.getString(SLinkOperations.getTarget(method, LINKS.decl$QvLv), PROPS.name$MnvL)))) {
      visitor.visitName(method, SPropertyOperations.getString(SLinkOperations.getTarget(method, LINKS.decl$QvLv), PROPS.name$MnvL));
    }

    // ret type
    final SNode retType = MethodDescriptor__BehaviorDescriptor.getReturnType_id3m06Jgso0l8.invoke(SLinkOperations.getTarget(method, LINKS.decl$QvLv));
    if (SNodeOperations.isInstanceOf(retType, CONCEPTS.DependentTypeDescriptor$ny)) {
      if (!(SNodeOperations.isInstanceOf(SLinkOperations.getTarget(method, LINKS.returnType$5xoi), CONCEPTS.DependentTypeInstance$N9)) || SLinkOperations.getTarget(SNodeOperations.cast(SLinkOperations.getTarget(method, LINKS.returnType$5xoi), CONCEPTS.DependentTypeInstance$N9), LINKS.decl$HI1L) != retType) {
        visitor.visitReturnType(method, () -> (SNode) DependentTypeDescriptor__BehaviorDescriptor.create_id2h59CdJp8nr.invoke(SNodeOperations.cast(retType, CONCEPTS.DependentTypeDescriptor$ny), method));
      }
    } else {
      if (!(new SNodeMatcher().match(SLinkOperations.getTarget(method, LINKS.returnType$5xoi), retType))) {
        visitor.visitReturnType(method, () -> SNodeOperations.copyNode(retType));
      }
    }

    // parameters
    for (int i = 0, cur = 0; i < ListSequence.fromList(SLinkOperations.getChildren(SLinkOperations.getTarget(method, LINKS.decl$QvLv), LINKS.param$HGTA)).count(); i++) {
      final SNode formalPar = ListSequence.fromList(SLinkOperations.getChildren(SLinkOperations.getTarget(method, LINKS.decl$QvLv), LINKS.param$HGTA)).getElement(i);
      if (cur == ListSequence.fromList(SLinkOperations.getChildren(method, LINKS.parameter$5xBj)).count()) {
        if (SLinkOperations.getTarget(formalPar, LINKS.condition$uzGY) == null || (boolean) ParameterDescriptor__BehaviorDescriptor.isNeeded_id7GXvAHO1j1d.invoke(formalPar, method)) {
          visitor.visitMissingParam(method, cur++, () -> (SNode) ParameterDescriptor__BehaviorDescriptor.create_id2h59CdJp99Y.invoke(formalPar, method));
        }
        continue;
      }

      SNode actualPar = ListSequence.fromList(SLinkOperations.getChildren(method, LINKS.parameter$5xBj)).getElement(cur);

      // todo this condition should be removed after migration to our params
      if (SNodeOperations.isInstanceOf(actualPar, CONCEPTS.MethodParameterInstance$SI)) {
        // conditional? need to check presence conforms with condition
        if (SLinkOperations.getTarget(formalPar, LINKS.condition$uzGY) != null) {
          // needed, but not present
          if ((boolean) ParameterDescriptor__BehaviorDescriptor.isNeeded_id7GXvAHO1j1d.invoke(formalPar, method) && SLinkOperations.getTarget(SNodeOperations.cast(actualPar, CONCEPTS.MethodParameterInstance$SI), LINKS.decl$JKxQ) != formalPar) {
            visitor.visitMissingParam(method, cur++, () -> (SNode) ParameterDescriptor__BehaviorDescriptor.create_id2h59CdJp99Y.invoke(formalPar, method));
            continue;
          }
          // not needed, but present
          if (!((boolean) ParameterDescriptor__BehaviorDescriptor.isNeeded_id7GXvAHO1j1d.invoke(formalPar, method)) && SLinkOperations.getTarget(SNodeOperations.cast(actualPar, CONCEPTS.MethodParameterInstance$SI), LINKS.decl$JKxQ) == formalPar) {
            visitor.visitOddParam(actualPar);
            continue;
          }
          // otherwise, check as a regular parameter
        }

        if (SLinkOperations.getTarget(SNodeOperations.cast(actualPar, CONCEPTS.MethodParameterInstance$SI), LINKS.decl$JKxQ) != formalPar) {
          visitor.visitUnknownParam(method, cur++);
        }
      }

      if (!(Objects.equals(SPropertyOperations.getString(actualPar, PROPS.name$MnvL), SPropertyOperations.getString(formalPar, PROPS.name$MnvL)))) {
        visitor.visitParamName(actualPar, SPropertyOperations.getString(formalPar, PROPS.name$MnvL));
      }

      if (SNodeOperations.isInstanceOf(SLinkOperations.getTarget(formalPar, LINKS.type$HBuM), CONCEPTS.DependentTypeDescriptor$ny)) {
        if (!(SNodeOperations.isInstanceOf(SLinkOperations.getTarget(actualPar, LINKS.type$a1UY), CONCEPTS.DependentTypeInstance$N9))) {
          visitor.visitParamType(actualPar, () -> (SNode) DependentTypeDescriptor__BehaviorDescriptor.create_id2h59CdJp8nr.invoke(SNodeOperations.cast(SLinkOperations.getTarget(formalPar, LINKS.type$HBuM), CONCEPTS.DependentTypeDescriptor$ny), method));
        }
      } else {
        if (!(new SNodeMatcher().match(SLinkOperations.getTarget(actualPar, LINKS.type$a1UY), SLinkOperations.getTarget(formalPar, LINKS.type$HBuM)))) {
          visitor.visitParamType(actualPar, () -> SNodeOperations.copyNode(SLinkOperations.getTarget(formalPar, LINKS.type$HBuM)));
        }
      }
      cur++;
    }
    return false;
  }

  public static class StopMethodCheckerException extends Exception {

  }

  public enum ErrorState {
    OK(),
    REPAIRABLE(),
    NON_REPAIRABLE()
  }

  private static final class LINKS {
    /*package*/ static final SReferenceLink decl$QvLv = MetaAdapterFactory.getReferenceLink(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x340eb2bd2e03d154L, 0x443e89bb321537L, "decl");
    /*package*/ static final SContainmentLink returnType$5xoi = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b1fcL, 0xf8cc56b1fdL, "returnType");
    /*package*/ static final SReferenceLink decl$HI1L = MetaAdapterFactory.getReferenceLink(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x340eb2bd2e03d16bL, 0x340eb2bd2e03d16cL, "decl");
    /*package*/ static final SContainmentLink param$HGTA = MetaAdapterFactory.getContainmentLink(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x340eb2bd2e03d167L, 0x340eb2bd2e03d168L, "param");
    /*package*/ static final SContainmentLink condition$uzGY = MetaAdapterFactory.getContainmentLink(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x340eb2bd2e03d164L, 0x1c6f8dad3c495fe9L, "condition");
    /*package*/ static final SContainmentLink parameter$5xBj = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0xf8cc56b1fcL, 0xf8cc56b1feL, "parameter");
    /*package*/ static final SReferenceLink decl$JKxQ = MetaAdapterFactory.getReferenceLink(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x7b3d7e6b74000c44L, 0x7b3d7e6b7400c831L, "decl");
    /*package*/ static final SContainmentLink type$HBuM = MetaAdapterFactory.getContainmentLink(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x340eb2bd2e03d164L, 0x340eb2bd2e03d166L, "type");
    /*package*/ static final SContainmentLink type$a1UY = MetaAdapterFactory.getContainmentLink(0xf3061a5392264cc5L, 0xa443f952ceaf5816L, 0x450368d90ce15bc3L, 0x4ed4d318133c80ceL, "type");
  }

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

  private static final class CONCEPTS {
    /*package*/ static final SConcept DependentTypeDescriptor$ny = MetaAdapterFactory.getConcept(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x340eb2bd2e03d15cL, "jetbrains.mps.baseLanguage.lightweightdsl.structure.DependentTypeDescriptor");
    /*package*/ static final SConcept DependentTypeInstance$N9 = MetaAdapterFactory.getConcept(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x340eb2bd2e03d16bL, "jetbrains.mps.baseLanguage.lightweightdsl.structure.DependentTypeInstance");
    /*package*/ static final SConcept MethodParameterInstance$SI = MetaAdapterFactory.getConcept(0xc7d5b9dda05f4be2L, 0xbc73f2e16994cc67L, 0x7b3d7e6b74000c44L, "jetbrains.mps.baseLanguage.lightweightdsl.structure.MethodParameterInstance");
  }
}
