// drawarea
var svg;

// colors
var border = "thin solid lightgrey";


// nodes


// links

// tree
var compoundSize = 75; // = diameter of nodes of small compounds
var maxCompoundSize = function(){ return 1.5 * compoundSize }; // limit increased size of larger compounds
var maxCompoundPopupSize = function(){ return 5 * compoundSize };
var compoundImageMargin = 0.15;  // compounds images are rectangles, reduce size to fit into nodes

var treeLayoutLinkDistance = function(){ return 2.5 * compoundSize };
var treeLayoutCharge = -1000; // compounds push each other apart
var treeLayoutPseudoCharge = treeLayoutCharge * 0.1; // relaxes forces on pseudo nodes
var treeLayoutLevelGravity = 0.5; // pulls compounds back to their depth level
var treeLayoutXCenterGravity = 0.03; // pulls compounds to x center
var treeLayoutLevels = function(){ return treeLayoutLinkDistance() }; // distance between depth levels
var treeLayoutXInitMargin = function(){ return treeLayoutLinkDistance() }; // initial x-distance between nodes of same depth


// Convenience method to check wether a given node is a leaf
function isLeaf(node) {
    return node.children.length == 0;
}

function getMaxNodeDepth(graph) {
    maxDepth = 0
    graph.nodes.forEach(function(node, i) {
        if (node.depth > maxDepth)
            maxDepth = node.depth;
    });
    return maxDepth;
}

// adds a parents[] and a children[] array for each node set treeSize
function setChildrenAndParents(graph) {
    // Init arrays on each node
    graph.nodes.forEach(function(node, i) {
        node.parents = []
        node.children = []
    });

    // loop through links, determine source and target and populate
    // their parent/children arrays
    graph.links.forEach(function(link, j) {
        source = graph.nodes[link.source];
        target = graph.nodes[link.target];
        source.children.push(target);
        target.parents.push(source);
    });

    // Recursively traverses the tree to count the nodes that can be reached
    function count(node) {
        node.touch = true;
        c = 1;
        node.children.forEach(function(child, i) {
            if (!child.touch) {
                c += count(child);
            }
        });
        node.count = false;
        return c;
    }

    // Check how many nodes can be reached from each given node
    // by traversing the children property
    graph.nodes.forEach(function(node, i) {
        // reset touch on all nodes
        graph.nodes.forEach(function(n, i) {
            n.touch = false;
        });
        // get count
        node.downstreamNodeCount = count(node);
    });

    // determine siblings for each links
    graph.links.forEach(function(link, j) {
        p = graph.nodes[link.source].children.length;
        c = graph.nodes[link.target].parents.length;
        link.siblings = Math.max(p, c);
    });
}

// sets all links to pseudo that are connected to a pseudo node
function setPseudoLinks(graph) {
    graph.links.forEach(function(link, j) {
        if (graph.nodes[link.source].pseudo || graph.nodes[link.target].pseudo) {
            link.pseudo = true;
        } else {
            link.pseudo = false;
        }
    });
}

function redraw() {
    svg.attr("transform", "translate(" + d3.event.translate + ")" + " scale(" + d3.event.scale + ")");
}

function reset() {
    // remove all;
    svg.selectAll("g.node").remove();
    svg.selectAll("rect").remove();
    svg.selectAll("image").remove();
    svg.selectAll("line").remove();
    svg.selectAll("marker").remove();
    svg.selectAll("text").remove();
    svg.selectAll("g").remove();
    svg.selectAll("defs").remove();
    //start(__graph);
}

function _drawPathway(elem, graph, options) {
    width = document.getElementById(elem).offsetWidth;
    height = width * 0.75;

    svg = d3.select("#" + elem)
            .append("svg")
            .attr("width", "100%")
            .attr("height", height)
            .style("border", border)
            .attr("inline", "block")
            .attr("margin", "auto")
            .attr("pointer-events", "all")
            .append('svg:g')
            .call(d3.behavior.zoom().on("zoom", redraw))
            .attr("preserveAspectRatio", "xMidYMid meet")
            .append('svg:g');

    // Background rectangle to enable zoom function
    // even cursor is not on a node or path
    svg.append('svg:rect')
        .attr('width', 9 * width)
        .attr('height', 9 * height)
        .attr('fill', 'white')
        .attr("x", -4 * width)
        .attr("y", -4 * height)
        .attr("opacity", 0.0);

    setChildrenAndParents(graph)
    setPseudoLinks(graph)
    var maxDepths = getMaxNodeDepth(graph);

//    if(options.treeLayout) {
//
//    } else {
//
//    }
    force = d3.layout.force()
                .charge(function(d) {
                    if (d.pseudo) {
                        return treeLayoutPseudoCharge;
                    } else {
                        return treeLayoutCharge;
                    }
                })
                .gravity(0)
                .linkDistance(function(d) {
                    dist = Math.abs(d.target.treeLevel - d.source.treeLevel) * treeLayoutLinkDistance();
                    if (dist == 0) {
                        // nodes with depth in brackets: A (0) -> B (1) + C (2)
                        // then there is a pseudo node that is on the same level as B
                        dist = 0.5 * treeLayoutLinkDistance();
                    }
                    return dist;
                })
                .linkStrength(function(d) {
                    return strength = 1 / d.siblings;
                })
                .size([width, height]);

    force.nodes(graph.nodes).links(graph.links).start();

}

function drawPathway(elem, options) {

    d3.json("", function(error, graph) {
        if (graph) {
            _drawPathway(elem, graph, options);
        } else {
            throw new Error("Graph not defined: " + error);
        }
    });
}