const assert = require('assert')
const Prune = require('./prune')
module.exports = function (limit, dive, test, tree) {
function find (found, limit, dive, test, node) {
let forked = false
function descend (node, dive, test, depth) {
if (depth > dive[1]) {
} else if (depth < dive[0]) {
node.errors.forEach(error => descend(error, dive, test, depth + 1))
} else {
const matches = test(tree.ordered[node.index])
if (matches == null) {
node.errors.forEach(error => descend(error, dive, test, depth + 1))
} else if (Array.isArray(matches)) {
forked = true
assert(limit == -1, 'limit can only be specified in leaf alternates')
fork(found, matches, node)
} else if (matches.dive.length == 0) {
found.push(node.id)
} else {
const subDive = matches.dive.map(value => value == Number.MAX_SAFE_INTEGER ? value : value + depth + 1)
node.errors.forEach(error => descend(error, subDive, matches.test, depth + 1))
}
}
}
descend(node, dive, test, 0)
return forked
}
function fork (found, matches, node) {
for (const match of matches) {
const _found = []
const forked = find(_found, match.limit, match.dive, match.test, node)
if (forked) {
if (node.errors.length == 0) {
found.push.apply(found, _found)
}
} else {
if ((limit == 0 && _found.length != 0) || (limit == -1 && _found.length == 1) || _found.length == limit) {
Prune(tree, _found)
found.push.apply(found, _found)
}
}
}
}
const found = []
fork(found, [{ limit, dive, test }], tree)
if (tree.errors.length == 0) {
return found
}
return null
}