"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ObjectCursor = void 0;
const optional_1 = require("../../types/optional");
const clone_1 = require("../../util/clone");
const key_path_1 = require("./key-path");
const traverse_1 = require("./traverse");
class ObjectCursor {
    /**
     * Create a cursor for an object.
     *
     * @param root - An object to traverse.
     */
    constructor(root) {
        this.values = [root];
        this.keyPaths = [key_path_1.thisKeyPath];
        this.savedDepths = [];
    }
    /**
     * The current value this cursor is pointing at.
     */
    get currentValue() {
        return this.values[this.values.length - 1];
    }
    /**
     * The key path of the value this cursor is pointing at.
     */
    get currentKeyPath() {
        return this.keyPaths[this.keyPaths.length - 1];
    }
    /**
     * Advance this cursor to a given value and the key path which
     * was used to reach it.
     *
     * Use this method to override the internal traversal logic of
     * the cursor as needed. Like `moveTo`, calls to this method can
     * be balanced with calls to `back`.
     *
     * @param value - The new value for the cursor to represent.
     * @param keyPath - The key path used to reach the value.
     */
    interject(value, keyPath) {
        this.values.push(value);
        this.keyPaths.push(keyPath);
    }
    /**
     * Reconfigure this cursor to traverse a given object.
     *
     * @param newRoot - The new root object to traverse.
     * @param keyPath - The key path specifying where the root object came from.
     * Typically this should be `thisKeyPath` (the default value for this parameter.)
     */
    reuse(newRoot, keyPath = key_path_1.thisKeyPath) {
        this.values.length = 0;
        this.values.push(newRoot);
        this.keyPaths.length = 0;
        this.keyPaths.push(keyPath);
        this.savedDepths.length = 0;
    }
    /**
     * Advance this cursor to a new position in the object it is traversing,
     * saving its previous position so that the cursor may be moved back.
     *
     * @param keyPath - A key path referring to a location in the cursor's current value.
     * @returns The new current value of the cursor.
     */
    moveTo(keyPath) {
        const newValue = (0, traverse_1.traverse)(this.currentValue, keyPath);
        this.values.push(newValue);
        this.keyPaths.push(keyPath);
        return newValue;
    }
    /**
     * Rewind this cursor to its previous position in the object it is traversing.
     */
    moveBack() {
        const currentDepth = this.values.length;
        if (currentDepth === 1) {
            throw new Error("Cannot move back past the root of a cursor");
        }
        const numberOfSaves = this.savedDepths.length;
        if (numberOfSaves > 0 && currentDepth <= this.savedDepths[numberOfSaves - 1]) {
            throw new Error("Cannot move back past the most recent saved state");
        }
        this.values.pop();
        this.keyPaths.pop();
    }
    /**
     * Save the current position of this cursor so that it may be restored later.
     *
     * Calls to this method must be balanced with a call to `restoreState`.
     */
    saveState() {
        this.savedDepths.push(this.values.length);
    }
    /**
     * Restore this cursor's position to a previously saved state.
     *
     * Use this method to balance a previous call to `saveState`.
     */
    restoreState() {
        const savedLength = this.savedDepths.pop();
        if ((0, optional_1.isNothing)(savedLength)) {
            throw new Error("Calls to restoreState must balance previous calls to saveState");
        }
        this.values.length = savedLength;
        this.keyPaths.length = savedLength;
    }
    // section Cloneable
    clone() {
        const copy = (0, clone_1.shallowCloneOf)(this);
        copy.values = this.values.slice();
        copy.keyPaths = this.keyPaths.slice();
        copy.savedDepths = this.savedDepths.slice();
        return copy;
    }
}
exports.ObjectCursor = ObjectCursor;
//# sourceMappingURL=object-cursor.js.map