text: var apijs = require("tnt.api");
  var iterator = require("tnt.utils").iterator;
  
  var tnt_node = function (data) {
  //tnt.tree.node = function (data) {
      "use strict";
  
      var node = function () {
      };
  
      var api = apijs (node);
  
      // API
  //     node.nodes = function() {
  // 	if (cluster === undefined) {
  // 	    cluster = d3.layout.cluster()
  // 	    // TODO: length and children should be exposed in the API
  // 	    // i.e. the user should be able to change this defaults via the API
  // 	    // children is the defaults for parse_newick, but maybe we should change that
  // 	    // or at least not assume this is always the case for the data provided
  // 		.value(function(d) {return d.length})
  // 		.children(function(d) {return d.children});
  // 	}
  // 	nodes = cluster.nodes(data);
  // 	return nodes;
  //     };
  
      var apply_to_data = function (data, cbak) {
  	cbak(data);
  	if (data.children !== undefined) {
  	    for (var i=0; i<data.children.length; i++) {
  		apply_to_data(data.children[i], cbak);
  	    }
  	}
      };
  
      var create_ids = function () {
  	var i = iterator(1);
  	// We can't use apply because apply creates new trees on every node
  	// We should use the direct data instead
  	apply_to_data (data, function (d) {
  	    if (d._id === undefined) {
  		d._id = i();
  		// TODO: Not sure _inSubTree is strictly necessary
  		// d._inSubTree = {prev:true, curr:true};
  	    }
  	});
      };
  
      var link_parents = function (data) {
  	if (data === undefined) {
  	    return;
  	}
  	if (data.children === undefined) {
  	    return;
  	}
  	for (var i=0; i<data.children.length; i++) {
  	    // _parent?
  	    data.children[i]._parent = data;
  	    link_parents(data.children[i]);
  	}
      };
  
      var compute_root_dists = function (data) {
  	apply_to_data (data, function (d) {
  	    var l;
  	    if (d._parent === undefined) {
  		d._root_dist = 0;
  	    } else {
  		var l = 0;
  		if (d.branch_length) {
  		    l = d.branch_length
  		}
  		d._root_dist = l + d._parent._root_dist;
  	    }
  	});
      };
  
      // TODO: data can't be rewritten used the api yet. We need finalizers
      node.data = function(new_data) {
  	if (!arguments.length) {
  	    return data
  	}
  	data = new_data;
  	create_ids();
  	link_parents(data);
  	compute_root_dists(data);
  	return node;
      };
      // We bind the data that has been passed
      node.data(data);
  
      api.method ('find_all', function (cbak, deep) {
  	var nodes = [];
  	node.apply (function (n) {
  	    if (cbak(n)) {
  		nodes.push (n);
  	    }
  	});
  	return nodes;
      });
      
      api.method ('find_node', function (cbak, deep) {
  	if (cbak(node)) {
  	    return node;
  	}
  
  	if (data.children !== undefined) {
  	    for (var j=0; j<data.children.length; j++) {
  		var found = tnt_node(data.children[j]).find_node(cbak, deep);
  		if (found) {
  		    return found;
  		}
  	    }
  	}
  
  	if (deep && (data._children !== undefined)) {
  	    for (var i=0; i<data._children.length; i++) {
  		tnt_node(data._children[i]).find_node(cbak, deep)
  		var found = tnt_node(data._children[i]).find_node(cbak, deep);
  		if (found) {
  		    return found;
  		}
  	    }
  	}
      });
  
      api.method ('find_node_by_name', function(name, deep) {
  	return node.find_node (function (node) {
  	    return node.node_name() === name
  	}, deep);
      });
  
      api.method ('toggle', function() {
  	if (data) {
  	    if (data.children) { // Uncollapsed -> collapse
  		var hidden = 0;
  		node.apply (function (n) {
  		    var hidden_here = n.n_hidden() || 0;
  		    hidden += (n.n_hidden() || 0) + 1;
  		});
  		node.n_hidden (hidden-1);
  		data._children = data.children;
  		data.children = undefined;
  	    } else {             // Collapsed -> uncollapse
  		node.n_hidden(0);
  		data.children = data._children;
  		data._children = undefined;
  	    }
  	}
  	return this;
      });
  
      api.method ('is_collapsed', function () {
  	return (data._children !== undefined && data.children === undefined);
      });
  
      var has_ancestor = function(n, ancestor) {
  	// It is better to work at the data level
  	n = n.data();
  	ancestor = ancestor.data();
  	if (n._parent === undefined) {
  	    return false
  	}
  	n = n._parent
  	for (;;) {
  	    if (n === undefined) {
  		return false;
  	    }
  	    if (n === ancestor) {
  		return true;
  	    }
  	    n = n._parent;
  	}
      };
  
      // This is the easiest way to calculate the LCA I can think of. But it is very inefficient too.
      // It is working fine by now, but in case it needs to be more performant we can implement the LCA
      // algorithm explained here:
      // http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=lowestCommonAncestor
      api.method ('lca', function (nodes) {
  	if (nodes.length === 1) {
  	    return nodes[0];
  	}
  	var lca_node = nodes[0];
  	for (var i = 1; i<nodes.length; i++) {
  	    lca_node = _lca(lca_node, nodes[i]);
  	}
  	return lca_node;
  	// return tnt_node(lca_node);
      });
  
      var _lca = function(node1, node2) {
  	if (node1.data() === node2.data()) {
  	    return node1;
  	}
  	if (has_ancestor(node1, node2)) {
  	    return node2;
  	}
  	return _lca(node1, node2.parent());
      };
  
      api.method('n_hidden', function (val) {
  	if (!arguments.length) {
  	    return node.property('_hidden');
  	}
  	node.property('_hidden', val);
  	return node
      });
  
      api.method ('get_all_nodes', function (deep) {
  	var nodes = [];
  	node.apply(function (n) {
  	    nodes.push(n);
  	}, deep);
  	return nodes;
      });
  
      api.method ('get_all_leaves', function (deep) {
  	var leaves = [];
  	node.apply(function (n) {
  	    if (n.is_leaf(deep)) {
  		leaves.push(n);
  	    }
  	}, deep);
  	return leaves;
      });
  
      api.method ('upstream', function(cbak) {
  	cbak(node);
  	var parent = node.parent();
  	if (parent !== undefined) {
  	    parent.upstream(cbak);
  	}
  //	tnt_node(parent).upstream(cbak);
  // 	node.upstream(node._parent, cbak);
      });
  
      api.method ('subtree', function(nodes, keep_singletons) {
  	if (keep_singletons === undefined) {
  	    keep_singletons = false;
  	}
      	var node_counts = {};
      	for (var i=0; i<nodes.length; i++) {
  	    var n = nodes[i];
  	    if (n !== undefined) {
  		n.upstream (function (this_node){
  		    var id = this_node.id();
  		    if (node_counts[id] === undefined) {
  			node_counts[id] = 0;
  		    }
  		    node_counts[id]++
      		});
  	    }
      	}
      
  	var is_singleton = function (node_data) {
  	    var n_children = 0;
  	    if (node_data.children === undefined) {
  		return false;
  	    }
  	    for (var i=0; i<node_data.children.length; i++) {
  		var id = node_data.children[i]._id;
  		if (node_counts[id] > 0) {
  		    n_children++;
  		}
  	    }
  	    return n_children === 1;
  	};
  
  	var subtree = {};
  	copy_data (data, subtree, 0, function (node_data) {
  	    var node_id = node_data._id;
  	    var counts = node_counts[node_id];
  	    
  	    // Is in path
  	    if (counts > 0) {
  		if (is_singleton(node_data) && !keep_singletons) {
  		    return false; 
  		}
  		return true;
  	    }
  	    // Is not in path
  	    return false;
  	});
  
  	return tnt_node(subtree.children[0]);
      });
  
      var copy_data = function (orig_data, subtree, currBranchLength, condition) {
          if (orig_data === undefined) {
  	    return;
          }
  
          if (condition(orig_data)) {
  	    var copy = copy_node(orig_data, currBranchLength);
  	    if (subtree.children === undefined) {
                  subtree.children = [];
  	    }
  	    subtree.children.push(copy);
  	    if (orig_data.children === undefined) {
                  return;
  	    }
  	    for (var i = 0; i < orig_data.children.length; i++) {
                  copy_data (orig_data.children[i], copy, 0, condition);
  	    }
          } else {
  	    if (orig_data.children === undefined) {
                  return;
  	    }
  	    currBranchLength += orig_data.branch_length || 0;
  	    for (var i = 0; i < orig_data.children.length; i++) {
                  copy_data(orig_data.children[i], subtree, currBranchLength, condition);
  	    }
          }
      };
  
      var copy_node = function (node_data, extraBranchLength) {
  	var copy = {};
  	// copy all the own properties excepts links to other nodes or depth
  	for (var param in node_data) {
  	    if ((param === "children") ||
  		(param === "_children") ||
  		(param === "_parent") ||
  		(param === "depth")) {
  		continue;
  	    }
  	    if (node_data.hasOwnProperty(param)) {
  		copy[param] = node_data[param];
  	    }
  	}
  	if ((copy.branch_length !== undefined) && (extraBranchLength !== undefined)) {
  	    copy.branch_length += extraBranchLength;
  	}
  	return copy;
      };
  
      
      // TODO: This method visits all the nodes
      // a more performant version should return true
      // the first time cbak(node) is true
      api.method ('present', function (cbak) {
  	// cbak should return true/false
  	var is_true = false;
  	node.apply (function (n) {
  	    if (cbak(n) === true) {
  		is_true = true;
  	    }
  	});
  	return is_true;
      });
  
      // cbak is called with two nodes
      // and should return a negative number, 0 or a positive number
      api.method ('sort', function (cbak) {
  	if (data.children === undefined) {
  	    return;
  	}
  
  	var new_children = [];
  	for (var i=0; i<data.children.length; i++) {
  	    new_children.push(tnt_node(data.children[i]));
  	}
  
  	new_children.sort(cbak);
  
  	data.children = [];
  	for (var i=0; i<new_children.length; i++) {
  	    data.children.push(new_children[i].data());
  	}
  
  	for (var i=0; i<data.children.length; i++) {
  	    tnt_node(data.children[i]).sort(cbak);
  	}
      });
  
      api.method ('flatten', function (preserve_internal) {
  	if (node.is_leaf()) {
  	    return node;
  	}
  	var data = node.data();
  	var newroot = copy_node(data);
  	var nodes;
  	if (preserve_internal) {
  	    nodes = node.get_all_nodes();
  	    nodes.shift(); // the self node is also included
  	} else {
  	    nodes = node.get_all_leaves();
  	}
  	newroot.children = [];
  	for (var i=0; i<nodes.length; i++) {
  	    delete (nodes[i].children);
  	    newroot.children.push(copy_node(nodes[i].data()));
  	}
  
  	return tnt_node(newroot);
      });
  
      
      // TODO: This method only 'apply's to non collapsed nodes (ie ._children is not visited)
      // Would it be better to have an extra flag (true/false) to visit also collapsed nodes?
      api.method ('apply', function(cbak, deep) {
  	if (deep === undefined) {
  	    deep = false;
  	}
  	cbak(node);
  	if (data.children !== undefined) {
  	    for (var i=0; i<data.children.length; i++) {
  		var n = tnt_node(data.children[i])
  		n.apply(cbak, deep);
  	    }
  	}
  
  	if ((data._children !== undefined) && deep) {
  	    for (var j=0; j<data._children.length; j++) {
  		var n = tnt_node(data._children[j]);
  		n.apply(cbak, deep);
  	    }
  	}
      });
  
      // TODO: Not sure if it makes sense to set via a callback:
      // root.property (function (node, val) {
      //    node.deeper.field = val
      // }, 'new_value')
      api.method ('property', function(prop, value) {
  	if (arguments.length === 1) {
  	    if ((typeof prop) === 'function') {
  		return prop(data)	
  	    }
  	    return data[prop]
  	}
  	if ((typeof prop) === 'function') {
  	    prop(data, value);   
  	}
  	data[prop] = value;
  	return node;
      });
  
      api.method ('is_leaf', function(deep) {
  	if (deep) {
  	    return ((data.children === undefined) && (data._children === undefined));
  	}
  	return data.children === undefined;
      });
  
      // It looks like the cluster can't be used for anything useful here
      // It is now included as an optional parameter to the tnt.tree() method call
      // so I'm commenting the getter
      // node.cluster = function() {
      // 	return cluster;
      // };
  
      // node.depth = function (node) {
      //     return node.depth;
      // };
  
  //     node.name = function (node) {
  //         return node.name;
  //     };
  
      api.method ('id', function () {
  	return node.property('_id');
      });
  
      api.method ('node_name', function () {
  	return node.property('name');
      });
  
      api.method ('branch_length', function () {
  	return node.property('branch_length');
      });
  
      api.method ('root_dist', function () {
  	return node.property('_root_dist');
      });
  
      api.method ('children', function (deep) {
  	var children = [];
  
  	if (data.children) {
  	    for (var i=0; i<data.children.length; i++) {
  		children.push(tnt_node(data.children[i]));
  	    }
  	}
  	if ((data._children) && deep) {
  	    for (var j=0; j<data._children.length; j++) {
  		children.push(tnt_node(data._children[j]));
  	    }
  	}
  	if (children.length === 0) {
  	    return undefined;
  	}
  	return children;
      });
  
      api.method ('parent', function () {
  	if (data._parent === undefined) {
  	    return undefined;
  	}
  	return tnt_node(data._parent);
      });
  
      return node;
  
  };
  
  module.exports = exports = tnt_node;
  
  