/*
 * User defined callbacks for handling FlowViz events.
 *
 * Note: in the callbacks, 'this' is bound to our instance of the FlowViz object because FlowViz extends EventEmitter!
 */
var App = new FlowViz.App('config_calculator.json', 'svg#InteractiveViz', {
    Events: {
        "flowviz-ready": function() {
            this.Controls.Create('div#LeftSidebar');
            this.Legend.Create('div#LeftSidebar');
            this.DataEditor.Create('div#LeftSidebar');

            var types = this.Config.getLeafNodesTypes();

            var nodes = [];

            nodes.push(this.GraphManager.AddNode(types[0], 50, 50, false));
            nodes.push(this.GraphManager.AddNode(types[0], 250, 50, false));
            nodes.push(this.GraphManager.AddNode(types[4], 150, 250, false));
            nodes.push(this.GraphManager.AddNode(types[0], 350, 250, false));
            nodes.push(this.GraphManager.AddNode(types[2], 250, 450));
            nodes.push(this.GraphManager.AddNode(types[5], 250, 650));

            nodes[0].SetDataItem('Value', 1);
            nodes[1].SetDataItem('Value', 2);
            nodes[3].SetDataItem('Value', 4);

            var edges = [];

            edges.push(this.GraphManager.AddEdge(nodes[0], nodes[2], this.FlowEdge.STD, false));
            edges.push(this.GraphManager.AddEdge(nodes[1], nodes[2], this.FlowEdge.STD, false));
            edges.push(this.GraphManager.AddEdge(nodes[2], nodes[4], this.FlowEdge.STD, false));
            edges.push(this.GraphManager.AddEdge(nodes[3], nodes[4], this.FlowEdge.STD, false));
            edges.push(this.GraphManager.AddEdge(nodes[4], nodes[5], this.FlowEdge.STD));

            edges[0].SetDataItem('Order', 1);
            edges[1].SetDataItem('Order', 2);
            edges[2].SetDataItem('Order', 2);
            edges[3].SetDataItem('Order', 1);
        },

        "selection-changed": function(node) {
            if (node !== null && node.type.name === "Output") {
                if (this.ConstraintChecker.IsValidGraph(this.GraphManager.nodes, this.GraphManager.edges)) {
                    this.ShowMessage(calculateResult(this.GraphManager.nodes));
                }
            }
        },

        "node-data-changed": function(node, data, old) {
            console.log('node changed');
            d3.selectAll('.flow-node')
                .each(function(d) {
                    if(d === node) {
                        d3.select(this)
                            .select('.node-svg text')
                            .attr('x', node.type.width / 2)
                            .attr('y', node.type.height / 2 + 20)
                            .attr('transform', '')
                            .attr('font-size', '60px')
                            .attr("fill", "blue")
                            .attr('text-anchor', 'middle')
                            .text("" + data.value);
                    }
                });
        },

        "renderer-redrawn": function(edge, data, old) {
            d3.selectAll('.flow-node')
                .each(function(d) {
                    if(d.type.type === "Terminals.Number") {
                        d3.select(this)
                            .select('.node-svg text')
                            .attr('x', d.type.width / 2)
                            .attr('y', d.type.height / 2 + 20)
                            .attr('transform', '')
                            .attr('font-size', '60px')
                            .attr("fill", "blue")
                            .attr('text-anchor', 'middle')
                            .text("" + d.DataItems['Value'].value);
                    }
                });
        }
    },

    DataValidation: {
        NumberIsOneOrTwo: function(oldValue, newValue) {
            return newValue === 1 || newValue === 2;
        }
    }
});

function calculateResult(nodes) {
    for (var j = 0; j < nodes.length; j++) {
        nodes[j].input = [];
    }
    var ts = getTopSort(nodes);
    for (var i = 0; i < ts.length; i++) {
        if (ts[i].getOutgoingEdges().length == 0) {
            return ts[i].input[0];
        }
        console.log(ts[i].type.type);
        var value = 0;
        var incoming = ts[i].getIncomingEdges();
        if (incoming.length > 0) {
            var order = incoming[0].GetDataItemValue("Order");
            value = compute(ts[i], order);
        } else {
            value = ts[i].GetDataItemValue('Value');
        }
        ts[i].getOutgoingEdges()[0].end.input.push(value);
    }
    return null;
}

function compute(node, order) {
    if (node.type.name === "Addition") {
        return node.input[0] + node.input[1];
    } else if (node.type.name === "Subtraction") {
        console.log(order);
        if(order === 1) {
            return node.input[1] - node.input[0];
        } else {
            return node.input[0] - node.input[1];
        }
    } else if (node.type.name === "Multiplication") {
        return node.input[0] * node.input[1];
    } else if (node.type.name === "Division") {
        if(order === 1) {
            return node.input[1] / node.input[0];
        } else {
            return node.input[0] / node.input[1];
        }
    }
}

function getTopSort(nodes) {
    var ts = [];
    var currentSet = [];
    for (var i = 0; i < nodes.length; i++) {
        if (nodes[i].getIncomingEdges().length == 0) {
            currentSet.push(nodes[i]);
        }
    }
    var visitedEdges = [];
    while (currentSet.length > 0) {
        var current = currentSet.pop();
        ts.push(current);
        var outEdges = current.getOutgoingEdges();
        for (i = 0; i < outEdges.length; i++) {
            visitedEdges.push(outEdges[i]);
            var hasEdges = false;
            var inEdges = outEdges[i].end.getIncomingEdges();
            for (var j = 0; j < inEdges.length; j++) {
                if (visitedEdges.indexOf(inEdges[j]) == -1) {
                    hasEdges = true;
                    break;
                }
            }
            if (!hasEdges) {
                currentSet.push(outEdges[i].end)
            }
        }
    }
    return ts;
}