package som.compiler;

import static som.interpreter.SNodeFactory.createArgumentRead;
import static som.interpreter.SNodeFactory.createLocalVarRead;
import static som.interpreter.SNodeFactory.createSelfRead;
import static som.interpreter.SNodeFactory.createSuperRead;
import static som.interpreter.SNodeFactory.createVariableWrite;
import static som.interpreter.TruffleCompiler.transferToInterpreterAndInvalidate;
import som.compiler.ClassBuilder.ClassDefinitionId;
import som.interpreter.nodes.ExpressionNode;

import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
import com.oracle.truffle.api.frame.FrameSlot;
import com.oracle.truffle.api.source.SourceSection;

public abstract class Variable {
  public final String name;

  @CompilationFinal protected boolean isRead;
  @CompilationFinal protected boolean isReadOutOfContext;

  Variable(final String name) {
    this.name      = name;
    this.isRead    = false;
    this.isReadOutOfContext = false;
  }

  @Override
  public String toString() {
    return getClass().getName() + "(" + name + ")";
  }

  public boolean isAccessed() {
    return isRead;
  }

  public boolean isAccessedOutOfContext() {
    return isReadOutOfContext;
  }

  public abstract ExpressionNode getReadNode(final int contextLevel,
      final SourceSection source);

  public final ExpressionNode getSelfReadNode(final int contextLevel,
      final ClassDefinitionId holderClass,
      final SourceSection source) {
    assert this instanceof Argument;
    isRead = true;
    if (contextLevel > 0) {
      isReadOutOfContext = true;
    }
    return createSelfRead(contextLevel, holderClass, source);
  }

  public final ExpressionNode getSuperReadNode(final int contextLevel,
      final ClassDefinitionId holderClass, final boolean classSide,
      final SourceSection source) {
    assert this instanceof Argument;
    isRead = true;
    if (contextLevel > 0) {
      isReadOutOfContext = true;
    }
    return createSuperRead(contextLevel, holderClass, classSide, source);
  }

  public static final class Argument extends Variable {
    public final int index;

    Argument(final String name, final int index) {
      super(name);
      this.index = index;
    }

    public boolean isSelf() {
      return "self".equals(name) || "$blockSelf".equals(name); // TODO: i think, $blockSelf should never be accessed by any program
    }

    @Override
    public ExpressionNode getReadNode(final int contextLevel,
        final SourceSection source) {
      transferToInterpreterAndInvalidate("Variable.getReadNode");
      isRead = true;
      if (contextLevel > 0) {
        isReadOutOfContext = true;
      }
      return createArgumentRead(this, contextLevel, source);
    }
  }

  public static final class Local extends Variable {
    private final FrameSlot slot;
    @CompilationFinal private boolean isWritten;
    @CompilationFinal private boolean isWrittenOutOfContext;

    Local(final String name, final FrameSlot slot) {
      super(name);
      this.isWritten = false;
      this.slot      = slot;
      this.isWrittenOutOfContext = false;
    }

    @Override
    public ExpressionNode getReadNode(final int contextLevel,
        final SourceSection source) {
      transferToInterpreterAndInvalidate("Variable.getReadNode");
      isRead = true;
      if (contextLevel > 0) {
        isReadOutOfContext = true;
      }
      return createLocalVarRead(this, contextLevel, source);
    }

    public FrameSlot getSlot() {
      return slot;
    }

    public Object getSlotIdentifier() {
      return slot.getIdentifier();
    }

    public Local cloneForInlining(final FrameSlot inlinedSlot) {
      Local local = new Local(name, inlinedSlot);
      local.isRead = isRead;
      local.isReadOutOfContext = isReadOutOfContext;
      local.isWritten = isWritten;
      local.isWrittenOutOfContext = isWrittenOutOfContext;
      return local;
    }

    @Override
    public boolean isAccessed() {
      return super.isAccessed() || isWritten;
    }

    @Override
    public boolean isAccessedOutOfContext() {
      return super.isAccessedOutOfContext() || isWrittenOutOfContext;
    }

    public ExpressionNode getWriteNode(final int contextLevel,
        final ExpressionNode valueExpr, final SourceSection source) {
      transferToInterpreterAndInvalidate("Variable.getWriteNode");
      isWritten = true;
      if (contextLevel > 0) {
        isWrittenOutOfContext = true;
      }
      return createVariableWrite(this, contextLevel, valueExpr, source);
    }
  }
}

@@ -509,75 +509,8 @@
 e;%0A%0A
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;%0A
 impo
@@ -671,113 +671,8 @@
 e;%0A%0A
-  @CompilationFinal protected boolean isRead;%0A  @CompilationFinal protected boolean isReadOutOfContext;%0A%0A
   Va
@@ -717,21 +717,16 @@
 ame 
-     
 = name;%0A
     
@@ -725,73 +725,8 @@
 me;%0A
-    this.isRead    = false;%0A    this.isReadOutOfContext = false;%0A
   %7D%0A
@@ -828,144 +828,8 @@
  %7D%0A%0A
-  public boolean isAccessed() %7B%0A    return isRead;%0A  %7D%0A%0A  public boolean isAccessedOutOfContext() %7B%0A    return isReadOutOfContext;%0A  %7D%0A%0A
   pu
@@ -1119,94 +1119,8 @@
 nt;%0A
-    isRead = true;%0A    if (contextLevel %3E 0) %7B%0A      isReadOutOfContext = true;%0A    %7D%0A
     
@@ -1398,94 +1398,8 @@
 nt;%0A
-    isRead = true;%0A    if (contextLevel %3E 0) %7B%0A      isReadOutOfContext = true;%0A    %7D%0A
     
@@ -2014,102 +2014,8 @@
 %22);%0A
-      isRead = true;%0A      if (contextLevel %3E 0) %7B%0A        isReadOutOfContext = true;%0A      %7D%0A
     
@@ -2173,290 +2173,103 @@
 ot;%0A
-    @CompilationFinal private boolean isWritten;%0A    @CompilationFinal private boolean isWrittenOutOfContext;%0A%0A    Local(final String name, final FrameSlot slot) %7B%0A      super(name);%0A      this.isWritten = false;%0A      this.slot      = slot;%0A      this.isWrittenOutOfContext = false
+%0A    Local(final String name, final FrameSlot slot) %7B%0A      super(name);%0A      this.slot = slot
 ;%0A  
@@ -2457,102 +2457,8 @@
 %22);%0A
-      isRead = true;%0A      if (contextLevel %3E 0) %7B%0A        isReadOutOfContext = true;%0A      %7D%0A
     
@@ -2787,434 +2787,20 @@
     
-local.isRead = isRead;%0A      local.isReadOutOfContext = isReadOutOfContext;%0A      local.isWritten = isWritten;%0A      local.isWrittenOutOfContext = isWrittenOutOfContext;%0A      return local;%0A    %7D%0A%0A    @Override%0A    public boolean isAccessed() %7B%0A      return super.isAccessed() %7C%7C isWritten;%0A    %7D%0A%0A    @Override%0A    public boolean isAccessedOutOfContext() %7B%0A      return super.isAccessedOutOfContext() %7C%7C isWrittenOutOfContext
+return local
 ;%0A  
@@ -3008,108 +3008,8 @@
 %22);%0A
-      isWritten = true;%0A      if (contextLevel %3E 0) %7B%0A        isWrittenOutOfContext = true;%0A      %7D%0A
     

Remove tracking of variable access, has not been used since the experiments