/* *
 *
 *  (c) 2016-2019 Highsoft AS
 *
 *  Authors: Jon Arild Nygard
 *
 *  License: www.highcharts.com/license
 *
 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
 *
 * */
/* eslint no-console: 0 */
'use strict';
import U from '../parts/Utilities.js';
var extend = U.extend, isNumber = U.isNumber, pick = U.pick;
var isFunction = function (x) {
    return typeof x === 'function';
};
/**
 * Creates an object map from parent id to childrens index.
 *
 * @private
 * @function Highcharts.Tree#getListOfParents
 *
 * @param {Array<*>} data
 *        List of points set in options. `Array.parent` is parent id of point.
 *
 * @param {Array<string>} ids
 *        List of all point ids.
 *
 * @return {Highcharts.Dictionary<Array<*>>}
 *         Map from parent id to children index in data
 */
var getListOfParents = function (data, ids) {
    var listOfParents = data.reduce(function (prev, curr) {
        var parent = pick(curr.parent, '');
        if (typeof prev[parent] === 'undefined') {
            prev[parent] = [];
        }
        prev[parent].push(curr);
        return prev;
    }, {}), parents = Object.keys(listOfParents);
    // If parent does not exist, hoist parent to root of tree.
    parents.forEach(function (parent, list) {
        var children = listOfParents[parent];
        if ((parent !== '') && (ids.indexOf(parent) === -1)) {
            children.forEach(function (child) {
                list[''].push(child);
            });
            delete list[parent];
        }
    });
    return listOfParents;
};
var getNode = function (id, parent, level, data, mapOfIdToChildren, options) {
    var descendants = 0, height = 0, after = options && options.after, before = options && options.before, node = {
        data: data,
        depth: level - 1,
        id: id,
        level: level,
        parent: parent
    }, start, end, children;
    // Allow custom logic before the children has been created.
    if (isFunction(before)) {
        before(node, options);
    }
    // Call getNode recursively on the children. Calulate the height of the
    // node, and the number of descendants.
    children = ((mapOfIdToChildren[id] || [])).map(function (child) {
        var node = getNode(child.id, id, (level + 1), child, mapOfIdToChildren, options), childStart = child.start, childEnd = (child.milestone === true ?
            childStart :
            child.end);
        // Start should be the lowest child.start.
        start = ((!isNumber(start) || childStart < start) ?
            childStart :
            start);
        // End should be the largest child.end.
        // If child is milestone, then use start as end.
        end = ((!isNumber(end) || childEnd > end) ?
            childEnd :
            end);
        descendants = descendants + 1 + node.descendants;
        height = Math.max(node.height + 1, height);
        return node;
    });
    // Calculate start and end for point if it is not already explicitly set.
    if (data) {
        data.start = pick(data.start, start);
        data.end = pick(data.end, end);
    }
    extend(node, {
        children: children,
        descendants: descendants,
        height: height
    });
    // Allow custom logic after the children has been created.
    if (isFunction(after)) {
        after(node, options);
    }
    return node;
};
var getTree = function (data, options) {
    var ids = data.map(function (d) {
        return d.id;
    }), mapOfIdToChildren = getListOfParents(data, ids);
    return getNode('', null, 1, null, mapOfIdToChildren, options);
};
var Tree = {
    getListOfParents: getListOfParents,
    getNode: getNode,
    getTree: getTree
};
export default Tree;
