package som.interpreter.nodes;

import som.compiler.ClassBuilder.ClassDefinitionId;
import som.compiler.ClassDefinition;
import som.vm.constants.KernelObj;
import som.vmobjects.SClass;
import som.vmobjects.SObjectWithoutFields;

import com.oracle.truffle.api.dsl.Cached;
import com.oracle.truffle.api.dsl.NodeChild;
import com.oracle.truffle.api.dsl.Specialization;
import com.oracle.truffle.api.nodes.ExplodeLoop;
import com.oracle.truffle.api.source.SourceSection;
import com.oracle.truffle.api.utilities.ValueProfile;

@NodeChild(value = "receiver", type = ExpressionNode.class)
public abstract class OuterObjectRead
    extends ExpressionNode implements ISpecialSend {

  protected static final int INLINE_CACHE_SIZE = 6;

  protected final int contextLevel;
  private final ClassDefinitionId classDefId;
  private final ClassDefinitionId enclosingLexicalClassId;

  private final ValueProfile enclosingObj;

  public OuterObjectRead(final int contextLevel,
      final ClassDefinitionId classDefId,
      final ClassDefinitionId enclosingLexicalClassId,
      final SourceSection sourceSection) {
    super(sourceSection);
    this.contextLevel = contextLevel;
    this.classDefId = classDefId;
    this.enclosingLexicalClassId = enclosingLexicalClassId;
    this.enclosingObj = ValueProfile.createIdentityProfile();
  }

  public ClassDefinitionId getClassId() {
    return classDefId;
  }

  @Override
  public ClassDefinitionId getLexicalClass() {
    return enclosingLexicalClassId;
  }

  @Override
  public boolean isSuperSend() { return false; }

  protected final SClass getLexicalClass(final SObjectWithoutFields rcvr) {
    SClass lexicalClass = rcvr.getSOMClass().getClassCorrespondingTo(classDefId);
    assert lexicalClass != null;
    return lexicalClass;
  }

  @Specialization(guards = "contextLevel == 0")
  public final Object doForOuterIsDirectClass(final SObjectWithoutFields receiver) {
    return enclosingObj.profile(receiver);
  }

  @Specialization(limit = "INLINE_CACHE_SIZE",
      guards = {"contextLevel != 0", "receiver.getSOMClass().getClassDefinition() == rcvrClassDef"})
  public final Object doForFurtherOuter(final SObjectWithoutFields receiver,
      @Cached("receiver.getSOMClass().getClassDefinition()") final ClassDefinition rcvrClassDef,
      @Cached("getLexicalClass(receiver)") final SClass lexicalClass) {
    return getEnclosingObject(receiver, lexicalClass);
  }

  @Specialization(guards = "contextLevel != 0", contains = "doForFurtherOuter")
  public final Object fallback(final SObjectWithoutFields receiver) {
    return getEnclosingObject(receiver, getLexicalClass(receiver));
  }

  public abstract Object executeEvaluated(Object receiver);

  protected static final boolean notSObjectWithoutFields(final Object receiver) {
    return !(receiver instanceof SObjectWithoutFields);
  }

  @Specialization(guards = "notSObjectWithoutFields(receiver)")
  public Object doObject(final Object receiver) {
    return KernelObj.kernel;
  }

  @ExplodeLoop
  private Object getEnclosingObject(final SObjectWithoutFields receiver,
      final SClass lexicalClass) {
    assert lexicalClass != null;
    int ctxLevel = contextLevel - 1; // 0 is already covered with specialization
    SObjectWithoutFields enclosing = lexicalClass.getEnclosingObject();

    while (ctxLevel > 0) {
      ctxLevel--;
      enclosing = enclosing.getSOMClass().getEnclosingObject();
    }
    return enclosingObj.profile(enclosing);
  }
}

@@ -81,45 +81,8 @@
 Id;%0A
-import som.compiler.ClassDefinition;%0A
 impo
@@ -1599,29 +1599,14 @@
     
-SClass lexicalClass =
+return
  rcv
@@ -1662,66 +1662,8 @@
 d);%0A
-    assert lexicalClass != null;%0A    return lexicalClass;%0A
   %7D%0A
@@ -1951,37 +1951,16 @@
 MClass()
-.getClassDefinition()
  == rcvr
@@ -1964,19 +1964,16 @@
 cvrClass
-Def
 %22%7D)%0A  pu
@@ -2086,29 +2086,8 @@
 ss()
-.getClassDefinition()
 %22) f
@@ -2091,31 +2091,22 @@
 ) final 
+S
 Class
-Definition
  rcvrCla
@@ -2107,19 +2107,16 @@
 cvrClass
-Def
 ,%0A      
@@ -2943,41 +2943,8 @@
 ) %7B%0A
-    assert lexicalClass != null;%0A
     

Revert "Try to make OuterObjectRead more robust by relying on structural information"