package jetbrains.mps.debugger.java.runtime.breakpoints;

/*Generated by MPS */

import jetbrains.mps.annotations.GeneratedClass;
import jetbrains.mps.debug.api.breakpoints.ILocationBreakpoint;
import jetbrains.mps.logging.Logger;
import org.jetbrains.mps.openapi.model.SNodeReference;
import jetbrains.mps.debug.api.breakpoints.BreakpointLocation;
import com.intellij.openapi.project.Project;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.mps.openapi.model.SNode;
import jetbrains.mps.debugger.java.runtime.engine.events.EventsProcessor;
import com.sun.jdi.ReferenceType;
import jetbrains.mps.debugger.java.runtime.engine.RequestManager;
import com.sun.jdi.request.MethodEntryRequest;
import com.sun.jdi.request.MethodExitRequest;
import com.sun.jdi.ClassNotPreparedException;
import com.sun.jdi.ObjectCollectedException;
import jetbrains.mps.textgen.trace.TraceablePositionInfo;
import jetbrains.mps.debugger.java.runtime.engine.events.EventContext;
import com.sun.jdi.event.LocatableEvent;
import com.sun.jdi.Method;
import com.sun.jdi.event.MethodEntryEvent;
import com.sun.jdi.event.MethodExitEvent;
import java.util.Objects;

@GeneratedClass(node = "r:b4441af2-7d93-477f-8f98-ff1136374539(jetbrains.mps.debugger.java.runtime.breakpoints)/2891782949125146029", model = "r:b4441af2-7d93-477f-8f98-ff1136374539(jetbrains.mps.debugger.java.runtime.breakpoints)")
public class MethodBreakpoint extends JavaBreakpoint implements ILocationBreakpoint {
  private static final Logger LOG = Logger.getLogger(MethodBreakpoint.class);
  private final SNodeReference myNode;
  private BreakpointLocation myLocation;
  private String myMethodName = null;
  private String myJniSignature = null;

  public MethodBreakpoint(SNodeReference nodePointer, Project project) {
    super(project);
    myNode = nodePointer;
  }
  public MethodBreakpoint(@NotNull SNode node, Project project) {
    this(node.getReference(), project);
  }

  @NotNull
  @Override
  public BreakpointLocation getLocation() {
    if (myLocation == null) {
      myLocation = new BreakpointLocationUpdate(myNode, getRepository()).get();
    }
    return myLocation;
  }

  @Override
  protected String getClassNameToPrepare() {
    return getLocation().getTargetUnitName();
  }
  @NotNull
  @Override
  public JavaBreakpointKind getKind() {
    return JavaBreakpointKind.METHOD_BREAKPOINT;
  }
  @Override
  public String getPresentation() {
    return new BreakpointPresentation(getLocation(), getRepository()).getText();
  }
  @Override
  protected void createRequestForPreparedClass(EventsProcessor debugProcess, ReferenceType classType) {
    RequestManager requestManager = debugProcess.getRequestManager();
    if (!(updateMethodNameAndSignature())) {
      return;
    }
    try {
      MethodEntryRequest methodEntryRequest = requestManager.createMethodEntryRequest(this, classType);
      MethodExitRequest methodExitRequest = requestManager.createMethodExitRequest(this, classType);
      requestManager.enableRequest(methodEntryRequest);
      requestManager.enableRequest(methodExitRequest);
    } catch (ClassNotPreparedException ex) {
      LOG.warning("ClassNotPreparedException: " + ex.getMessage());
      //  there's a chance to add a breakpoint when the class is prepared
    } catch (ObjectCollectedException ex) {
      LOG.warning("ObjectCollectedException: " + ex.getMessage());
    } catch (Exception ex) {
      LOG.error(ex);
    }
  }

  private boolean updateMethodNameAndSignature() {
    if (myMethodName != null && myJniSignature != null) {
      return true;
    }
    TraceablePositionInfo targetCodePosition = getLocation().getTargetCodePosition();
    if (targetCodePosition == null) {
      return false;
    }
    String propertyString = targetCodePosition.getPropertyString();
    if (propertyString == null) {
      return false;
    }
    //  todo property string format should be described in one place
    String[] split = propertyString.split("#");
    if (split.length != 2) {
      return false;
    }
    myMethodName = split[0];
    myJniSignature = split[1];
    return myMethodName != null && myJniSignature != null;
  }
  @Override
  public boolean isRequestHitByEvent(EventContext context, LocatableEvent event) {
    boolean result = super.isRequestHitByEvent(context, event);
    if (!(result)) {
      return false;
    }
    Method method = null;
    if (event instanceof MethodEntryEvent) {
      method = ((MethodEntryEvent) event).method();
    } else
    if (event instanceof MethodExitEvent) {
      method = ((MethodExitEvent) event).method();
    }
    return !(method == null || !(accept(method)));
  }
  private boolean accept(Method method) {
    return method.name().equals(myMethodName) && method.signature().equals(myJniSignature);
  }
  @Override
  public boolean isValid() {
    return super.isValid() && updateMethodNameAndSignature();
  }
  @Override
  public boolean equals(Object o) {
    if (this == o) {
      return true;
    }
    if (o == null || this.getClass() != o.getClass()) {
      return false;
    }

    return Objects.equals(getLocation(), ((MethodBreakpoint) o).getLocation());
  }
  @Override
  public int hashCode() {
    return myNode.hashCode() + getKind().hashCode() * 31;
  }
}
