/**
 * @requires javelin-install
 *           javelin-dom
 *           javelin-vector
 *           javelin-request
 *           javelin-uri
 * @provides phabricator-hovercard
 * @javelin
 */

JX.install('Hovercard', {

  statics : {
    _node : null,
    _activeRoot : null,
    _visiblePHID : null,
    _alignment: null,

    fetchUrl : '/search/hovercard/',

    /**
     * Hovercard storage. {"PHID-XXXX-YYYY":"<...>", ...}
     */
    _cards : {},

    getAnchor : function() {
      return this._activeRoot;
    },

    getCard : function() {
      var self = JX.Hovercard;
      return self._node;
    },

    getAlignment: function() {
      var self = JX.Hovercard;
      return self._alignment;
    },

    show : function(root, phid) {
      var self = JX.Hovercard;

      if (root === this._activeRoot) {
        return;
      }

      self.hide();

      self._visiblePHID = phid;
      self._activeRoot = root;

      if (!(phid in self._cards)) {
        self._load([phid]);
      } else {
        self._drawCard(phid);
      }
    },

    _drawCard : function(phid) {
      var self = JX.Hovercard;
      // card is loading...
      if (self._cards[phid] === true) {
        return;
      }
      // Not the current requested card
      if (phid != self._visiblePHID) {
        return;
      }
      // Not loaded
      if (!(phid in self._cards)) {
        return;
      }

      var root = self._activeRoot;
      var node = JX.$N('div',
        { className: 'jx-hovercard-container' },
        JX.$H(self._cards[phid]));

      self._node = node;

      // Append the card to the document, but offscreen, so we can measure it.
      node.style.left = '-10000px';
      document.body.appendChild(node);

      // Retrieve size from child (wrapper), since node gives wrong dimensions?
      var child = node.firstChild;
      var p = JX.$V(root);
      var d = JX.Vector.getDim(root);
      var n = JX.Vector.getDim(child);
      var v = JX.Vector.getViewport();
      var s = JX.Vector.getScroll();

      // Move the tip so it's nicely aligned.
      var margin = 20;


      // Try to align the card directly above the link, with left borders
      // touching.
      var x = p.x;

      // If this would push us off the right side of the viewport, push things
      // back to the left.
      if ((x + n.x + margin) > (s.x + v.x)) {
        x = (s.x + v.x) - n.x - margin;
      }

      // Try to put the card above the link.
      var y = p.y - n.y - margin;
      self._alignment = 'north';

      // If the card is near the top of the window, show it beneath the
      // link we're hovering over instead.
      if ((y - margin) < s.y) {
        y = p.y + d.y + margin;
        self._alignment = 'south';
      }

      node.style.left = x + 'px';
      node.style.top  = y + 'px';
    },

    hide : function() {
      var self = JX.Hovercard;
      self._visiblePHID = null;
      self._activeRoot = null;
      if (self._node) {
        JX.DOM.remove(self._node);
        self._node = null;
      }
    },

    /**
     * Pass it an array of phids to load them into storage
     *
     * @param list phids
     */
    _load : function(phids) {
      var self = JX.Hovercard;
      var uri = JX.$U(self.fetchUrl);

      var send = false;
      for (var ii = 0; ii < phids.length; ii++) {
        var phid = phids[ii];
        if (phid in self._cards) {
          continue;
        }
        self._cards[phid] = true; // means "loading"
        uri.setQueryParam('phids['+ii+']', phids[ii]);
        send = true;
      }

      if (!send) {
        // already loaded / loading everything!
        return;
      }

      new JX.Request(uri, function(r) {
        for (var phid in r.cards) {
          self._cards[phid] = r.cards[phid];

          // Don't draw if the user is faster than the browser
          // Only draw if the user is still requesting the original card
          if (self.getCard() && phid != self._visiblePHID) {
            continue;
          }

          self._drawCard(phid);
        }
      }).send();
    }
  }
});
