/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { assert } from "@fluidframework/core-utils";
/**
 * A symbol for marking an object as an {@link ITreeCursor}.
 *
 * Useful when APIs want to take in tree data in multiple formats, including cursors.
 */
export const CursorMarker = Symbol("CursorMarker");
/**
 * Check if something is an {@link ITreeCursor}.
 *
 * Useful when APIs want to take in tree data in multiple formats, including cursors.
 */
export function isCursor(data) {
    // Other than on null and undefined, looking up a missing symbol shouldn't type error.
    // typeof check deals with undefined while providing an early out for other non-object types.
    return data !== null && typeof data === "object" && data[CursorMarker] === true;
}
/**
 * @public
 */
export var CursorLocationType;
(function (CursorLocationType) {
    /**
     * Can iterate through nodes in a field.
     * At a "current node".
     */
    CursorLocationType[CursorLocationType["Nodes"] = 0] = "Nodes";
    /**
     * Can iterate through fields of a node.
     * At a "current field".
     */
    CursorLocationType[CursorLocationType["Fields"] = 1] = "Fields";
})(CursorLocationType || (CursorLocationType = {}));
/**
 * @param cursor - tree whose fields will be visited.
 * @param f - builds output from field, which will be selected in cursor when cursor is provided.
 * If `f` moves cursor, it must put it back to where it was at the beginning of `f` before returning.
 * @returns array resulting from applying `f` to each field of the current node on `cursor`.
 * Returns an empty array if the node is empty or not present (which are considered the same).
 * Note that order is not specified for field iteration.
 */
export function mapCursorFields(cursor, f) {
    const output = [];
    forEachField(cursor, (c) => {
        output.push(f(c));
    });
    return output;
}
/**
 * @param cursor - cursor at a node whose fields will be visited.
 * @param f - For on each field.
 * If `f` moves cursor, it must put it back to where it was at the beginning of `f` before returning.
 */
export function forEachField(cursor, f) {
    assert(cursor.mode === 0 /* Nodes */, 0x411 /* should be in nodes */);
    for (let inField = cursor.firstField(); inField; inField = cursor.nextField()) {
        f(cursor);
    }
}
/**
 * @param cursor - tree whose field will be visited.
 * @param f - builds output from field member, which will be selected in cursor when cursor is provided.
 * If `f` moves cursor, it must put it back to where it was at the beginning of `f` before returning.
 * @returns array resulting from applying `f` to each item of the current field on `cursor`.
 * Returns an empty array if the field is empty or not present (which are considered the same).
 */
export function mapCursorField(cursor, f) {
    const output = [];
    forEachNode(cursor, (c) => {
        output.push(f(c));
    });
    return output;
}
/**
 * @param cursor - cursor at a field whose nodes will be visited.
 * @param f - For on each node.
 * If `f` moves cursor, it must put it back to where it was at the beginning of `f` before returning.
 */
export function forEachNode(cursor, f) {
    assert(cursor.mode === 1 /* Fields */, 0x3bd /* should be in fields */);
    for (let inNodes = cursor.firstNode(); inNodes; inNodes = cursor.nextNode()) {
        f(cursor);
    }
}
/**
 * @param cursor - cursor at a field or node.
 * @param f - Function to invoke for each node.
 * If `f` moves the cursor, it must put it back to where it was at the beginning of `f` before returning.
 *
 * Invokes `f` on each node in the subtree rooted at the current field or node.
 * Traversal is pre-order.
 * If the cursor is at a node, `f` will be invoked on that node.
 *
 * Returns the `cursor` to its initial position.
 */
export function forEachNodeInSubtree(cursor, f) {
    if (cursor.mode === 0 /* Nodes */) {
        f(cursor);
        forEachField(cursor, (c) => forEachNodeInSubtree(c, f));
    }
    else {
        forEachNode(cursor, (c) => forEachNodeInSubtree(c, f));
    }
}
/**
 * Casts a cursor to an {@link ITreeCursorSynchronous}.
 *
 * TODO: #1404: Handle this properly for partial data loading support.
 */
export function castCursorToSynchronous(cursor) {
    return cursor;
}
/**
 * Runs `f` inside of field `field` on `cursor`.
 * @param cursor - Cursor whoso field to enter and exit. Must be in `nodes` mode.
 * @param field - Field to enter.
 * @param f - Callback to run when in field.
 * @returns return value of `f`
 */
export function inCursorField(cursor, field, f) {
    cursor.enterField(field);
    const result = f(cursor);
    cursor.exitField();
    return result;
}
/**
 * Runs `f` inside of node `index` on `cursor`.
 * @param cursor - Cursor whoso node to enter and exit. Must be in `fields` mode.
 * @param index - Node to enter.
 * @param f - Callback to run when in node.
 * @returns return value of `f`
 */
export function inCursorNode(cursor, index, f) {
    cursor.enterNode(index);
    const result = f(cursor);
    cursor.exitNode();
    return result;
}
//# sourceMappingURL=cursor.js.map