/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* import-globals-from ../debugger-controller.js */
/* import-globals-from ../debugger-view.js */
/* import-globals-from ../utils.js */
/* globals document */
"use strict";

/**
 * Functions handling the toolbar view: close button, expand/collapse button,
 * pause/resume and stepping buttons etc.
 */
function ToolbarView(DebuggerController, DebuggerView) {
  dumpn("ToolbarView was instantiated");

  this.StackFrames = DebuggerController.StackFrames;
  this.ThreadState = DebuggerController.ThreadState;
  this.DebuggerController = DebuggerController;
  this.DebuggerView = DebuggerView;

  this._onTogglePanesActivated = this._onTogglePanesActivated.bind(this);
  this._onTogglePanesPressed = this._onTogglePanesPressed.bind(this);
  this._onResumePressed = this._onResumePressed.bind(this);
  this._onStepOverPressed = this._onStepOverPressed.bind(this);
  this._onStepInPressed = this._onStepInPressed.bind(this);
  this._onStepOutPressed = this._onStepOutPressed.bind(this);
}

ToolbarView.prototype = {
  get activeThread() {
    return this.DebuggerController.activeThread;
  },

  get resumptionWarnFunc() {
    return this.DebuggerController._ensureResumptionOrder;
  },

  /**
   * Initialization function, called when the debugger is started.
   */
  initialize: function () {
    dumpn("Initializing the ToolbarView");

    this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
    this._resumeButton = document.getElementById("resume");
    this._stepOverButton = document.getElementById("step-over");
    this._stepInButton = document.getElementById("step-in");
    this._stepOutButton = document.getElementById("step-out");
    this._resumeOrderTooltip = new Tooltip(document);
    this._resumeOrderTooltip.defaultPosition = TOOLBAR_ORDER_POPUP_POSITION;

    let resumeKey = ShortcutUtils.prettifyShortcut(document.getElementById("resumeKey"));
    let stepOverKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOverKey"));
    let stepInKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepInKey"));
    let stepOutKey = ShortcutUtils.prettifyShortcut(document.getElementById("stepOutKey"));
    this._resumeTooltip = L10N.getFormatStr("resumeButtonTooltip", resumeKey);
    this._pauseTooltip = L10N.getFormatStr("pauseButtonTooltip", resumeKey);
    this._pausePendingTooltip = L10N.getStr("pausePendingButtonTooltip");
    this._stepOverTooltip = L10N.getFormatStr("stepOverTooltip", stepOverKey);
    this._stepInTooltip = L10N.getFormatStr("stepInTooltip", stepInKey);
    this._stepOutTooltip = L10N.getFormatStr("stepOutTooltip", stepOutKey);

    this._instrumentsPaneToggleButton.addEventListener("mousedown",
      this._onTogglePanesActivated, false);
    this._instrumentsPaneToggleButton.addEventListener("keydown",
      this._onTogglePanesPressed, false);
    this._resumeButton.addEventListener("mousedown", this._onResumePressed, false);
    this._stepOverButton.addEventListener("mousedown", this._onStepOverPressed, false);
    this._stepInButton.addEventListener("mousedown", this._onStepInPressed, false);
    this._stepOutButton.addEventListener("mousedown", this._onStepOutPressed, false);

    this._stepOverButton.setAttribute("tooltiptext", this._stepOverTooltip);
    this._stepInButton.setAttribute("tooltiptext", this._stepInTooltip);
    this._stepOutButton.setAttribute("tooltiptext", this._stepOutTooltip);
    this._toggleButtonsState({ enabled: false });

    this._addCommands();
  },

  /**
   * Destruction function, called when the debugger is closed.
   */
  destroy: function () {
    dumpn("Destroying the ToolbarView");

    this._instrumentsPaneToggleButton.removeEventListener("mousedown",
      this._onTogglePanesActivated, false);
    this._instrumentsPaneToggleButton.removeEventListener("keydown",
      this._onTogglePanesPressed, false);
    this._resumeButton.removeEventListener("mousedown", this._onResumePressed, false);
    this._stepOverButton.removeEventListener("mousedown", this._onStepOverPressed, false);
    this._stepInButton.removeEventListener("mousedown", this._onStepInPressed, false);
    this._stepOutButton.removeEventListener("mousedown", this._onStepOutPressed, false);
  },

  /**
   * Add commands that XUL can fire.
   */
  _addCommands: function () {
    XULUtils.addCommands(document.getElementById("debuggerCommands"), {
      resumeCommand: this.getCommandHandler("resumeCommand"),
      stepOverCommand: this.getCommandHandler("stepOverCommand"),
      stepInCommand: this.getCommandHandler("stepInCommand"),
      stepOutCommand: this.getCommandHandler("stepOutCommand")
    });
  },

  /**
   * Retrieve the callback associated with the provided  debugger command.
   *
   * @param {String} command
   *        The debugger command id.
   * @return {Function} the corresponding callback.
   */
  getCommandHandler: function (command) {
    switch (command) {
      case "resumeCommand":
        return () => this._onResumePressed();
      case "stepOverCommand":
        return () => this._onStepOverPressed();
      case "stepInCommand":
        return () => this._onStepInPressed();
      case "stepOutCommand":
        return () => this._onStepOutPressed();
      default:
        return () => {};
    }
  },

  /**
   * Display a warning when trying to resume a debuggee while another is paused.
   * Debuggees must be unpaused in a Last-In-First-Out order.
   *
   * @param string aPausedUrl
   *        The URL of the last paused debuggee.
   */
  showResumeWarning: function (aPausedUrl) {
    let label = L10N.getFormatStr("resumptionOrderPanelTitle", aPausedUrl);
    let defaultStyle = "default-tooltip-simple-text-colors";
    this._resumeOrderTooltip.setTextContent({ messages: [label] });
    this._resumeOrderTooltip.show(this._resumeButton);
  },

  /**
   * Sets the resume button state based on the debugger active thread.
   *
   * @param string aState
   *        Either "paused", "attached", or "breakOnNext".
   * @param boolean hasLocation
   *        True if we are paused at a specific JS location
   */
  toggleResumeButtonState: function (aState, hasLocation) {
    // Intermidiate state after pressing the pause button and waiting
    // for the next script execution to happen.
    if (aState == "breakOnNext") {
      this._resumeButton.setAttribute("break-on-next", "true");
      this._resumeButton.disabled = true;
      this._resumeButton.setAttribute("tooltiptext", this._pausePendingTooltip);
      return;
    }

    this._resumeButton.removeAttribute("break-on-next");
    this._resumeButton.disabled = false;

    // If we're paused, check and show a resume label on the button.
    if (aState == "paused") {
      this._resumeButton.setAttribute("checked", "true");
      this._resumeButton.setAttribute("tooltiptext", this._resumeTooltip);

      // Only enable the stepping buttons if we are paused at a
      // specific location. After bug 789430, we'll always be paused
      // at a location, but currently you can pause the entire engine
      // at any point without knowing the location.
      if (hasLocation) {
        this._toggleButtonsState({ enabled: true });
      }
    }
    // If we're attached, do the opposite.
    else if (aState == "attached") {
      this._resumeButton.removeAttribute("checked");
      this._resumeButton.setAttribute("tooltiptext", this._pauseTooltip);
      this._toggleButtonsState({ enabled: false });
    }
  },

  _toggleButtonsState: function ({ enabled }) {
    const buttons = [
      this._stepOutButton,
      this._stepInButton,
      this._stepOverButton
    ];
    for (let button of buttons) {
      button.disabled = !enabled;
    }
  },

  /**
  * Listener handling the toggle button space and return key event.
  */
  _onTogglePanesPressed: function (event) {
    if (ViewHelpers.isSpaceOrReturn(event)) {
      this._onTogglePanesActivated();
    }
  },

  /**
   * Listener handling the toggle button click event.
   */
  _onTogglePanesActivated: function() {
    DebuggerView.toggleInstrumentsPane({
      visible: DebuggerView.instrumentsPaneHidden,
      animated: true,
      delayed: true
    });
  },

  /**
   * Listener handling the pause/resume button click event.
   */
  _onResumePressed: function () {
    if (this.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL ||
        this._resumeButton.disabled) {
      return;
    }

    if (this.activeThread.paused) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.resume(this.resumptionWarnFunc);
    } else {
      this.ThreadState.interruptedByResumeButton = true;
      this.toggleResumeButtonState("breakOnNext");
      this.activeThread.breakOnNext();
    }
  },

  /**
   * Listener handling the step over button click event.
   */
  _onStepOverPressed: function () {
    if (this.activeThread.paused && !this._stepOverButton.disabled) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.stepOver(this.resumptionWarnFunc);
    }
  },

  /**
   * Listener handling the step in button click event.
   */
  _onStepInPressed: function () {
    if (this.StackFrames._currentFrameDescription != FRAME_TYPE.NORMAL ||
       this._stepInButton.disabled) {
      return;
    }

    if (this.activeThread.paused) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.stepIn(this.resumptionWarnFunc);
    }
  },

  /**
   * Listener handling the step out button click event.
   */
  _onStepOutPressed: function () {
    if (this.activeThread.paused && !this._stepOutButton.disabled) {
      this.StackFrames.currentFrameDepth = -1;
      this.activeThread.stepOut(this.resumptionWarnFunc);
    }
  },

  _instrumentsPaneToggleButton: null,
  _resumeButton: null,
  _stepOverButton: null,
  _stepInButton: null,
  _stepOutButton: null,
  _resumeOrderTooltip: null,
  _resumeTooltip: "",
  _pauseTooltip: "",
  _stepOverTooltip: "",
  _stepInTooltip: "",
  _stepOutTooltip: ""
};

DebuggerView.Toolbar = new ToolbarView(DebuggerController, DebuggerView);