// Copyright 2016 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_DEBUG_DEBUG_INTERFACE_H_
#define V8_DEBUG_DEBUG_INTERFACE_H_

#include "include/v8-debug.h"
#include "include/v8-util.h"
#include "include/v8.h"

namespace v8 {

class DebugInterface {
 public:
  /**
   * An event details object passed to the debug event listener.
   */
  class EventDetails : public v8::Debug::EventDetails {
   public:
    /**
     * Event type.
     */
    virtual v8::DebugEvent GetEvent() const = 0;

    /**
     * Access to execution state and event data of the debug event. Don't store
     * these cross callbacks as their content becomes invalid.
     */
    virtual Local<Object> GetExecutionState() const = 0;
    virtual Local<Object> GetEventData() const = 0;

    /**
     * Get the context active when the debug event happened. Note this is not
     * the current active context as the JavaScript part of the debugger is
     * running in its own context which is entered at this point.
     */
    virtual Local<Context> GetEventContext() const = 0;

    /**
     * Client data passed with the corresponding callback when it was
     * registered.
     */
    virtual Local<Value> GetCallbackData() const = 0;

    virtual ~EventDetails() {}
  };

  /**
   * Debug event callback function.
   *
   * \param event_details object providing information about the debug event
   *
   * A EventCallback does not take possession of the event data,
   * and must not rely on the data persisting after the handler returns.
   */
  typedef void (*EventCallback)(const EventDetails& event_details);

  static bool SetDebugEventListener(Isolate* isolate, EventCallback that,
                                    Local<Value> data = Local<Value>());

  /**
   * Debugger is running in its own context which is entered while debugger
   * messages are being dispatched. This is an explicit getter for this
   * debugger context. Note that the content of the debugger context is subject
   * to change. The Context exists only when the debugger is active, i.e. at
   * least one DebugEventListener or MessageHandler is set.
   */
  static Local<Context> GetDebugContext(Isolate* isolate);

  /**
   * Run a JavaScript function in the debugger.
   * \param fun the function to call
   * \param data passed as second argument to the function
   * With this call the debugger is entered and the function specified is called
   * with the execution state as the first argument. This makes it possible to
   * get access to information otherwise not available during normal JavaScript
   * execution e.g. details on stack frames. Receiver of the function call will
   * be the debugger context global object, however this is a subject to change.
   * The following example shows a JavaScript function which when passed to
   * v8::Debug::Call will return the current line of JavaScript execution.
   *
   * \code
   *   function frame_source_line(exec_state) {
   *     return exec_state.frame(0).sourceLine();
   *   }
   * \endcode
   */
  // TODO(dcarney): data arg should be a MaybeLocal
  static MaybeLocal<Value> Call(Local<Context> context,
                                v8::Local<v8::Function> fun,
                                Local<Value> data = Local<Value>());

  /**
   * Enable/disable LiveEdit functionality for the given Isolate
   * (default Isolate if not provided). V8 will abort if LiveEdit is
   * unexpectedly used. LiveEdit is enabled by default.
   */
  static void SetLiveEditEnabled(Isolate* isolate, bool enable);

  // Schedule a debugger break to happen when JavaScript code is run
  // in the given isolate.
  static void DebugBreak(Isolate* isolate);

  // Remove scheduled debugger break in given isolate if it has not
  // happened yet.
  static void CancelDebugBreak(Isolate* isolate);

  /**
   * Returns array of internal properties specific to the value type. Result has
   * the following format: [<name>, <value>,...,<name>, <value>]. Result array
   * will be allocated in the current context.
   */
  static MaybeLocal<Array> GetInternalProperties(Isolate* isolate,
                                                 Local<Value> value);

  enum ExceptionBreakState {
    NoBreakOnException = 0,
    BreakOnUncaughtException = 1,
    BreakOnAnyException = 2
  };

  /**
   * Defines if VM will pause on exceptions or not.
   * If BreakOnAnyExceptions is set then VM will pause on caught and uncaught
   * exception, if BreakOnUncaughtException is set then VM will pause only on
   * uncaught exception, otherwise VM won't stop on any exception.
   */
  static void ChangeBreakOnException(Isolate* isolate,
                                     ExceptionBreakState state);

  enum StepAction {
    StepOut = 0,   // Step out of the current function.
    StepNext = 1,  // Step to the next statement in the current function.
    StepIn = 2,    // Step into new functions invoked or the next statement
                   // in the current function.
    StepFrame = 3  // Step into a new frame or return to previous frame.
  };

  static void PrepareStep(Isolate* isolate, StepAction action);
  static void ClearStepping(Isolate* isolate);

  /**
   * Defines location inside script.
   * Lines and columns are 0-based.
   */
  class Location {
   public:
    Location(int lineNumber, int columnNumber);
    /**
     * Create empty location.
     */
    Location();

    int GetLineNumber() const;
    int GetColumnNumber() const;
    bool IsEmpty() const;

   private:
    int lineNumber_;
    int columnNumber_;
  };

  /**
   * Native wrapper around v8::internal::Script object.
   */
  class Script {
   public:
    v8::Isolate* GetIsolate() const;

    ScriptOriginOptions OriginOptions() const;
    bool WasCompiled() const;
    int Id() const;
    int LineOffset() const;
    int ColumnOffset() const;
    std::vector<int> LineEnds() const;
    MaybeLocal<String> Name() const;
    MaybeLocal<String> SourceURL() const;
    MaybeLocal<String> SourceMappingURL() const;
    MaybeLocal<String> ContextData() const;
    MaybeLocal<String> Source() const;
    bool GetPossibleBreakpoints(const Location& start, const Location& end,
                                std::vector<Location>* locations) const;

    /**
     * script parameter is a wrapper v8::internal::JSObject for
     * v8::internal::Script.
     * This function gets v8::internal::Script from v8::internal::JSObject and
     * wraps it with DebugInterface::Script.
     * Returns empty local if not called with a valid wrapper of
     * v8::internal::Script.
     */
    static MaybeLocal<Script> Wrap(Isolate* isolate,
                                   v8::Local<v8::Object> script);

   private:
    int GetSourcePosition(const Location& location) const;
  };

  /**
   * Return array of compiled scripts.
   */
  static void GetLoadedScripts(Isolate* isolate,
                               PersistentValueVector<Script>& scripts);
};

}  // namespace v8

#endif  // V8_DEBUG_DEBUG_INTERFACE_H_
