iD.behavior.Copy = function(context) {
    var keybinding = d3.keybinding('copy');

    function groupEntities(ids, graph) {
        var entities = ids.map(function (id) { return graph.entity(id); });
        return _.extend({relation: [], way: [], node: []},
            _.groupBy(entities, function(entity) { return entity.type; }));
    }

    function getDescendants(id, graph, descendants) {
        var entity = graph.entity(id),
            i, children;

        descendants = descendants || {};

        if (entity.type === 'relation') {
            children = _.pluck(entity.members, 'id');
        } else if (entity.type === 'way') {
            children = entity.nodes;
        } else {
            children = [];
        }

        for (i = 0; i < children.length; i++) {
            if (!descendants[children[i]]) {
                descendants[children[i]] = true;
                descendants = getDescendants(children[i], graph, descendants);
            }
        }

        return descendants;
    }

    function doCopy() {
        d3.event.preventDefault();

        var graph = context.graph(),
            selected = groupEntities(context.selectedIDs(), graph),
            canCopy = [],
            skip = {},
            i, entity;

        for (i = 0; i < selected.relation.length; i++) {
            entity = selected.relation[i];
            if (!skip[entity.id] && entity.isComplete(graph)) {
                canCopy.push(entity.id);
                skip = getDescendants(entity.id, graph, skip);
            }
        }
        for (i = 0; i < selected.way.length; i++) {
            entity = selected.way[i];
            if (!skip[entity.id]) {
                canCopy.push(entity.id);
                skip = getDescendants(entity.id, graph, skip);
            }
        }
        for (i = 0; i < selected.node.length; i++) {
            entity = selected.node[i];
            if (!skip[entity.id]) {
                canCopy.push(entity.id);
            }
        }

        context.copyIDs(canCopy);
    }

    function copy() {
        keybinding.on(iD.ui.cmd('⌘C'), doCopy);
        d3.select(document).call(keybinding);
        return copy;
    }

    copy.off = function() {
        d3.select(document).call(keybinding.off);
    };

    return copy;
};
