/* ------------------------------------------------------------------------------
 *
 *  # D3.js - hierarchical bar chart
 *
 *  Demo d3.js hierarchical bar chart setup with .json data
 *
 * ---------------------------------------------------------------------------- */


// Setup module
// ------------------------------

var D3BarHierarchy = function() {


    //
    // Setup module components
    //

    // Chart
    var _barHierarchy = function() {
        if (typeof d3 == 'undefined') {
            console.warn('Warning - d3.min.js is not loaded.');
            return;
        }

        // Main variables
        var element = document.getElementById('d3-hierarchical-bars'),
            height = 400;


        // Initialize chart only if element exsists in the DOM
        if(element) {

            // Basic setup
            // ------------------------------

            // Define main variables
            var d3Container = d3.select(element),
                margin = {top: 25, right: 40, bottom: 20, left: 130},
                width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right,
                height = height - margin.top - margin.bottom - 5,
                barHeight = 30,
                duration = 750,
                delay = 25;



            // Construct scales
            // ------------------------------

            // Horizontal
            var x = d3.scale.linear()
                .range([0, width]);

            // Colors
            var color = d3.scale.ordinal()
                .range(["#26A69A", "#ccc"]);



            // Create axes
            // ------------------------------

            // Horizontal
            var xAxis = d3.svg.axis()
                .scale(x)
                .orient("top");



            // Create chart
            // ------------------------------

            // Add SVG element
            var container = d3Container.append("svg");

            // Add SVG group
            var svg = container
                .attr("width", width + margin.left + margin.right)
                .attr("height", height + margin.top + margin.bottom)
                .append("g")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");


            // Construct chart layout
            // ------------------------------

            // Partition
            var partition = d3.layout.partition()
                .value(function(d) { return d.size; });



            // Load data
            // ------------------------------

            d3.json("../../../../global_assets/demo_data/d3/bars/bars_hierarchical.json", function(error, root) {
                partition.nodes(root);
                x.domain([0, root.value]).nice();
                down(root, 0);
            });


            //
            // Append chart elements
            //

            // Add background bars
            svg.append("rect")
                .attr("class", "d3-bars-background")
                .attr("width", width)
                .attr("height", height)
                .style("fill", "#fff")
                .on("click", up);


            // Append axes
            // ------------------------------

            // Horizontal
            svg.append("g")
                .attr("class", "d3-axis d3-axis-horizontal d3-axis-strong");


            // Append bars
            // ------------------------------

            // Create hierarchical structure
            function down(d, i) {
                if (!d.children || this.__transition__) return;
                var end = duration + d.children.length * delay;

                // Mark any currently-displayed bars as exiting.
                var exit = svg.selectAll(".enter")
                    .attr("class", "exit");

                // Entering nodes immediately obscure the clicked-on bar, so hide it.
                exit.selectAll("rect").filter(function(p) { return p === d; })
                    .style("fill-opacity", 1e-6);

                // Enter the new bars for the clicked-on data.
                // Per above, entering bars are immediately visible.
                var enter = bar(d)
                    .attr("transform", stack(i))
                    .style("opacity", 1);

                // Have the text fade-in, even though the bars are visible.
                // Color the bars as parents; they will fade to children if appropriate.
                enter.select("text").style("fill-opacity", 1e-6);
                enter.select("rect").style("fill", color(true));

                // Update the x-scale domain.
                x.domain([0, d3.max(d.children, function(d) { return d.value; })]).nice();

                // Update the x-axis.
                svg.selectAll(".d3-axis-horizontal").transition()
                    .duration(duration)
                    .call(xAxis);

                // Transition entering bars to their new position.
                var enterTransition = enter.transition()
                    .duration(duration)
                    .delay(function(d, i) { return i * delay; })
                    .attr("transform", function(d, i) { return "translate(0," + barHeight * i * 1.2 + ")"; });

                // Transition entering text.
                enterTransition.select("text")
                    .style("fill-opacity", 1);

                // Transition entering rects to the new x-scale.
                enterTransition.select("rect")
                    .attr("width", function(d) { return x(d.value); })
                    .style("fill", function(d) { return color(!!d.children); });

                // Transition exiting bars to fade out.
                var exitTransition = exit.transition()
                    .duration(duration)
                    .style("opacity", 1e-6)
                    .remove();

                // Transition exiting bars to the new x-scale.
                exitTransition.selectAll("rect")
                    .attr("width", function(d) { return x(d.value); });

                // Rebind the current node to the background.
                svg.select(".d3-bars-background")
                    .datum(d)
                    .transition()
                    .duration(end);

                d.index = i;
            }

            // Return to parent level
            function up(d) {
                if (!d.parent || this.__transition__) return;
                var end = duration + d.children.length * delay;

                // Mark any currently-displayed bars as exiting.
                var exit = svg.selectAll(".enter")
                    .attr("class", "exit");

                // Enter the new bars for the clicked-on data's parent.
                var enter = bar(d.parent)
                    .attr("transform", function(d, i) { return "translate(0," + barHeight * i * 1.2 + ")"; })
                    .style("opacity", 1e-6);

                // Color the bars as appropriate.
                // Exiting nodes will obscure the parent bar, so hide it.
                enter.select("rect")
                    .style("fill", function(d) { return color(!!d.children); })
                    .filter(function(p) { return p === d; })
                    .style("fill-opacity", 1e-6);

                // Update the x-scale domain.
                x.domain([0, d3.max(d.parent.children, function(d) { return d.value; })]).nice();

                // Update the x-axis.
                svg.selectAll(".d3-axis-horizontal").transition()
                    .duration(duration)
                    .call(xAxis);

                // Transition entering bars to fade in over the full duration.
                var enterTransition = enter.transition()
                    .duration(end)
                    .style("opacity", 1);

                // Transition entering rects to the new x-scale.
                // When the entering parent rect is done, make it visible!
                enterTransition.select("rect")
                    .attr("width", function(d) { return x(d.value); })
                    .each("end", function(p) { if (p === d) d3.select(this).style("fill-opacity", null); });

                // Transition exiting bars to the parent's position.
                var exitTransition = exit.selectAll("g").transition()
                    .duration(duration)
                    .delay(function(d, i) { return i * delay; })
                    .attr("transform", stack(d.index));

                // Transition exiting text to fade out.
                exitTransition.select("text")
                    .style("fill-opacity", 1e-6);

                // Transition exiting rects to the new scale and fade to parent color.
                exitTransition.select("rect")
                    .attr("width", function(d) { return x(d.value); })
                    .style("fill", color(true));

                // Remove exiting nodes when the last child has finished transitioning.
                exit.transition()
                    .duration(end)
                    .remove();

                // Rebind the current parent to the background.
                svg.select(".d3-bars-background")
                    .datum(d.parent)
                    .transition()
                    .duration(end);
            }

            // Creates a set of bars for the given data node, at the specified index.
            function bar(d) {
                var bar = svg.insert("g", ".d3-axis-vertical")
                    .attr("class", "enter")
                    .attr("transform", "translate(0,5)")
                    .selectAll("g")
                    .data(d.children)
                    .enter()
                    .append("g")
                        .style("cursor", function(d) { return !d.children ? null : "pointer"; })
                        .on("click", down);

                bar.append("text")
                    .attr("x", -6)
                    .attr("y", barHeight / 2)
                    .attr("dy", ".35em")
                    .style("text-anchor", "end")
                    .text(function(d) { return d.name; });

                bar.append("rect")
                    .attr("width", function(d) { return x(d.value); })
                    .attr("height", barHeight);

                return bar;
            }

            // A stateful closure for stacking bars horizontally.
            function stack(i) {
                var x0 = 0;
                return function(d) {
                    var tx = "translate(" + x0 + "," + barHeight * i * 1.2 + ")";
                    x0 += x(d.value);
                    return tx;
                };
            }



            // Resize chart
            // ------------------------------

            // Call function on window resize
            $(window).on('resize', resize);

            // Call function on sidebar width change
            $('.sidebar-control').on('click', resize);

            // Resize function
            // 
            // Since D3 doesn't support SVG resize by default,
            // we need to manually specify parts of the graph that need to 
            // be updated on window resize
            function resize() {

                // Layout variables
                width = d3Container.node().getBoundingClientRect().width - margin.left - margin.right;


                // Layout
                // -------------------------

                // Main svg width
                container.attr("width", width + margin.left + margin.right);

                // Width of appended group
                svg.attr("width", width + margin.left + margin.right);


                // Axes
                // -------------------------

                // Horizontal range
                x.range([0, width]);

                // Horizontal axis
                svg.selectAll('.d3-axis-horizontal').call(xAxis);


                // Chart elements
                // -------------------------

                // Bars
                svg.selectAll('.enter rect').attr("width", function(d) { return x(d.value); });
            }
        }
    };


    //
    // Return objects assigned to module
    //

    return {
        init: function() {
            _barHierarchy();
        }
    }
}();


// Initialize module
// ------------------------------

document.addEventListener('DOMContentLoaded', function() {
    D3BarHierarchy.init();
});
