/**
 *
 * @param {Object3D} a
 * @param {Object3D} b
 * @param {function(Object3D, Object3D)} callback
 */
function parallelTraverse(a, b, callback) {

    callback(a, b);

    for (var i = 0; i < a.children.length; i++) {

        parallelTraverse(a.children[i], b.children[i], callback);

    }

}

/**
 *
 * @param {Object3D} source
 * @return {Object3D}
 */
export function cloneObject3D(source) {
    var sourceLookup = new Map();
    var cloneLookup = new Map();

    var clone = source.clone();

    parallelTraverse(source, clone, function (sourceNode, clonedNode) {

        sourceLookup.set(clonedNode, sourceNode);
        cloneLookup.set(sourceNode, clonedNode);

    });

    clone.traverse(function (node) {
        if (!node.isMesh) {
            return;
        }

        var clonedMesh = node;
        var sourceMesh = sourceLookup.get(node);

        //copy custom depth material
        if (sourceMesh.customDepthMaterial !== undefined) {
            clonedMesh.customDepthMaterial = sourceMesh.customDepthMaterial;
        }

        if (!node.isSkinnedMesh) {
            return;
        }

        var sourceBones = sourceMesh.skeleton.bones;

        clonedMesh.skeleton = sourceMesh.skeleton.clone();
        clonedMesh.bindMatrix.copy(sourceMesh.bindMatrix);

        clonedMesh.skeleton.bones = sourceBones.map(function (bone) {

            return cloneLookup.get(bone);

        });

        clonedMesh.bind(clonedMesh.skeleton, clonedMesh.bindMatrix);

    });

    return clone;
}
