package jetbrains.mps.idea.java.psiStubs;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.idea.core.refactoring.NodePtr;
import com.intellij.psi.PsiElement;
import jetbrains.mps.smodel.SNodeId;
import org.jetbrains.mps.openapi.model.SModelReference;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.NotNull;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiJavaFile;
import org.jetbrains.mps.openapi.module.SModule;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.openapi.module.Module;
import com.intellij.openapi.module.ModuleManager;
import jetbrains.mps.idea.core.facet.MPSFacet;
import com.intellij.facet.FacetManager;
import jetbrains.mps.idea.core.facet.MPSFacetType;
import jetbrains.mps.java.stub.JavaPackageNameStub;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiField;
import com.intellij.psi.PsiNamedElement;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.PsiParameter;
import com.intellij.psi.PsiTypeElement;
import com.intellij.psi.PsiType;

@GeneratedClass(node = "r:e32ada56-1876-4524-85c0-29075cbd8d32(jetbrains.mps.idea.java.psiStubs)/6481525656078378666", model = "r:e32ada56-1876-4524-85c0-29075cbd8d32(jetbrains.mps.idea.java.psiStubs)")
public class JavaForeignIdBuilder {

  public static NodePtr computeNodePtr(PsiElement element) {
    SNodeId nodeId = computeNodeId(element);
    SModelReference modelRef = computeModelReference(element);
    if (nodeId == null || modelRef == null) {
      return null;
    }
    return new NodePtr(modelRef, nodeId);
  }

  @Nullable
  public static SNodeId.Foreign computeNodeId(@NotNull PsiElement element) {
    PsiElement parent = element.getParent();
    if (parent == null) {
      return null;
    }
    String prefix;
    if (parent instanceof PsiFile) {
      // element is root
      prefix = SNodeId.Foreign.ID_PREFIX;
    } else {
      org.jetbrains.mps.openapi.model.SNodeId parentId = computeNodeId(parent);
      if (parentId == null) {
        return null;
      }
      prefix = parentId.toString() + ".";
    }

    // e.g. this was an anonymous class and we don't compute nodeId for parent
    if (prefix == null) {
      return null;
    }
    return computeNodeId(prefix, element);
  }

  @Nullable
  public static SModelReference computeModelReference(PsiElement element) {
    PsiFile file = element.getContainingFile();
    if (!(file instanceof PsiJavaFile)) {
      return null;
    }
    String packageName = ((PsiJavaFile) file).getPackageName();

    // we want to know mps module id
    SModule mpsModule = null;
    Project project = element.getProject();
    VirtualFile vfile = file.getVirtualFile();
    // MPS-17704: there was situation when an element with null virtual file came from MPSReferenceSearch
    // element was the target of search
    if (vfile == null) {
      return null;
    }
    for (Module module : ModuleManager.getInstance(project).getModules()) {
      MPSFacet facet = FacetManager.getInstance(module).getFacetByType(MPSFacetType.ID);
      if (facet == null) {
        continue;
      }
      if (module.getModuleContentScope().contains(vfile)) {
        mpsModule = facet.getSolution();
        break;
      }
    }

    if (mpsModule == null) {
      return null;
    }
    JavaPackageNameStub pnStub = new JavaPackageNameStub(packageName);
    if (pnStub.isDefaultPackage()) {
      // MPS doesn't deal with nodes in default package as it's impossible to create a model
      // that corresponds to empty package (model has non-empty name), hence null.
      // In an unlikely case we still need to reference Java classes from default package, perhaps we could do it with
      // an approach computeModelReference(string,string) used to suggest (but seemed have never used)
      // (check initial version of this class).
      return null;
    }
    return pnStub.asModelReference(mpsModule.getModuleReference());
  }

  /*package*/ static SNodeId.Foreign computeNodeId(String prefix, PsiElement element) {
    StringBuilder sb = new StringBuilder(prefix);

    if (element instanceof PsiClass || element instanceof PsiField) {
      // simply take the name
      String name = ((PsiNamedElement) element).getName();
      if (name == null) {
        return null;
      }
      sb.append(name);

    } else if (element instanceof PsiMethod) {
      PsiMethod method = (PsiMethod) element;
      String name = method.getName();
      if (name == null) {
        return null;
      }
      if (((PsiMethod) element).isConstructor()) {
        sb.deleteCharAt(sb.length() - 1);
      } else {
        sb.append(name);
      }
      sb.append('(');

      for (PsiParameter param : method.getParameterList().getParameters()) {
        // for classess we want their name exactly how it is written in the source file
        // (either short name, partially qualified or fully qualified)
        // to make it equal to what we have in eclipse stubs

        PsiTypeElement psiTypeElem = param.getTypeElement();
        PsiType psiType = param.getType();

        if (psiType != null && psiTypeElem != null) {
          sb.append(ASTConverter.typeReferenceId(psiTypeElem));
          sb.append(',');
        }
      }
      int last = sb.length() - 1;
      if (sb.charAt(last) == ',') {
        sb.deleteCharAt(last);
      }

      sb.append(')');
    }

    return new SNodeId.Foreign(sb.toString());
  }
}
