"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.walkAllChildSegments = exports.backwardExcursion = exports.forwardExcursion = exports.depthFirstNodeWalk = exports.NodeAction = exports.LeafAction = void 0;
exports.LeafAction = {
    Exit: false,
};
exports.NodeAction = {
    Continue: undefined,
    // exit is false to unify with leafActionOverride
    Exit: exports.LeafAction.Exit,
    Skip: 2,
};
/**
 * Does a depth first walk of the tree from the specific start.
 *
 * @param startBlock - The block of the tree to start the walk from
 * @param startChild - The child of that block to start from
 * @param downAction - Called as we walk down the tree to the leaves.
 * @param leafActionOverride - Overrides downAction for leaves, generally used without downAction
 * @param upAction - Called after all the children of a block are walked.
 * @param forward - whether to walk forward or backward
 * @returns true if we naturally exit, false if exiting due to Exit action result
 */
function depthFirstNodeWalk(startBlock, startChild, downAction, leafActionOverride, upAction, forward = true) {
    const increment = forward ? 1 : -1;
    const leafAction = leafActionOverride !== null && leafActionOverride !== void 0 ? leafActionOverride : downAction;
    if (leafAction === undefined) {
        return true;
    }
    let block = startBlock;
    let childCount = block.childCount;
    let start = startChild;
    // eslint-disable-next-line no-constant-condition
    while (true) {
        // go down to the leaf level
        let blockResult;
        while ((start === null || start === void 0 ? void 0 : start.isLeaf()) === false) {
            // cast is safe due to isLeaf === false in while above
            block = start;
            childCount = block.childCount;
            blockResult = downAction === null || downAction === void 0 ? void 0 : downAction(block);
            // setting start undefined will skip the leaf walk
            // so if the block result isn't continue, set it
            // undefined
            start = blockResult === exports.NodeAction.Continue
                ? block.children[forward ? 0 : childCount - 1]
                : undefined;
        }
        let exit = blockResult === exports.NodeAction.Exit;
        // walk the leaves if we reached them
        if (start !== undefined) {
            for (let i = start.index; i !== -1 && i !== childCount; i += increment) {
                // the above loop ensures start is a leaf or undefined, so all children
                // will be leaves if start exits, so the cast is safe
                if (leafAction(block.children[i]) === exports.LeafAction.Exit) {
                    exit = true;
                    break;
                }
            }
        }
        // if there is no upAction, we don't need to walk up before exiting
        if (upAction === undefined && exit) {
            return false;
        }
        // since we already enumerated the children
        // we walk up to the next level until there is a sibling
        // or all the way up if exit is true
        let nextIndex = -1;
        do {
            // if the blockAction was exit or skip
            // we shouldn't process that block again,
            // if there are subsequent parents while walking up
            // we will process those.
            if (blockResult === exports.NodeAction.Continue) {
                upAction === null || upAction === void 0 ? void 0 : upAction(block);
            }
            else {
                blockResult = exports.NodeAction.Continue;
            }
            if (block.parent === undefined) {
                return !exit;
            }
            start = block;
            block = block.parent;
            childCount = block.childCount;
            nextIndex = start.index + increment;
        } while (exit || nextIndex === -1 || nextIndex === childCount);
        // the above loop ensured that siblings are possible
        start = block.children[nextIndex];
    }
}
exports.depthFirstNodeWalk = depthFirstNodeWalk;
/**
 * Visit segments starting from node's right/far/forward siblings, then up to node's parent.
 * All segments past `node` are visited, regardless of their visibility.
 */
function forwardExcursion(startNode, leafAction) {
    if (startNode.parent === undefined) {
        return true;
    }
    return depthFirstNodeWalk(startNode.parent, 
    // this will either be the sibling, or undefined
    // either is fine, and will result in skipping
    // the startNode only
    startNode.parent.children[startNode.index + 1], undefined /* downAction */, leafAction);
}
exports.forwardExcursion = forwardExcursion;
/**
 * Visit segments starting from node's left/near/backwards siblings, then up to node's parent.
 * All segments past `node` are visited, regardless of their visibility.
 */
function backwardExcursion(startNode, leafAction) {
    if (startNode.parent === undefined) {
        return true;
    }
    return depthFirstNodeWalk(startNode.parent, 
    // this will either be the sibling, or undefined
    // either is fine, and will result in skipping
    // the startNode only
    startNode.parent.children[startNode.index - 1], undefined /* downAction */, leafAction, undefined /* upAction */, false /* forward */);
}
exports.backwardExcursion = backwardExcursion;
/**
 * Walks all segments below the specific start block
 * @param startBlock - The block to start the walk at
 * @param leafAction - The action to perform on the leaves
 * @returns true if we naturally exit, false if exiting due to leaf action result
 */
function walkAllChildSegments(startBlock, leafAction) {
    if (startBlock.childCount === 0) {
        return true;
    }
    // undefined shouldn't actually be added, but this allows subsequent check for `node.parent` to typecheck
    // without further runtime work.
    const ancestors = new Set();
    for (let cur = startBlock.parent; cur !== undefined; cur = cur.parent) {
        ancestors.add(cur);
    }
    return depthFirstNodeWalk(startBlock, startBlock.children[0], ancestors.size === 0
        ? undefined
        : (node) => ancestors.has(node.parent) ? exports.NodeAction.Exit : exports.NodeAction.Continue, leafAction);
}
exports.walkAllChildSegments = walkAllChildSegments;
//# sourceMappingURL=mergeTreeNodeWalk.js.map