package jetbrains.mps.kotlin.api.builtins;

/*Generated by MPS */

import jetbrains.mps.smodel.SNodePointer;
import org.jetbrains.mps.openapi.model.SNode;
import org.jetbrains.mps.openapi.module.SRepository;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SNodeOperations;
import org.jetbrains.mps.openapi.model.SReference;
import java.util.Objects;
import org.jetbrains.mps.openapi.persistence.PersistenceFacade;
import org.jetbrains.mps.openapi.model.SModelId;
import org.jetbrains.mps.openapi.model.SModelReference;
import jetbrains.mps.smodel.SNodeId;
import org.jetbrains.mps.openapi.model.SNodeReference;
import jetbrains.mps.lang.smodel.generator.smodelAdapter.SConceptOperations;
import jetbrains.mps.smodel.adapter.structure.MetaAdapterFactory;
import org.jetbrains.mps.openapi.language.SInterfaceConcept;
import org.jetbrains.mps.openapi.language.SReferenceLink;

/**
 * Enumeration of kotlin types commonly used in type system.
 * 
 * Contains several utilities methods to use the references. Regular node pointers are not used to avoid circular dependencies
 * (kotlin language should exist without stdlib, so in case one breaks, the other is easier to fix)
 */
public enum BuiltIn {
  UNIT("kotlin/Unit"),
  ANY("kotlin/Any"),
  ENUM("kotlin/Enum"),
  NOTHING("kotlin/Nothing"),
  CHAR("kotlin/Char"),
  STRING("kotlin/String"),
  BOOLEAN("kotlin/Boolean"),
  FUNCTION("kotlin/Function"),
  ARRAY("kotlin/Array"),
  FLOAT("kotlin/Float"),
  DOUBLE("kotlin/Double"),
  LONG("kotlin/Long"),
  INT("kotlin/Int");

  private SNodePointer myNodePointer;
  private final String myFqName;

  BuiltIn(String fqName) {
    myFqName = fqName;
  }

  /**
   * Returns the pointer to the built in type. On first call, create the reference from the fqName.
   * 
   * The reason this is not initialized in constructor is the possible unavailability of MPS services on class
   * loading.
   */
  public SNodePointer pointer() {
    if (myNodePointer == null) {
      myNodePointer = pointerOf(myFqName);
    }

    return myNodePointer;
  }

  /**
   * Return the resolved node.
   * 
   * @param contextNode node to get the repository from
   */
  public SNode getClass(SNode contextNode) {
    return getClass(contextNode.getModel().getRepository());
  }

  public SNode getClass(SRepository repository) {
    return SNodeOperations.as(check_foaryp_a0a0j(pointer(), repository, this), CONCEPTS.IClassLike$go);
  }

  public boolean isReferedBy(SNode reference) {
    return isReferedBy(SNodeOperations.getReference(reference, LINKS.class$ExdX));
  }

  public boolean isReferedBy(SReference ref) {
    if (ref == null) {
      return false;
    }
    return Objects.equals(pointer(), ref.getTargetNodeReference());
  }

  /**
   * Returns an empty class type referring to the built in type, without any argument.
   */
  public SNode toClassType() {
    return classTypeOf(pointer());
  }

  public static SNodePointer pointerOf(String fqName) {
    String packageName = fqName.substring(0, fqName.lastIndexOf("/")).replaceAll("/", ".");
    // code extracted from KotlinLanguage implementation as it is a MPS loaded class
    PersistenceFacade facade = PersistenceFacade.getInstance();
    SModelId modelId = facade.createModelId("kotlin:" + packageName);
    SModelReference modelReference = facade.createModelReference(PersistenceFacade.getInstance().createModuleReference("b50d89c0-0fb9-4105-b652-222148c26a9b(jetbrains.mps.kotlin.stdlib)"), modelId, modelId.getModelName());
    return new SNodePointer(modelReference, new SNodeId.Foreign(SNodeId.Foreign.ID_PREFIX + fqName));
  }

  public static SNode classTypeOf(SNodeReference pointer) {
    SNode classType = SConceptOperations.createNewNode(MetaAdapterFactory.getConcept(0x6b3888c1980244d8L, 0x8baff8e6c33ed689L, 0x28bef6d7551af4dfL, "jetbrains.mps.kotlin.structure.ClassType"));
    classType.setReference(LINKS.class$ExdX, pointer);
    return classType;
  }
  private static SNode check_foaryp_a0a0j(SNodePointer checkedDotOperand, SRepository repository, BuiltIn checkedDotThisExpression) {
    if (null != checkedDotOperand) {
      return checkedDotOperand.resolve(repository);
    }
    return null;
  }

  private static final class CONCEPTS {
    /*package*/ static final SInterfaceConcept IClassLike$go = MetaAdapterFactory.getInterfaceConcept(0x6b3888c1980244d8L, 0x8baff8e6c33ed689L, 0x298a6a355c110274L, "jetbrains.mps.kotlin.structure.IClassLike");
  }

  private static final class LINKS {
    /*package*/ static final SReferenceLink class$ExdX = MetaAdapterFactory.getReferenceLink(0x6b3888c1980244d8L, 0x8baff8e6c33ed689L, 0x5c7be90f2440b378L, 0x5c7be90f2440b37bL, "class");
  }
}
