// Copyright 2015 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef V8_INSPECTOR_V8DEBUGGERAGENTIMPL_H_
#define V8_INSPECTOR_V8DEBUGGERAGENTIMPL_H_

#include <vector>

#include "src/base/macros.h"
#include "src/inspector/java-script-call-frame.h"
#include "src/inspector/protocol/Debugger.h"
#include "src/inspector/protocol/Forward.h"

namespace v8_inspector {

struct ScriptBreakpoint;
class JavaScriptCallFrame;
class PromiseTracker;
class V8Debugger;
class V8DebuggerScript;
class V8InspectorImpl;
class V8InspectorSessionImpl;
class V8Regex;
class V8StackTraceImpl;

using protocol::Maybe;
using protocol::Response;

class V8DebuggerAgentImpl : public protocol::Debugger::Backend {
 public:
  enum SkipPauseRequest {
    RequestNoSkip,
    RequestContinue,
    RequestStepInto,
    RequestStepOut,
    RequestStepFrame
  };

  enum BreakpointSource {
    UserBreakpointSource,
    DebugCommandBreakpointSource,
    MonitorCommandBreakpointSource
  };

  V8DebuggerAgentImpl(V8InspectorSessionImpl*, protocol::FrontendChannel*,
                      protocol::DictionaryValue* state);
  ~V8DebuggerAgentImpl() override;
  void restore();

  // Part of the protocol.
  Response enable() override;
  Response disable() override;
  Response setBreakpointsActive(bool active) override;
  Response setSkipAllPauses(bool skip) override;
  Response setBreakpointByUrl(
      int lineNumber, Maybe<String16> optionalURL,
      Maybe<String16> optionalURLRegex, Maybe<int> optionalColumnNumber,
      Maybe<String16> optionalCondition, String16*,
      std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations)
      override;
  Response setBreakpoint(
      std::unique_ptr<protocol::Debugger::Location>,
      Maybe<String16> optionalCondition, String16*,
      std::unique_ptr<protocol::Debugger::Location>* actualLocation) override;
  Response removeBreakpoint(const String16& breakpointId) override;
  Response continueToLocation(
      std::unique_ptr<protocol::Debugger::Location>) override;
  Response searchInContent(
      const String16& scriptId, const String16& query,
      Maybe<bool> optionalCaseSensitive, Maybe<bool> optionalIsRegex,
      std::unique_ptr<protocol::Array<protocol::Debugger::SearchMatch>>*)
      override;
  Response getPossibleBreakpoints(
      std::unique_ptr<protocol::Debugger::Location> start,
      Maybe<protocol::Debugger::Location> end,
      std::unique_ptr<protocol::Array<protocol::Debugger::Location>>* locations)
      override;
  Response setScriptSource(
      const String16& inScriptId, const String16& inScriptSource,
      Maybe<bool> dryRun,
      Maybe<protocol::Array<protocol::Debugger::CallFrame>>* optOutCallFrames,
      Maybe<bool>* optOutStackChanged,
      Maybe<protocol::Runtime::StackTrace>* optOutAsyncStackTrace,
      Maybe<protocol::Runtime::ExceptionDetails>* optOutCompileError) override;
  Response restartFrame(
      const String16& callFrameId,
      std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>*
          newCallFrames,
      Maybe<protocol::Runtime::StackTrace>* asyncStackTrace) override;
  Response getScriptSource(const String16& scriptId,
                           String16* scriptSource) override;
  Response pause() override;
  Response resume() override;
  Response stepOver() override;
  Response stepInto() override;
  Response stepOut() override;
  Response setPauseOnExceptions(const String16& pauseState) override;
  Response evaluateOnCallFrame(
      const String16& callFrameId, const String16& expression,
      Maybe<String16> objectGroup, Maybe<bool> includeCommandLineAPI,
      Maybe<bool> silent, Maybe<bool> returnByValue,
      Maybe<bool> generatePreview,
      std::unique_ptr<protocol::Runtime::RemoteObject>* result,
      Maybe<protocol::Runtime::ExceptionDetails>*) override;
  Response setVariableValue(
      int scopeNumber, const String16& variableName,
      std::unique_ptr<protocol::Runtime::CallArgument> newValue,
      const String16& callFrame) override;
  Response setAsyncCallStackDepth(int depth) override;
  Response setBlackboxPatterns(
      std::unique_ptr<protocol::Array<String16>> patterns) override;
  Response setBlackboxedRanges(
      const String16& scriptId,
      std::unique_ptr<protocol::Array<protocol::Debugger::ScriptPosition>>
          positions) override;

  bool enabled();

  void setBreakpointAt(const String16& scriptId, int lineNumber,
                       int columnNumber, BreakpointSource,
                       const String16& condition = String16());
  void removeBreakpointAt(const String16& scriptId, int lineNumber,
                          int columnNumber, BreakpointSource);
  void schedulePauseOnNextStatement(
      const String16& breakReason,
      std::unique_ptr<protocol::DictionaryValue> data);
  void cancelPauseOnNextStatement();
  void breakProgram(const String16& breakReason,
                    std::unique_ptr<protocol::DictionaryValue> data);
  void breakProgramOnException(const String16& breakReason,
                               std::unique_ptr<protocol::DictionaryValue> data);

  void reset();

  // Interface for V8InspectorImpl
  SkipPauseRequest didPause(v8::Local<v8::Context>,
                            v8::Local<v8::Value> exception,
                            const std::vector<String16>& hitBreakpoints,
                            bool isPromiseRejection, bool isUncaught);
  void didContinue();
  void didParseSource(std::unique_ptr<V8DebuggerScript>, bool success);
  void willExecuteScript(int scriptId);
  void didExecuteScript();

  v8::Isolate* isolate() { return m_isolate; }

 private:
  void enableImpl();

  SkipPauseRequest shouldSkipExceptionPause(JavaScriptCallFrame* topCallFrame);
  SkipPauseRequest shouldSkipStepPause(JavaScriptCallFrame* topCallFrame);

  void schedulePauseOnNextStatementIfSteppingInto();

  Response currentCallFrames(
      std::unique_ptr<protocol::Array<protocol::Debugger::CallFrame>>*);
  std::unique_ptr<protocol::Runtime::StackTrace> currentAsyncStackTrace();

  void changeJavaScriptRecursionLevel(int step);

  void setPauseOnExceptionsImpl(int);

  std::unique_ptr<protocol::Debugger::Location> resolveBreakpoint(
      const String16& breakpointId, const String16& scriptId,
      const ScriptBreakpoint&, BreakpointSource);
  void removeBreakpointImpl(const String16& breakpointId);
  void clearBreakDetails();

  bool isCurrentCallStackEmptyOrBlackboxed();
  bool isTopPausedCallFrameBlackboxed();
  bool isCallFrameWithUnknownScriptOrBlackboxed(JavaScriptCallFrame*);

  void internalSetAsyncCallStackDepth(int);
  void increaseCachedSkipStackGeneration();

  Response setBlackboxPattern(const String16& pattern);

  using ScriptsMap =
      protocol::HashMap<String16, std::unique_ptr<V8DebuggerScript>>;
  using BreakpointIdToDebuggerBreakpointIdsMap =
      protocol::HashMap<String16, std::vector<String16>>;
  using DebugServerBreakpointToBreakpointIdAndSourceMap =
      protocol::HashMap<String16, std::pair<String16, BreakpointSource>>;
  using MuteBreakpoins = protocol::HashMap<String16, std::pair<String16, int>>;

  enum DebuggerStep { NoStep = 0, StepInto, StepOver, StepOut };

  V8InspectorImpl* m_inspector;
  V8Debugger* m_debugger;
  V8InspectorSessionImpl* m_session;
  bool m_enabled;
  protocol::DictionaryValue* m_state;
  protocol::Debugger::Frontend m_frontend;
  v8::Isolate* m_isolate;
  v8::Global<v8::Context> m_pausedContext;
  JavaScriptCallFrames m_pausedCallFrames;
  ScriptsMap m_scripts;
  BreakpointIdToDebuggerBreakpointIdsMap m_breakpointIdToDebuggerBreakpointIds;
  DebugServerBreakpointToBreakpointIdAndSourceMap m_serverBreakpoints;
  String16 m_continueToLocationBreakpointId;
  String16 m_breakReason;
  std::unique_ptr<protocol::DictionaryValue> m_breakAuxData;
  DebuggerStep m_scheduledDebuggerStep;
  bool m_skipNextDebuggerStepOut;
  bool m_javaScriptPauseScheduled;
  bool m_steppingFromFramework;
  bool m_pausingOnNativeEvent;

  int m_skippedStepFrameCount;
  int m_recursionLevelForStepOut;
  int m_recursionLevelForStepFrame;
  bool m_skipAllPauses;

  std::unique_ptr<V8Regex> m_blackboxPattern;
  protocol::HashMap<String16, std::vector<std::pair<int, int>>>
      m_blackboxedPositions;

  DISALLOW_COPY_AND_ASSIGN(V8DebuggerAgentImpl);
};

}  // namespace v8_inspector

#endif  // V8_INSPECTOR_V8DEBUGGERAGENTIMPL_H_