/* *
 *
 *  !!!!!!! SOURCE GETS TRANSPILED BY TYPESCRIPT. EDIT TS FILE ONLY. !!!!!!!
 *
 * */
import H from '../parts/Globals.js';
import Point from '../parts/Point.js';
import U from '../parts/Utilities.js';
var defined = U.defined, extend = U.extend, find = U.find, pick = U.pick;
H.NodesMixin = {
    /* eslint-disable valid-jsdoc */
    /**
     * Create a single node that holds information on incoming and outgoing
     * links.
     * @private
     */
    createNode: function (id) {
        /**
         * @private
         */
        function findById(nodes, id) {
            return find(nodes, function (node) {
                return node.id === id;
            });
        }
        var node = findById(this.nodes, id), PointClass = this.pointClass, options;
        if (!node) {
            options = this.options.nodes && findById(this.options.nodes, id);
            node = (new PointClass()).init(this, extend({
                className: 'highcharts-node',
                isNode: true,
                id: id,
                y: 1 // Pass isNull test
            }, options));
            node.linksTo = [];
            node.linksFrom = [];
            node.formatPrefix = 'node';
            node.name = node.name || node.options.id; // for use in formats
            // Mass is used in networkgraph:
            node.mass = pick(
            // Node:
            node.options.mass, node.options.marker && node.options.marker.radius, 
            // Series:
            this.options.marker && this.options.marker.radius, 
            // Default:
            4);
            /**
             * Return the largest sum of either the incoming or outgoing links.
             * @private
             */
            node.getSum = function () {
                var sumTo = 0, sumFrom = 0;
                node.linksTo.forEach(function (link) {
                    sumTo += link.weight;
                });
                node.linksFrom.forEach(function (link) {
                    sumFrom += link.weight;
                });
                return Math.max(sumTo, sumFrom);
            };
            /**
             * Get the offset in weight values of a point/link.
             * @private
             */
            node.offset = function (point, coll) {
                var offset = 0;
                for (var i = 0; i < node[coll].length; i++) {
                    if (node[coll][i] === point) {
                        return offset;
                    }
                    offset += node[coll][i].weight;
                }
            };
            // Return true if the node has a shape, otherwise all links are
            // outgoing.
            node.hasShape = function () {
                var outgoing = 0;
                node.linksTo.forEach(function (link) {
                    if (link.outgoing) {
                        outgoing++;
                    }
                });
                return (!node.linksTo.length ||
                    outgoing !== node.linksTo.length);
            };
            this.nodes.push(node);
        }
        return node;
    },
    /**
     * Extend generatePoints by adding the nodes, which are Point objects
     * but pushed to the this.nodes array.
     */
    generatePoints: function () {
        var chart = this.chart, nodeLookup = {};
        H.Series.prototype.generatePoints.call(this);
        if (!this.nodes) {
            this.nodes = []; // List of Point-like node items
        }
        this.colorCounter = 0;
        // Reset links from previous run
        this.nodes.forEach(function (node) {
            node.linksFrom.length = 0;
            node.linksTo.length = 0;
            node.level = node.options.level;
        });
        // Create the node list and set up links
        this.points.forEach(function (point) {
            if (defined(point.from)) {
                if (!nodeLookup[point.from]) {
                    nodeLookup[point.from] = this.createNode(point.from);
                }
                nodeLookup[point.from].linksFrom.push(point);
                point.fromNode = nodeLookup[point.from];
                // Point color defaults to the fromNode's color
                if (chart.styledMode) {
                    point.colorIndex = pick(point.options.colorIndex, nodeLookup[point.from].colorIndex);
                }
                else {
                    point.color =
                        point.options.color || nodeLookup[point.from].color;
                }
            }
            if (defined(point.to)) {
                if (!nodeLookup[point.to]) {
                    nodeLookup[point.to] = this.createNode(point.to);
                }
                nodeLookup[point.to].linksTo.push(point);
                point.toNode = nodeLookup[point.to];
            }
            point.name = point.name || point.id; // for use in formats
        }, this);
        // Store lookup table for later use
        this.nodeLookup = nodeLookup;
    },
    // Destroy all nodes on setting new data
    setData: function () {
        if (this.nodes) {
            this.nodes.forEach(function (node) {
                node.destroy();
            });
            this.nodes.length = 0;
        }
        H.Series.prototype.setData.apply(this, arguments);
    },
    // Destroy alll nodes and links
    destroy: function () {
        // Nodes must also be destroyed (#8682, #9300)
        this.data = []
            .concat(this.points || [], this.nodes);
        return H.Series.prototype.destroy.apply(this, arguments);
    },
    /**
     * When hovering node, highlight all connected links. When hovering a link,
     * highlight all connected nodes.
     */
    setNodeState: function (state) {
        var args = arguments, others = this.isNode ? this.linksTo.concat(this.linksFrom) :
            [this.fromNode, this.toNode];
        if (state !== 'select') {
            others.forEach(function (linkOrNode) {
                if (linkOrNode && linkOrNode.series) {
                    Point.prototype.setState.apply(linkOrNode, args);
                    if (!linkOrNode.isNode) {
                        if (linkOrNode.fromNode.graphic) {
                            Point.prototype.setState.apply(linkOrNode.fromNode, args);
                        }
                        if (linkOrNode.toNode && linkOrNode.toNode.graphic) {
                            Point.prototype.setState.apply(linkOrNode.toNode, args);
                        }
                    }
                }
            });
        }
        Point.prototype.setState.apply(this, args);
    }
    /* eslint-enable valid-jsdoc */
};
