var zrUtil = require("@okfe/zrender/lib/core/util");

/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements.  See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership.  The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License.  You may obtain a copy of the License at
*
*   http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied.  See the License for the
* specific language governing permissions and limitations
* under the License.
*/

/**
 * nest helper used to group by the array.
 * can specified the keys and sort the keys.
 */
function nest() {
  var keysFunction = [];
  var sortKeysFunction = [];
  /**
   * map an Array into the mapObject.
   * @param {Array} array
   * @param {number} depth
   */

  function map(array, depth) {
    if (depth >= keysFunction.length) {
      return array;
    }

    var i = -1;
    var n = array.length;
    var keyFunction = keysFunction[depth++];
    var mapObject = {};
    var valuesByKey = {};

    while (++i < n) {
      var keyValue = keyFunction(array[i]);
      var values = valuesByKey[keyValue];

      if (values) {
        values.push(array[i]);
      } else {
        valuesByKey[keyValue] = [array[i]];
      }
    }

    zrUtil.each(valuesByKey, function (value, key) {
      mapObject[key] = map(value, depth);
    });
    return mapObject;
  }
  /**
   * transform the Map Object to multidimensional Array
   * @param {Object} map
   * @param {number} depth
   */


  function entriesMap(mapObject, depth) {
    if (depth >= keysFunction.length) {
      return mapObject;
    }

    var array = [];
    var sortKeyFunction = sortKeysFunction[depth++];
    zrUtil.each(mapObject, function (value, key) {
      array.push({
        key: key,
        values: entriesMap(value, depth)
      });
    });

    if (sortKeyFunction) {
      return array.sort(function (a, b) {
        return sortKeyFunction(a.key, b.key);
      });
    } else {
      return array;
    }
  }

  return {
    /**
     * specified the key to groupby the arrays.
     * users can specified one more keys.
     * @param {Function} d
     */
    key: function (d) {
      keysFunction.push(d);
      return this;
    },

    /**
     * specified the comparator to sort the keys
     * @param {Function} order
     */
    sortKeys: function (order) {
      sortKeysFunction[keysFunction.length - 1] = order;
      return this;
    },

    /**
     * the array to be grouped by.
     * @param {Array} array
     */
    entries: function (array) {
      return entriesMap(map(array, 0), 0);
    }
  };
}

module.exports = nest;