var toTree = require('./id_to_tree')

module.exports = function graph(waterfall){
  var ret = {}
  //make a clean copy of the nodes
  var nodes = JSON.parse(JSON.stringify(waterfall.functions))
  var links = {}
  if( waterfall.waterfalls ){
    waterfall.waterfalls.forEach(handleWaterfall.bind(this, nodes, links))
  }
  ret.nodemap = nodes
  ret.linkmap = links
  return ret
}

//
function handleWaterfall(nodes, links, w){
  if( w.segments && w.segments.length === 0){
    return
  }

  var segs = w.segments.filter(function(d){ return d.type == 'fn'})
  var callbacks = w.segments.filter(function(d){return d.type == 'wait'})
  var tree = toTree(w.id)
  tree.children.forEach(dft)
  addCallbacks(callbacks)

  function dft(tree){
    var costs,
        s,
        node,
        inclusive,
        exclusive,
        child = 0
    if( !tree.id ){
      return
    }

    s = segs.shift()
    node = nodes[s.id]
    if( tree.children ){
      costs = tree.children.map(dft)
      costs.map(function(d){child += d})
      addLinks(tree, s)
    }
    inclusive = s.end - s.start
    exclusive = inclusive - child

    //initialize
    node.exclusive_duration   = node.exclusive_duration || 0
    node.child_duration       = node.child_duration || 0
    node.num_calls            = node.num_calls || 0
    node.duration             = node.duration || 0
    // set
    node.exclusive_duration   += exclusive
    node.child_duration       += child
    node.num_calls            += s.visits * w.instance_count
    node.duration             += inclusive

    return exclusive
  }

  function addLinks(tree, seg){
    tree.children.forEach(function(d){
      var id = [tree.id, d.id, 'invocation'].join('~')
      var link = links[id]
      if( !link ){
        link = {
          id: id,
          source: tree.id,
          target: d.id,
          type: 'invocation',
          num_calls: seg.visits * w.instance_count
        }
        links[id] = link
      } else {
        link.num_calls += seg.visits * w.instance_count
      }
    })
  }

  function addCallbacks(callbacks){
    callbacks.forEach(function(d){
      var id = [d.source, d.target, 'callback'].join('~')
      var link = links[id]
      if( !link ){
        link = {
          id: id,
          source: d.source,
          target: d.target,
          type: 'callback',
          num_calls: d.visits * w.instance_count,
          total_delay: d.end - d.start
        }
        links[id] = link
      } else {
        link.num_calls += d.visits * w.instance_count
        link.total_delay += (d.end - d.start)
      }
    })
  }

}
