/* 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/. */

/*
 * Handles nav overlay button positioning.
 */

// minimum amount of movement using the mouse after which we cancel the button click handlers
const kOnClickMargin = 3;

const kNavButtonPref = "browser.display.overlaynavbuttons";

var NavButtonSlider = {
  _back: document.getElementById("overlay-back"),
  _plus: document.getElementById("overlay-plus"),
  _mouseMoveStarted: false,
  _mouseDown: false,
  _yPos: -1,

  get back() {
    return this._back;
  },

  get plus() {
    return this._plus;
  },

  /*
   * custom dragger, see input.js
   */

  freeDrag: function freeDrag() {
    return true;
  },

  isDraggable: function isDraggable(aTarget, aContent) {
    return { x: false, y: true };
  },

  dragStart: function dragStart(aX, aY, aTarget, aScroller) {
    return true;
  },

  dragStop: function dragStop(aDx, aDy, aScroller) {
    return true;
  },

  dragMove: function dragMove(aDx, aDy, aScroller, aIsKenetic, aClientX, aClientY) {
    // Note if aIsKenetic is true this is synthetic movement,
    // we don't want that so return false.
    if (aIsKenetic) {
      return false;
    }
    
    this._updatePosition(aClientY);

    // return true if we moved, false otherwise. The result
    // is used in deciding if we should repaint between drags.
    return true;
  },

  /*
   * logic
   */

  init: function init() {
    // Touch drag support provided by input.js
    this._back.customDragger = this;
    this._plus.customDragger = this;
    Elements.browsers.addEventListener("ContentSizeChanged", this, true);
    let events = ["mousedown", "mouseup", "mousemove", "click", "touchstart", "touchmove", "touchend"];
    events.forEach(function (value) {
      this._back.addEventListener(value, this, true);
      this._plus.addEventListener(value, this, true);
    }, this);

    this._updateStops();
    this._updateVisibility();
    Services.prefs.addObserver(kNavButtonPref, this, false);
  },

  observe: function (aSubject, aTopic, aData) {
    if (aTopic == "nsPref:changed" && aData == kNavButtonPref) {
      this._updateVisibility();
    }
  },

  _updateVisibility: function () {
    if (Services.prefs.getBoolPref(kNavButtonPref)) {
      this._back.removeAttribute("hidden");
      this._plus.removeAttribute("hidden");
    } else {
      this._back.setAttribute("hidden", true);
      this._plus.setAttribute("hidden", true);
    }
  },

  _updateStops: function () {
    this._contentHeight = ContentAreaObserver.contentHeight;
    this._imageHeight = 118;
    this._topStop = this._imageHeight * .7;
    this._bottomStop = this._contentHeight - (this._imageHeight * .7);

    // Check to see if we need to move the slider into view
    if (this._yPos != -1 &&
        (this._topStop > this._yPos || this._bottomStop < this._yPos)) {
      this._back.style.top = "50%";
      this._plus.style.top = "50%";
    }
  },

  _getPosition: function _getPosition() {
    this._yPos = parseInt(getComputedStyle(this._back).top);
  },

  _setPosition: function _setPosition() {
    this._back.style.top = this._yPos + "px";
    this._plus.style.top = this._yPos + "px";
  },

  _updatePosition: function (aClientY) {
    if (this._topStop > aClientY || this._bottomStop < aClientY)
      return;
    this._yPos = aClientY;
    this._setPosition();
  },

  _updateOffset: function (aOffset) {
    let newPos = this._yPos + aOffset;
    if (this._topStop > newPos || this._bottomStop < newPos)
      return;
    this._yPos = newPos;
    this._setPosition();
  },

  /*
   * Events
   */

  handleEvent: function handleEvent(aEvent) {
    switch (aEvent.type) {
      case "ContentSizeChanged":
        this._updateStops();
        break;

      case "touchstart":
        if (aEvent.touches.length != 1)
          break;
        aEvent.preventDefault();
        aEvent = aEvent.touches[0];
      case "mousedown":
        this._getPosition();
        this._mouseDown = true;
        this._mouseMoveStarted = false;
        this._mouseY = aEvent.clientY;
        if (aEvent.originalTarget)
          aEvent.originalTarget.setCapture();
        this._back.setAttribute("mousedrag", true);
        this._plus.setAttribute("mousedrag", true);
        break;

      case "touchend":
        if (aEvent.touches.length != 0)
          break;
        this._mouseDown = false;
        this._back.removeAttribute("mousedrag");
        this._plus.removeAttribute("mousedrag");
        if (!this._mouseMoveStarted) {
          if (aEvent.originalTarget == this._back) {
            CommandUpdater.doCommand('cmd_back');
          } else {
            CommandUpdater.doCommand('cmd_newTab');
          }
        }
        break;
      case "mouseup":
        this._mouseDown = false;
        this._back.removeAttribute("mousedrag");
        this._plus.removeAttribute("mousedrag");
        break;

      case "touchmove":
        if (aEvent.touches.length != 1)
          break;
        aEvent = aEvent.touches[0];
      case "mousemove":
        // Check to be sure this is a drag operation
        if (!this._mouseDown) {
          return;
        }
        // Don't start a drag until we've passed a threshold
        let dy = aEvent.clientY - this._mouseY;
        if (!this._mouseMoveStarted && Math.abs(dy) < kOnClickMargin) {
          return;
        }
        // Start dragging via the mouse
        this._mouseMoveStarted = true;
        this._mouseY = aEvent.clientY;
        this._updateOffset(dy);
        break;
      case "click":
        // Don't invoke the click action if we've moved the buttons via the mouse.
        if (this._mouseMoveStarted) {
          return;
        }
        if (aEvent.originalTarget == this._back) {
           CommandUpdater.doCommand('cmd_back');
        } else {
           CommandUpdater.doCommand('cmd_newTab');
        }
        break;
    }
  },
};


