/**
 * Abstract class for D3 components
 * with the [Tree layout](https://github.com/d3/d3-hierarchy/#tree).
 */
Ext.define('Ext.d3.hierarchy.tree.Tree', {
    extend: 'Ext.d3.hierarchy.Hierarchy',
 
    config: {
        treeCls: 'tree',
 
        nodeTransform: null,
 
        /**
         * @private
         * Specifies a fixed distance between the parent and child nodes.
         * By default, the distance is `tree depth / (number of tree levels - 1)`.
         * @cfg {Number} [depth=0]
         */
        depth: 0,
 
        /**
         * The radius of the circle that represents a node.
         * @cfg {Number} [nodeRadius=5]
         */
        nodeRadius: 5,
 
        nodeTransition: true,
 
        /**
         * [Fixed size](https://github.com/mbostock/d3/wiki/Tree-Layout#nodeSize),
         * of each node as a two-element array of numbers representing x and y.
         * Note that if the `nodeSize` config is not set,
         * the [layout size](https://github.com/d3/d3-hierarchy/blob/master/README.md#tree_size)
         * will be set to the size of the component's {@link #getScene scene}, but the resulting
         * tree node layout is not guaranteed to stretch to fill the whole scene.
         * @cfg {Number[]} nodeSize
         */
        nodeSize: null,
 
        noSizeLayout: false,
 
        renderLinks: true,
 
        transitions: {
            select: {
                targetScale: 1.5
            }
        }
    },
 
    updateTreeCls: function(treeCls, oldTreeCls) {
        var baseCls = this.baseCls,
            el = this.element;
 
        if (treeCls && Ext.isString(treeCls)) {
            el.addCls(treeCls, baseCls);
 
            if (oldTreeCls) {
                el.removeCls(oldTreeCls, baseCls);
            }
        }
    },
 
    applyLayout: function() {
        return d3.tree();
    },
 
    updateNodeSize: function(nodeSize) {
        var layout = this.getLayout();
 
        layout.nodeSize(nodeSize);
    },
 
    updateColorAxis: function(colorAxis) {
        var me = this;
 
        if (!me.isConfiguring) {
            me.getRenderedNodes()
                .select('circle')
                .style('fill', function(node) {
                    return colorAxis.getColor(node);
                });
        }
    },
 
    hideRoot: function() {
        // Overriding the parent method here.
        // With trees, we have to have the root hidden via
        // `display: none` instead of `visibility: hidden`,
        // so that the hidden nodes are not visible to
        // `svgElement.getBBox`. In all other cases, hiding
        // via `visibility: hidden` is the preferred way,
        // because we can still measure hidden things.
        var me = this,
            rootVisible = me.getRootVisible();
 
        me.nodesGroup
            .select('.' + me.defaultCls.root)
            .classed(me.defaultCls.hidden, !rootVisible);
    },
 
    onNodeSelect: function(node, el) {
        var me = this,
            transitionCfg = me.getTransitions().select,
            to = transitionCfg.targetScale,
            from = transitionCfg.sourceScale,
            transition = me.createTransition('select'),
            circle = el.select('circle');
 
        me.callParent(arguments);
 
        // Remove the fill given by the `colorAxis`, so that
        // the CSS style can be used to specify the color
        // of the selection.
        circle.style('fill', null);
 
        if (transition.duration()) {
            circle
                .transition(transition)
                .attr('transform', 'scale(' + to + ',' + to + ')')
                .transition(transition)
                .attr('transform', 'scale(' + from + ',' + from + ')');
        }
    },
 
    onNodeDeselect: function(node, el) {
        var me = this,
            colorAxis = me.getColorAxis();
 
        me.callParent(arguments);
 
        // Restore the original color.
        // (see 'onNodeSelect' comments).
        el.select('circle')
            .style('fill', function(node) {
                return colorAxis.getColor(node);
            });
    }
 
});