minispade.register('shared', function () {

  /**
   * ClusterAndNodeControls contains properties shared by:
   * - RiakControl.ClusterController
   * - RiakControl.NodesController
   * - RiakControl.RingController
   */
  RiakControl.ClusterAndNodeControls = Ember.Mixin.create({

    actions: {
      /**
       * Stage a change.
       *
       * @returns {void}
       */
      stageChange: function(node, action, replacement, success, failure) {
        var self = this,
            ajax = $.ajax({
              type:     'PUT',
              url:      '/admin/cluster',
              dataType: 'json',
              data:     { changes:
                          [{
                            node:        node,
                            action:      action,
                            replacement: replacement
                          }]
                        }
            });

        ajax.then(

          // success...
          function(d) {
            if(success) {
              success();
            }

            self.reload();
          },

          // error...
          function (jqXHR, textStatus, errorThrown) {
            if(failure) {
              failure();
            }

            self.get('displayError').call(self, jqXHR, textStatus, errorThrown);
          }
        );

      },

      /**
       * The action specified on the <a> tag creating the 'x' button in the error message div.
       * By setting the 'errorMessage' property back to an empty string, the message will disappear.
       *
       * @returns {void}
       */
      hideError: function () {
        this.set('errorMessage', '');
      }
    },

    /**
     * If content is loading, return true.
     *
     * @returns {boolean}
     */
    isLoading: false,

    /**
     * Holds the most recent error message.
     */
    errorMessage: '',

    /**
     * Whenever an ajax call returns an error, we display
     * the error for the user.
     *
     * @param {Object} jqXHR       The xhr request object generated by jQuery.
     * @param {String} textStatus  The status of the response.
     * @param {Error}  errorThrown The error object produced by the ajax request.
     *
     * @returns {void}
     */
    displayError: function (jqXHR, textStatus, errorThrown) {
      var parsed, errors;

      if(jqXHR) {
        try {
          parsed = JSON.parse(jqXHR.responseText);
          errors = parsed.errors.join(', ');
        } catch(err) {
          errors = errorThrown;
        }
      } else {
        errors = errorThrown;
      }

      this.set('errorMessage', 'Request failed: ' + errors);
    },

    /**
     * Called by the router, to start polling when this controller/view is navigated to.
     *
     * @returns {void}
     */
    startInterval: function() {
      this._intervalId = setInterval($.proxy(this.reload, this), RiakControl.refreshInterval);
    },

    /**
     * Called by the router, to stop polling when this controller/view is navigated away from.
     *
     * @returns {void}
     */
    cancelInterval: function() {
      if(this._intervalId) {
        clearInterval(this._intervalId);
      }
    }
  });

  /**
   * RiakControl.NodeProperties contains properties shared by:
   * - RiakControl.CurrentClusterItemView
   * - RiakControl.StagedClusterItemView
   * - RiakControl.CurrentNodesItemView
   */
  RiakControl.NodeProperties = Ember.Mixin.create({

    /**
     * In the current cluster area, the host node has extra content in its
     * actions box so it needs to be a little taller than the others.
     *
     * @returns {String}
     */
    hostNodeClasses: function () {
      return this.get('me') ? 'node taller' : 'node';
    }.property('me'),

    /**
     * Color the lights appropriately based on the node status.
     *
     * @returns {string}
     */
    indicatorLights: function() {
      var status = this.get('status');
      var reachable = this.get('reachable');
      var color;

      if(reachable === false && status !== "down") {
        color = "red";
      } else if (status === "down") {
        color = "blue";
      } else if(status === 'leaving' || status === 'joining') {
        color = "orange";
      } else if (status === 'valid') {
        color = "green";
      } else {
        color = "grey";
      }

      return "gui-light status-light inline-block " + color;
    }.property('reachable', 'status'),

    /**
     * Color the arrows in the partitions column appropriately based
     * on how ring_pct and pending_pct compare.
     *
     * @returns {String}
     */
    coloredArrows: function() {
      var current = this.get('ring_pct'),
          pending = this.get('pending_pct'),
          common  = 'left pct-arrows ';

      if (pending > current) {
        return common + 'pct-gaining';
      } else if (pending < current) {
        return common + 'pct-losing';
      } else {
        return common + 'pct-static';
      }
    }.property('ring_pct', 'pending_pct'),

    /**
     * Normalizer.
     *
     * @returns {number}
     */
    memDivider: function() {
      return this.get('mem_total') / 100;
    }.property('mem_total'),

    /**
     * Compute memory ceiling.
     *
     * @returns {number}
     */
    memErlangCeil: function () {
      return Math.ceil(this.get('mem_erlang') / this.get('memDivider'));
    }.property('mem_erlang', 'memDivider'),

    /**
     * Compute free memory from total and used.
     *
     * @returns {number}
     */
    memNonErlang: function () {
      return Math.round(
          (this.get('mem_used') / this.get('memDivider')) - this.get('memErlangCeil'));
    }.property('mem_used', 'memDivider', 'memErlangCeil'),

    /**
     * Compute free memory from total and used.
     *
     * @returns {number}
     */
    memFree: function () {
      return this.get('mem_total') - this.get('mem_used');
    }.property('mem_total', 'mem_used'),

    /**
     * Format free memory to be a readbale version.
     *
     * @returns {number}
     */
    memFreeReadable: function () {
      return Math.round(this.get('memFree') / this.get('memDivider'));
    }.property('memFree', 'memDivider'),

    /**
     * Format used memory to be a readbale version.
     *
     * @returns {number}
     */
    memUsedReadable: function () {
      return Math.round((this.get('mem_total') - this.get('memFree')) /
          this.get('memDivider'));
    }.property('mem_total', 'memFree', 'memDivider'),

    /**
     * Return CSS style for rendering memory used by Erlang.
     *
     * @returns {number}
     */
    memErlangStyle: function () {
      return 'width: ' + this.get('memErlangCeil') + '%';
    }.property('memErlangCeil'),

    /**
     * Return CSS style for rendering occupied non-erlang memory.
     *
     * @returns {string}
     */
    memNonErlangStyle: function () {
      return 'width: ' + this.get('memNonErlang') + '%';
    }.property('memNonErlang'),

    /**
     * Return CSS style for rendering free memory.
     *
     * @returns {string}
     */
    memFreeStyle: function () {
      return 'width: ' + this.get('memFreeReadable') + '%';
    }.property('memFreeReadable'),

    /**
     * Formatted ring percentage.
     *
     * @returns {string}
     */
    ringPctReadable: function () {
      return Math.round(this.get('ring_pct') * 100);
    }.property('ring_pct')
  });

});
