package jetbrains.mps.kotlin.stubs.common;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.smodel.SNodeId;
import org.jetbrains.annotations.NotNull;
import java.util.Objects;
import org.jetbrains.mps.openapi.persistence.SNodeIdFactory;

/**
 * Migration ID for function with complex type parameter definition (see MPS-35026).
 * 
 * This class is responsible for bridging the previous function id definition and the new one, and persist references as
 * SNodeId.Foreigninstances (thanks to the toString() method that uses the Foreign id prefix).
 * 
 * Previously, ids were of the form receiver.name<typeParamCount>(parametersTypes), however, one could write the following
 * functions in the same file:
 * - fun <T: Comparable<U>, U> T.complex(): Double = TODO()
 * - fun <T: List<U>, U> T.complex(): Double = TODO()
 * Both had the same definition: 0.complex<2>(), but this obviously is causing an issue.
 * New definition includes upper bounds, so we have the following: 0.complex[Comparable<1>,*]() and 0.complex[List<1>,*]().
 * Note that such definition is used only if upper bounds are present, otherwise the previous id are used (instead
 * of [*,*,*,*], <4> is used).
 * 
 * @see jetbrains.mps.smodel.StringBasedIdForJavaStubMethods class this is inspired from
 * @see jetbrains.mps.kotlin.stubs.common.KotlinStringBasedNodeIdMap support in smodel
 */
@GeneratedClass(node = "r:3391afe4-c131-4e6c-87cf-990834a43a93(jetbrains.mps.kotlin.stubs.common)/6767752332560553359", model = "r:3391afe4-c131-4e6c-87cf-990834a43a93(jetbrains.mps.kotlin.stubs.common)")
public class KtFunctionNodeId extends SNodeId implements SNodeId.StringBasedId {
  public static String ID_PREFIX = "^~";

  @NotNull
  private final String myOldImplicitId;
  @NotNull
  private final String myNewExplicitId;

  public KtFunctionNodeId(String fullId) {
    assert fullId.startsWith(ID_PREFIX);
    fullId = fullId.substring(2);
    myNewExplicitId = fullId;
    myOldImplicitId = reduceId(fullId);
  }

  private String reduceId(String fullId) {
    int firstIndex = fullId.indexOf("[");
    int lastIndex = fullId.indexOf("]");
    int count = 1;
    for (int i = firstIndex + 1, depth = 0; i < lastIndex; i++) {
      char charAt = fullId.charAt(i);
      // Ignores comma in subtypes (eg. A<B<C,V,D>>) has only one type arg
      if (charAt == '<') {
        depth++;
      } else if (charAt == '>') {
        depth--;
      } else if (charAt == ',' && depth == 0) {
        count++;
      }
    }
    return fullId.substring(0, firstIndex) + "<" + count + ">" + fullId.substring(lastIndex + 1);
  }

  @NotNull
  @Override
  public String getId() {
    return myNewExplicitId;
  }

  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null) {
      return false;
    }
    if (o instanceof KtFunctionNodeId) {
      KtFunctionNodeId otherId = (KtFunctionNodeId) o;
      return Objects.equals(getId(), otherId.getId());
    }
    if (o instanceof SNodeId.Foreign) {
      SNodeId.Foreign foreign = (SNodeId.Foreign) o;
      return foreign.getId().equals(getId());
    }
    return false;
  }

  public String getOldId() {
    return myOldImplicitId;
  }

  @Override
  public int hashCode() {
    return myOldImplicitId.hashCode();
  }

  @Override
  public String toString() {
    // persist as new foreign id (which is compatible)
    return SNodeId.Foreign.ID_PREFIX + myNewExplicitId;
  }

  public static class Factory implements SNodeIdFactory {
    @Override
    public org.jetbrains.mps.openapi.model.SNodeId create(String string) {
      return new KtFunctionNodeId(string);
    }
  }
}
