'use strict';

Object.defineProperty(exports, '__esModule', { value: true });

var count = require('./count.js');
var each = require('./each.js');
var eachBefore = require('./eachBefore.js');
var eachAfter = require('./eachAfter.js');
var find = require('./find.js');
var sum = require('./sum.js');
var sort = require('./sort.js');
var path = require('./path.js');
var ancestors = require('./ancestors.js');
var descendants = require('./descendants.js');
var leaves = require('./leaves.js');
var links = require('./links.js');
var iterator = require('./iterator.js');

function hierarchy(data, children) {
  if (data instanceof Map) {
    data = [undefined, data];
    if (children === undefined) children = mapChildren;
  } else if (children === undefined) {
    children = objectChildren;
  }

  var root = new Node(data),
      node,
      nodes = [root],
      child,
      childs,
      i,
      n;

  while (node = nodes.pop()) {
    if ((childs = children(node.data)) && (n = (childs = Array.from(childs)).length)) {
      node.children = childs;
      for (i = n - 1; i >= 0; --i) {
        nodes.push(child = childs[i] = new Node(childs[i]));
        child.parent = node;
        child.depth = node.depth + 1;
      }
    }
  }

  return root.eachBefore(computeHeight);
}

function node_copy() {
  return hierarchy(this).eachBefore(copyData);
}

function objectChildren(d) {
  return d.children;
}

function mapChildren(d) {
  return Array.isArray(d) ? d[1] : null;
}

function copyData(node) {
  if (node.data.value !== undefined) node.value = node.data.value;
  node.data = node.data.data;
}

function computeHeight(node) {
  var height = 0;
  do node.height = height;
  while ((node = node.parent) && (node.height < ++height));
}

function Node(data) {
  this.data = data;
  this.depth =
  this.height = 0;
  this.parent = null;
}

Node.prototype = hierarchy.prototype = {
  constructor: Node,
  count: count,
  each: each,
  eachAfter: eachAfter,
  eachBefore: eachBefore,
  find: find,
  sum: sum,
  sort: sort,
  path: path,
  ancestors: ancestors,
  descendants: descendants,
  leaves: leaves,
  links: links,
  copy: node_copy,
  [Symbol.iterator]: iterator
};

exports.Node = Node;
exports.computeHeight = computeHeight;
exports.default = hierarchy;
