"use strict";

Object.defineProperty(exports, "__esModule", {
  value: true
});
exports.default = grade;

var _gradation = require("./gradation");

function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }

/**
 * Takes seconds `elapsed` and measures them against
 * `gradation` to return the suitable `gradation` step.
 *
 * @param {number} elapsed - Time interval (in seconds). Is < 0 for past dates and > 0 for future dates.
 *
 * @param {string[]} units - A list of allowed time units
 *                           (e.g. ['second', 'minute', 'hour', …])
 *
 * @param {Object} [gradation] - Time scale gradation steps.
 *
 *                               E.g.:
 *                               [
 *                                 { unit: 'second', factor: 1 },
 *                                 { unit: 'minute', factor: 60, threshold: 60 },
 *                                 { format(), threshold: 24 * 60 * 60 },
 *                                 …
 *                               ]
 *
 * @return {?Object} `gradation` step.
 */
function grade(elapsed, now, units) {
  var gradation = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _gradation.convenient;
  // Leave only allowed time measurement units.
  // E.g. omit "quarter" unit.
  gradation = getAllowedSteps(gradation, units); // If no steps of gradation fit the conditions
  // then return nothing.

  if (gradation.length === 0) {
    return;
  } // Find the most appropriate gradation step


  var i = findGradationStep(elapsed, now, gradation);
  var step = gradation[i]; // If time elapsed is too small and even
  // the first gradation step doesn't suit it
  // then return nothing.

  if (i === -1) {
    return;
  } // Apply granularity to the time amount
  // (and fall back to the previous step
  //  if the first level of granularity
  //  isn't met by this amount)


  if (step.granularity) {
    // Recalculate the elapsed time amount based on granularity
    var amount = Math.round(Math.abs(elapsed) / step.factor / step.granularity) * step.granularity; // If the granularity for this step
    // is too high, then fallback
    // to the previous step of gradation.
    // (if there is any previous step of gradation)

    if (amount === 0 && i > 0) {
      return gradation[i - 1];
    }
  }

  return step;
}
/**
 * Gets threshold for moving from `fromStep` to `next_step`.
 * @param  {Object} fromStep - From step.
 * @param  {Object} next_step - To step.
 * @param  {number} now - The current timestamp.
 * @param  {boolean} future - Is `true` for future dates ("in 5 minutes").
 * @return {number}
 * @throws Will throw if no threshold is found.
 */


function getThreshold(fromStep, toStep, now, future) {
  var threshold; // Allows custom thresholds when moving
  // from a specific step to a specific step.

  if (fromStep && (fromStep.id || fromStep.unit)) {
    threshold = toStep["threshold_for_".concat(fromStep.id || fromStep.unit)];
  } // If no custom threshold is set for this transition
  // then use the usual threshold for the next step.


  if (threshold === undefined) {
    threshold = toStep.threshold;
  } // Convert threshold to a number.


  if (typeof threshold === 'function') {
    threshold = threshold(now, future);
  } // Throw if no threshold is found.


  if (fromStep && typeof threshold !== 'number') {
    // Babel transforms `typeof` into some "branches"
    // so istanbul will show this as "branch not covered".

    /* istanbul ignore next */
    var type = _typeof(threshold);

    throw new Error("Each step of a gradation must have a threshold defined except for the first one. Got \"".concat(threshold, "\", ").concat(type, ". Step: ").concat(JSON.stringify(toStep)));
  }

  return threshold;
}
/**
 * @param  {number} elapsed - Time elapsed (in seconds).
 * @param  {number} now - Current timestamp.
 * @param  {Object} gradation - Gradation.
 * @param  {number} i - Gradation step currently being tested.
 * @return {number} Gradation step index.
 */


function findGradationStep(elapsed, now, gradation) {
  var i = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0;

  // If the threshold for moving from previous step
  // to this step is too high then return the previous step.
  if (Math.abs(elapsed) < getThreshold(gradation[i - 1], gradation[i], now, elapsed < 0)) {
    return i - 1;
  } // If it's the last step of gradation then return it.


  if (i === gradation.length - 1) {
    return i;
  } // Move to the next step.


  return findGradationStep(elapsed, now, gradation, i + 1);
}
/**
 * Leaves only allowed gradation steps.
 * @param  {Object[]} gradation
 * @param  {string[]} units - Allowed time units.
 * @return {Object[]}
 */


function getAllowedSteps(gradation, units) {
  return gradation.filter(function (_ref) {
    var unit = _ref.unit;

    // If this step has a `unit` defined
    // then this `unit` must be in the list of `units` allowed.
    if (unit) {
      return units.indexOf(unit) >= 0;
    } // A gradation step is not required to specify a `unit`.
    // E.g. for Twitter gradation it specifies `format()` instead.


    return true;
  });
}
//# sourceMappingURL=grade.js.map