package jetbrains.mps.debugger.java.api.state.proxy;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.debug.api.programState.IStackFrame;
import jetbrains.mps.debug.api.programState.IWatchable;
import java.util.List;
import jetbrains.mps.debugger.java.api.state.watchables.JavaLocalVariable;
import jetbrains.mps.internal.collections.runtime.ListSequence;
import java.util.ArrayList;
import com.sun.jdi.AbsentInformationException;
import jetbrains.mps.util.Pair;
import com.sun.jdi.StackFrame;
import org.jetbrains.annotations.Nullable;
import jetbrains.mps.ide.ThreadUtils;
import jetbrains.mps.logging.Logger;
import com.sun.jdi.IncompatibleThreadStateException;
import com.sun.jdi.LocalVariable;
import com.sun.jdi.ObjectReference;
import jetbrains.mps.debugger.java.api.state.watchables.JavaThisObject;
import jetbrains.mps.debugger.java.api.state.watchables.JavaStaticContext;
import jetbrains.mps.debug.api.programState.IValue;
import jetbrains.mps.debugger.java.api.state.customViewers.CustomViewersManager;

@GeneratedClass(node = "r:ac4cce94-c169-4971-be8f-807482637028(jetbrains.mps.debugger.java.api.state.proxy)/3432969378036016687", model = "r:ac4cce94-c169-4971-be8f-807482637028(jetbrains.mps.debugger.java.api.state.proxy)")
public class JavaStackFrame extends ProxyForJava implements IStackFrame {
  private final int myIndex;
  private final JavaLocation myLocation;
  private final JavaThread myThread;
  private IWatchable myContextWatchable;
  private final List<JavaLocalVariable> myVariables = ListSequence.fromList(new ArrayList<JavaLocalVariable>());
  private boolean myInitialized;
  public JavaStackFrame(JavaThread threadReference, int i) throws AbsentInformationException {
    super(new Pair<JavaThread, Integer>(threadReference, i));
    myIndex = i;
    myThread = threadReference;
    StackFrame stackFrame = getStackFrame();
    if (stackFrame != null) {
      myLocation = new JavaLocation(stackFrame.location());
    } else {
      myLocation = null;
    }
  }
  @Override
  @Nullable
  public JavaLocation getLocation() {
    return myLocation;
  }
  @Override
  public JavaThread getThread() {
    return myThread;
  }
  @Nullable
  public StackFrame getStackFrame() {
    assert !(ThreadUtils.isInEDT());
    try {
      return getFrame();
    } catch (Throwable t) {
      Logger.getLogger(JavaStackFrame.class).error(t);
      return null;
    }
  }
  @Override
  public synchronized List<IWatchable> getVisibleWatchables() {
    List<IWatchable> watchables = ListSequence.fromList(new ArrayList<IWatchable>());
    ListSequence.fromList(watchables).addSequence(ListSequence.fromList(myVariables));
    if (myContextWatchable != null) {
      ListSequence.fromList(watchables).addElement(myContextWatchable);
    }
    return watchables;
  }
  public synchronized List<JavaLocalVariable> getVisibleVariables() {
    return myVariables;
  }
  public synchronized IWatchable getContextWatchable() {
    return myContextWatchable;
  }
  public synchronized void initializeWatchables() {
    if (myInitialized) {
      return;
    }
    myInitialized = true;
    try {
      ListSequence.fromList(myVariables).addSequence(ListSequence.fromList(fetchVisibleVariables()));
      myContextWatchable = fetchContextWatchable();
    } catch (IncompatibleThreadStateException ignore) {
    } catch (AbsentInformationException ignore) {
    }
  }
  private List<JavaLocalVariable> fetchVisibleVariables() throws IncompatibleThreadStateException, AbsentInformationException {
    assert !(ThreadUtils.isInEDT());

    StackFrame stackFrame = getFrame();
    List<JavaLocalVariable> result = new ArrayList<JavaLocalVariable>();
    if (stackFrame != null) {
      for (LocalVariable variable : stackFrame.visibleVariables()) {
        ListSequence.fromList(result).addElement(new JavaLocalVariable(variable, this, myThread.getThread()));
      }
    }
    return result;
  }
  private IWatchable fetchContextWatchable() throws IncompatibleThreadStateException {
    assert !(ThreadUtils.isInEDT());

    StackFrame stackFrame = getFrame();
    if (stackFrame != null) {
      ObjectReference thisObject = stackFrame.thisObject();
      if (thisObject != null) {
        return new JavaThisObject(thisObject, this, myThread.getThread());
      } else {
        return new JavaStaticContext(stackFrame.location().declaringType(), myThread.getThread());
      }
    }
    return null;
  }
  private StackFrame getFrame() throws IncompatibleThreadStateException {
    return myThread.getThread().frame(myIndex);
  }
  @Override
  public IValue getValue(IWatchable watchable) {
    assert !(ThreadUtils.isInEDT());
    try {
      if (watchable instanceof JavaLocalVariable) {
        JavaLocalVariable localVariable = (JavaLocalVariable) watchable;
        return CustomViewersManager.getInstance().fromJdi(getFrame().getValue(localVariable.getLocalVariable()), myThread.getThread());
      }
    } catch (IncompatibleThreadStateException e) {
    }
    return null;
  }
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || this.getClass() != o.getClass()) {
      return false;
    }

    JavaStackFrame that = (JavaStackFrame) o;
    if (myIndex != that.myIndex) {
      return false;
    }
    if ((myLocation != null ? !(myLocation.equals(that.myLocation)) : that.myLocation != null)) {
      return false;
    }
    if ((myThread != null ? !(myThread.equals(that.myThread)) : that.myThread != null)) {
      return false;
    }

    return true;
  }
  @Override
  public int hashCode() {
    int result = 0;
    result = 31 * result + myIndex;
    result = 31 * result + ((myLocation != null ? (myLocation).hashCode() : 0));
    result = 31 * result + ((myThread != null ? (myThread).hashCode() : 0));
    return result;
  }
}
