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

// Fired when the tabtray is displayed
const kContextUITabsShowEvent = "MozContextUITabsShow";
// add more as needed...

/*
 * Manages context UI (navbar, tabbar, appbar) and track visibility. Also
 * tracks events that summon and hide the context UI.
 */
var ContextUI = {
  _expandable: true,
  _hidingId: 0,

  /*******************************************
   * init
   */

  init: function init() {
    Elements.browsers.addEventListener('URLChanged', this, true);
    Elements.browsers.addEventListener("AlertActive", this, true);
    Elements.browsers.addEventListener("AlertClose", this, true);
    Elements.tabList.addEventListener('TabSelect', this, true);
    Elements.panelUI.addEventListener('ToolPanelShown', this, false);
    Elements.panelUI.addEventListener('ToolPanelHidden', this, false);

    Elements.tray.addEventListener("mousemove", this, false);
    Elements.tray.addEventListener("mouseleave", this, false);

    window.addEventListener("touchstart", this, true);
    window.addEventListener("mousedown", this, true);
    window.addEventListener("MozEdgeUIStarted", this, true);
    window.addEventListener("MozEdgeUICanceled", this, true);
    window.addEventListener("MozEdgeUICompleted", this, true);
    window.addEventListener("keypress", this, true);
    window.addEventListener("KeyboardChanged", this, false);
    window.addEventListener("MozFlyoutPanelShowing", this, false);

    Elements.tray.addEventListener("transitionend", this, true);

    Appbar.init();
  },

  /*******************************************
   * Context UI state getters & setters
   */

  // any visiblilty
  get isVisible() {
    return this.navbarVisible || this.tabbarVisible || this.contextAppbarVisible;
  },

  // navbar visiblilty
  get navbarVisible() {
    return (Elements.navbar.hasAttribute("visible") ||
            Elements.navbar.hasAttribute("startpage"));
  },

  // tabbar visiblilty
  get tabbarVisible() {
    return Elements.tray.hasAttribute("expanded");
  },

  // appbar visiblilty
  get contextAppbarVisible() {
    return Elements.contextappbar.isShowing;
  },

  // currently not in use, for the always show tabs feature
  get isExpandable() { return this._expandable; },
  set isExpandable(aFlag) {
    this._expandable = aFlag;
    if (!this._expandable)
      this.dismiss();
  },

  /*******************************************
   * Public api
   */

  /*
   * Toggle the current nav UI state. Fires context ui events.
   */
  toggleNavUI: function () {
    // The navbar is forced open when the start page is visible. appbar.js
    // controls the "visible" property, and browser-ui controls the "startpage"
    // property. Hence we rely on the tabbar for current toggle state.
    if (this.tabbarVisible) {
      this.dismiss();
    } else {
      this.displayNavUI();
    }
  },

  /*
   * Show the nav and tabs bar. Returns true if any non-visible UI
   * was shown. Fires context ui events.
   */
  displayNavUI: function () {
    let shown = false;

    if (!this.navbarVisible) {
      BrowserUI.updateURI();
      this.displayNavbar();
      shown = true;
    }

    if (!this.tabbarVisible) {
      this.displayTabs();
      shown = true;
    }

    if (shown) {
      ContentAreaObserver.updateContentArea();
    }

    return shown;
  },

  /*
   * Dismiss any context UI currently visible. Returns true if any
   * visible UI was dismissed. Fires context ui events.
   */
  dismiss: function () {
    let dismissed = false;

    this._clearDelayedTimeout();

    // No assurances this will hide the nav bar. It may have the
    // 'startpage' property set. This removes the 'visible' property.
    if (this.navbarVisible) {
      BrowserUI.blurNavBar();
      this.dismissNavbar();
      dismissed = true;
    }
    if (this.tabbarVisible) {
      this.dismissTabs();
      dismissed = true;
    }
    if (Elements.contextappbar.isShowing) {
      this.dismissContextAppbar();
      dismissed = true;
    }

    if (dismissed) {
      ContentAreaObserver.updateContentArea();
    }

    return dismissed;
  },

  /*
   * Briefly show the tab bar and then hide it. Fires context ui events.
   */
  peekTabs: function peekTabs(aDelay) {
    if (!this.tabbarVisible)
      this.displayTabs();

    ContextUI.dismissTabsWithDelay(aDelay);
  },

  /*
   * Dismiss tab bar after a delay. Fires context ui events.
   */
  dismissTabsWithDelay: function (aDelay) {
    aDelay = aDelay || kForegroundTabAnimationDelay;
    this._clearDelayedTimeout();
    this._lastTimeoutDelay = aDelay;
    this._hidingId = setTimeout(function () {
        ContextUI.dismissTabs();
      }, aDelay);
  },

  /*
   * Display the nav bar.
   *
   * @return false if we were already visible, and didn't do anything.
   */
  displayNavbar: function () {
    if (Elements.chromeState.getAttribute("navbar") == "visible") {
      return false;
    }

    Elements.navbar.show();
    Elements.chromeState.setAttribute("navbar", "visible");
    ContentAreaObserver.updateContentArea();
    return true;
  },

  // Display the tab tray
  displayTabs: function () {
    this._clearDelayedTimeout();
    this._setIsExpanded(true);
  },

  // Dismiss the navbar if visible.
  dismissNavbar: function dismissNavbar() {
    if (!BrowserUI.isStartTabVisible) {
      Elements.autocomplete.closePopup();
      Elements.navbar.dismiss();
      Elements.chromeState.removeAttribute("navbar");
      ContentAreaObserver.updateContentArea();
    }
  },

  // Dismiss the tabstray if visible.
  dismissTabs: function dimissTabs() {
    this._clearDelayedTimeout();
    this._setIsExpanded(false);
  },

  // Dismiss the appbar if visible.
  dismissContextAppbar: function dismissContextAppbar() {
    Elements.contextappbar.dismiss();
  },

  /*******************************************
   * Internal utils
   */

  // tabtray state
  _setIsExpanded: function _setIsExpanded(aFlag, setSilently) {
    // if the tray can't be expanded, don't expand it.
    if (!this.isExpandable || this.tabbarVisible == aFlag)
      return;

    if (aFlag)
      Elements.tray.setAttribute("expanded", "true");
    else
      Elements.tray.removeAttribute("expanded");

    if (!setSilently)
      this._fire(kContextUITabsShowEvent);
  },

  _clearDelayedTimeout: function _clearDelayedTimeout() {
    if (this._hidingId) {
      clearTimeout(this._hidingId);
      this._hidingId = 0;
      this._delayedHide = false;
    }
  },

  _resetDelayedTimeout: function () {
    this._hidingId = setTimeout(function () {
        ContextUI.dismissTabs();
      }, this._lastTimeoutDelay);
  },

  /*******************************************
   * Events
   */

  _onEdgeUIStarted: function(aEvent) {
    this._hasEdgeSwipeStarted = true;
    this._clearDelayedTimeout();
    this.toggleNavUI();
  },

  _onEdgeUICanceled: function(aEvent) {
    this._hasEdgeSwipeStarted = false;
    this.dismiss();
  },

  _onEdgeUICompleted: function(aEvent) {
    if (this._hasEdgeSwipeStarted) {
      this._hasEdgeSwipeStarted = false;
      return;
    }

    this._clearDelayedTimeout();
    this.toggleNavUI();
  },

  onDownInput: function onDownInput(aEvent) {
    if (!this.isVisible) {
      return;
    }

    // Various ui element containers we do not update context ui for.
    let whitelist = [
      // Clicks on tab bar elements should not close the tab bar. the tabbar
      // handles this.
      Elements.tabs,
      // Don't let a click on an infobar button dismiss the appbar or navbar.
      // Note the notification box should always hover above these other two
      // bars.
      Browser.getNotificationBox()
    ];

    if (whitelist.some(elem => elem.contains(aEvent.target))) {
      return;
    }

    // If a start tab is visible only dismiss the tab bar.
    if (BrowserUI.isStartTabVisible) {
      ContextUI.dismissTabs();
      return;
    }

    // content, dismiss anything visible
    if (aEvent.target.ownerDocument.defaultView.top == getBrowser().contentWindow) {
      this.dismiss();
      return;
    }

    // dismiss tabs and context app bar if visible
    this.dismissTabs();
    this.dismissContextAppbar();
  },

  onMouseMove: function (aEvent) {
    if (this._hidingId) {
      this._clearDelayedTimeout();
      this._delayedHide = true;
    }
  },

  onMouseLeave: function (aEvent) {
    if (this._delayedHide) {
      this._delayedHide = false;
      this._resetDelayedTimeout();
    }
  },

  handleEvent: function handleEvent(aEvent) {
    switch (aEvent.type) {
      case "URLChanged":
        // "aEvent.detail" is a boolean value that indicates whether actual URL
        // has changed ignoring URL fragment changes.
        if (aEvent.target == Browser.selectedBrowser && aEvent.detail) {
          this.displayNavbar();
        }
        break;
      case "MozEdgeUIStarted":
        this._onEdgeUIStarted(aEvent);
        break;
      case "MozEdgeUICanceled":
        this._onEdgeUICanceled(aEvent);
        break;
      case "MozEdgeUICompleted":
        this._onEdgeUICompleted(aEvent);
        break;
      case "keypress":
        if (String.fromCharCode(aEvent.which) == "z" &&
            aEvent.getModifierState("Win"))
          this.toggleNavUI();
        break;
      case "transitionend":
        setTimeout(function () {
          ContentAreaObserver.updateContentArea();
        }, 0);
        break;
      case "KeyboardChanged":
        this.dismissTabs();
        break;
      case "mousedown":
        if (aEvent.button != 0) {
          break;
        }
        this.onDownInput(aEvent);
        break;
      case "mousemove":
        this.onMouseMove(aEvent);
        break;
      case "mouseleave":
        this.onMouseLeave(aEvent);
        break;
      case "touchstart":
        this.onDownInput(aEvent);
        break;
      case "ToolPanelShown":
      case "ToolPanelHidden":
        this.dismiss();
        break;
      case "AlertActive":
      case "AlertClose":
      case "TabSelect":
        ContentAreaObserver.updateContentArea();
        break;
      case "MozFlyoutPanelShowing":
        if (BrowserUI.isStartTabVisible) {
          this.dismissTabs();
          this.dismissContextAppbar();
        } else {
          this.dismiss();
        }
        break;
    }
  },

  _fire: function (name) {
    let event = document.createEvent("Events");
    event.initEvent(name, true, true);
    window.dispatchEvent(event);
  }
};
