// 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/.
//
// Based on original code by alice0775 https://github.com/alice0775


"use strict";
var browserMenuDragging = {
  //-- config --
  STAY_OPEN_ONDRAGEXIT: false,
  DEBUG: false,
  //-- config --

  menupopup: ['bookmarksMenuPopup',
              'PlacesToolbar',
              'BMB_bookmarksPopup',
              'appmenu_bookmarksPopup',
              'BookmarksMenuToolButtonPopup',
              'UnsortedBookmarksFolderToolButtonPopup',
              'bookmarksMenuPopup-context'],
  timer:[],
  count:[],


  init: function() {
    window.removeEventListener('load', this, false);
    window.addEventListener('unload', this, false);
    this.addPrefListener(this.PrefListener);

    window.addEventListener('aftercustomization', this, false);

    this.initPref();
    this.delayedStartup();
  },

  uninit: function() {
    window.removeEventListener('unload', this, false);
    this.removePrefListener(this.PrefListener);

    window.removeEventListener('aftercustomization', this, false);

    for (var i = 0; i < this.menupopup.length; i++){
      var menupopup = document.getElementById(this.menupopup[i]);
      if (menupopup){
        menupopup.removeEventListener('popupshowing', this, false);
        menupopup.removeEventListener('popuphiding', this, false);
      }
    }

  },

  initPref: function() {
    this.STAY_OPEN_ONDRAGEXIT =
          Services.prefs.getBoolPref('browser.menu.dragging.stayOpen', false);
    this.DEBUG =
          Services.prefs.getBoolPref('browser.menu.dragging.debug', false);
  },

  //delayed startup
  delayedStartup: function() {
    //wait until construction of bookmarksBarContent is completed.
    for (var i = 0; i < this.menupopup.length; i++) {
      this.count[i] = 0;
      this.timer[i] = setInterval(function(self, i) {
        if(++self.count[i] > 50 || document.getElementById(self.menupopup[i])) {
          clearInterval(self.timer[i]);
          var menupopup = document.getElementById(self.menupopup[i]);
          if (menupopup) {
            menupopup.addEventListener('popupshowing', self, false);
            menupopup.addEventListener('popuphiding', self, false);
          }
        }
      }, 250, this, i);
    }
  },

  handleEvent: function(event) {
    switch (event.type) {
      case 'popupshowing':
        this.popupshowing(event);
        break;
      case 'popuphiding':
        this.popuphiding(event);
        break;
      case 'aftercustomization':
        setTimeout(function(self){self.delayedStartup(self);}, 0, this);
        break;
      case 'load':
        this.init();
        break;
      case 'unload':
        this.uninit();
        break;
    }
  },

  popuphiding: function(event) {
    var menupopup = event.originalTarget;
    menupopup.parentNode.parentNode.openNode = null;

    if (menupopup.parentNode.localName == 'toolbarbutton') {
      // Fix for Bug 225434 -  dragging bookmark from personal toolbar and releasing
      // (on same bookmark or elsewhere) or clicking on bookmark menu then cancelling
      // leaves button depressed/sunken when hovered
      menupopup.parentNode.parentNode._openedMenuButton = null;

      if (!PlacesControllerDragHelper.getSession()) {
        // Clear the dragover attribute if present, if we are dragging into a
        // folder in the hierachy of current opened popup we don't clear
        // this attribute on clearOverFolder.  See Notify for closeTimer.
        if (menupopup.parentNode.hasAttribute('dragover')) {
          menupopup.parentNode.removeAttribute('dragover');
        }
      }
    }
  },

  popupshowing: function(event) {
    var menupopup = event.originalTarget;
    browserMenuDragging.debug("popupshowing ===============\n" + menupopup.parentNode.getAttribute('label'));

    var parentPopup = menupopup.parentNode.parentNode;

    if (!!parentPopup.openNode) {
      try {
        parentPopup.openNode.hidePopup();
      } catch(e) {}
    }
    parentPopup.openNode = menupopup;

    menupopup.onDragStart = function (event) {
      // Bug 555474 -  While bookmark is dragged, the tooltip should not appear
      browserMenuDragging.hideTooltip();
    }

    menupopup.onDragOver = function (event) {
      // Bug 555474 -  While bookmark is dragged, the tooltip should not appear
      browserMenuDragging.hideTooltip();

      var target = event.originalTarget;
      while (target) {
        if (/menupopup/.test(target.localName)) {
          break;
        }
        target = target.parentNode;
      }
      
      if (this != target) {
        return;
      }
      
      event.stopPropagation();
      browserMenuDragging.debug("onDragOver " + "\n" + this.parentNode.getAttribute('label'));

      PlacesControllerDragHelper.currentDropTarget = event.target;
      let dt = event.dataTransfer;

      let dropPoint = this._getDropPoint(event);

      if (!dropPoint || !dropPoint.ip ||
          !PlacesControllerDragHelper.canDrop(dropPoint.ip, dt)) {
        this._indicatorBar.hidden = true;
        event.stopPropagation();
        return;
      }

      // Mark this popup as being dragged over.
      this.setAttribute('dragover', 'true');

      if (dropPoint.folderElt) {
        // We are dragging over a folder.
        // _overFolder should take the care of opening it on a timer.
        if (this._overFolder.elt &&
            this._overFolder.elt != dropPoint.folderElt) {
        }
        if (!this._overFolder.elt) {
          this._overFolder.elt = dropPoint.folderElt;
          // Create the timer to open this folder.
          this._overFolder.openTimer = this._overFolder
                                           .setTimer(this._overFolder.hoverTime);
        }
      }
      else {
        // We are not dragging over a folder.
      }

      // Autoscroll the popup strip if we drag over the scroll buttons.
      let anonid = event.originalTarget.getAttribute('anonid');
      let scrollDir = anonid == 'scrollbutton-up' ? -1 :
                      anonid == 'scrollbutton-down' ? 1 : 0;
      if (scrollDir != 0) {
        this._scrollBox.scrollByIndex(scrollDir, false);
      }

      // Check if we should hide the drop indicator for this target.
      if (dropPoint.folderElt || this._hideDropIndicator(event)) {
        this._indicatorBar.hidden = true;
        event.preventDefault();
        event.stopPropagation();
        return;
      }

      // We should display the drop indicator relative to the arrowscrollbox.
      let sbo = this._scrollBox.scrollBoxObject;
      let newMarginTop = 0;
      if (scrollDir == 0) {
        let elt = this.firstChild;
        while (elt && event.screenY > elt.boxObject.screenY +
                                      elt.boxObject.height / 2) {
          elt = elt.nextSibling;
        }
        newMarginTop = elt ? elt.boxObject.screenY - sbo.screenY : sbo.height;
      } else if (scrollDir == 1) {
        newMarginTop = sbo.height;
      }

      // Set the new marginTop based on arrowscrollbox.
      newMarginTop += sbo.y - this._scrollBox.boxObject.y;
      this._indicatorBar.firstChild.style.marginTop = newMarginTop + 'px';
      this._indicatorBar.hidden = false;

      event.preventDefault();
      event.stopPropagation();
    }

    menupopup.onDragExit = function (event) {
      var target = event.originalTarget;
      while (target) {
        if (/menupopup/.test(target.localName)) {
          break;
        }
        target = target.parentNode;
      }
      
      if (this != target) {
        return;
      }
      
      event.stopPropagation();
      browserMenuDragging.debug("onDragExit " + browserMenuDragging.STAY_OPEN_ONDRAGEXIT);

      PlacesControllerDragHelper.currentDropTarget = null;
      this.removeAttribute('dragover');

      // If we have not moved to a valid new target clear the drop indicator
      // this happens when moving out of the popup.
      target = event.relatedTarget;
      if (!target) {
        this._indicatorBar.hidden = true;
      }

      // Close any folder being hovered over
      if (this._overFolder.elt) {
        this._overFolder.closeTimer = this._overFolder.setTimer(this._overFolder.hoverTime);
      }

      // The auto-opened attribute is set when this folder was automatically
      // opened after the user dragged over it.  If this attribute is set,
      // auto-close the folder on drag exit.
      // We should also try to close this popup if the drag has started
      // from here, the timer will check if we are dragging over a child.
      if (this.hasAttribute('autoopened') ||
          !browserMenuDragging.STAY_OPEN_ONDRAGEXIT &&
          this.hasAttribute('dragstart')) {
        this._overFolder.closeMenuTimer = this._overFolder
                                              .setTimer(this._overFolder.hoverTime);
      }

      event.stopPropagation();
    }

    menupopup.addEventListener('dragstart', menupopup.onDragStart, true);
    menupopup.addEventListener('dragover', menupopup.onDragOver, true);
    menupopup.addEventListener('dragleave', menupopup.onDragExit, true);
  },

  hideTooltip: function() {
    ['bhTooltip', 'btTooltip2'].forEach(function(id) {
      var tooltip = document.getElementById(id);
      if (tooltip) {
        tooltip.hidePopup();
      }
    });
  },

  get getVer() {
    const Cc = Components.classes;
    const Ci = Components.interfaces;
    var info = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo);
    var ver = parseInt(info.version.substr(0,3) * 10,10) / 10;
    return ver;
  },

  debug: function(aMsg) {
    if (!browserMenuDragging.DEBUG) {
      return;
    }
    Components.classes["@mozilla.org/consoleservice;1"]
              .getService(Components.interfaces.nsIConsoleService)
              .logStringMessage(aMsg);
  },

  setPref: function(aPrefString, aPrefType, aValue) {
    var xpPref = Components.classes["@mozilla.org/preferences-service;1"]
                           .getService(Components.interfaces.nsIPrefService);
    try{
      switch (aPrefType){
        case 'complex':
          return xpPref.setComplexValue(aPrefString, Components.interfaces.nsILocalFile, aValue); break;
        case 'str':
          return xpPref.setCharPref(aPrefString, aValue); break;
        case 'int':
          aValue = parseInt(aValue);
          return xpPref.setIntPref(aPrefString, aValue);  break;
        case 'bool':
        default:
          return xpPref.setBoolPref(aPrefString, aValue); break;
      }
    } catch(e) {}
    return null;
  },

  addPrefListener: function(aObserver) {
      try {
          var pbi = Components.classes["@mozilla.org/preferences;1"].
                    getService(Components.interfaces.nsIPrefBranch2);
          pbi.addObserver(aObserver.domain, aObserver, false);
      } catch(e) {}
  },

  removePrefListener: function(aObserver) {
      try {
          var pbi = Components.classes["@mozilla.org/preferences;1"].
                    getService(Components.interfaces.nsIPrefBranch2);
          pbi.removeObserver(aObserver.domain, aObserver);
      } catch(e) {}
  },

  PrefListener:{
      domain  : 'browser.menu.dragging.stayOpen',

      observe : function(aSubject, aTopic, aPrefstring) {
          if (aTopic == 'nsPref:changed') {
              browserMenuDragging.initPref();
          }
      }
  }
}

window.addEventListener('load', browserMenuDragging, false);