"use strict";
/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
Object.defineProperty(exports, "__esModule", { value: true });
exports.isSegmentPresent = exports.wasRemovedOrMovedBefore = exports.wasMovedBefore = exports.wasRemovedBefore = exports.PerspectiveImpl = void 0;
const constants_js_1 = require("./constants.js");
const mergeTreeNodeWalk_js_1 = require("./mergeTreeNodeWalk.js");
const mergeTreeNodes_js_1 = require("./mergeTreeNodes.js");
const segmentInfos_js_1 = require("./segmentInfos.js");
/**
 * Implementation of {@link Perspective}.
 * @privateRemarks
 * TODO:AB#29765: This class does not support non-local-client perspectives, but should.
 */
class PerspectiveImpl {
    /**
     * @param _mergeTree - The {@link MergeTree} to view.
     * @param _seqTime - The latest sequence number and local sequence number to consider.
     */
    constructor(_mergeTree, _seqTime) {
        this._mergeTree = _mergeTree;
        this._seqTime = _seqTime;
    }
    /**
     * Returns the immediately adjacent segment in the specified direction from this perspective.
     * There may actually be multiple segments between the given segment and the returned segment,
     * but they were either inserted after this perspective, or have been removed or moved before this perspective.
     *
     * @param segment - The segment to start from.
     * @param forward - The direction to search.
     * @returns the next segment in the specified direction, or the start or end of the tree if there is no next segment.
     */
    nextSegment(segment, forward = true) {
        let next;
        const action = (seg) => {
            if (isSegmentPresent(seg, this._seqTime)) {
                next = seg;
                return mergeTreeNodeWalk_js_1.LeafAction.Exit;
            }
        };
        (forward ? mergeTreeNodeWalk_js_1.forwardExcursion : mergeTreeNodeWalk_js_1.backwardExcursion)(segment, action);
        return next ?? (forward ? this._mergeTree.endOfTree : this._mergeTree.startOfTree);
    }
    /**
     * Finds the segment prior to the given segment.
     * @param segment - The segment to start from.
     * @returns the previous segment, or the start of the tree if there is no previous segment.
     * @remarks This is a convenient equivalent to calling `nextSegment(segment, false)`.
     */
    previousSegment(segment) {
        return this.nextSegment(segment, false);
    }
}
exports.PerspectiveImpl = PerspectiveImpl;
/**
 * Determines if the given segment was removed before the given perspective.
 * @param seg - The segment to check.
 * @param seq - The latest sequence number to consider.
 * @param localSeq - The latest local sequence number to consider.
 * @returns true iff this segment was removed in the given perspective.
 * @privateRemarks
 * TODO:AB#29765: This function does not support non-local-client perspectives, but should.
 */
function wasRemovedBefore(seg, { refSeq, localSeq }) {
    if (seg.removedSeq === constants_js_1.UnassignedSequenceNumber &&
        localSeq !== undefined &&
        seg.localRemovedSeq !== undefined) {
        return seg.localRemovedSeq <= localSeq;
    }
    return seg.removedSeq !== undefined && (0, mergeTreeNodes_js_1.seqLTE)(seg.removedSeq, refSeq);
}
exports.wasRemovedBefore = wasRemovedBefore;
/**
 * Determines if the given segment was moved before the given perspective.
 * @param seg - The segment to check.
 * @param refSeq - The latest sequence number to consider.
 * @param localSeq - The latest local sequence number to consider.
 * @returns true iff this segment was moved (aka obliterated) in the given perspective.
 * @privateRemarks
 * TODO:AB#29765: This function does not support non-local-client perspectives, but should.
 */
function wasMovedBefore(seg, { refSeq, localSeq }) {
    if (seg.movedSeq === constants_js_1.UnassignedSequenceNumber &&
        localSeq !== undefined &&
        seg.localMovedSeq !== undefined) {
        return seg.localMovedSeq <= localSeq;
    }
    return seg.movedSeq !== undefined && (0, mergeTreeNodes_js_1.seqLTE)(seg.movedSeq, refSeq);
}
exports.wasMovedBefore = wasMovedBefore;
/**
 * See {@link wasRemovedBefore} and {@link wasMovedBefore}.
 * @privateRemarks
 * TODO:AB#29765: This function does not support non-local-client perspectives, but should.
 */
function wasRemovedOrMovedBefore(seg, seqTime) {
    return ((0, segmentInfos_js_1.isInserted)(seg) &&
        (((0, segmentInfos_js_1.isRemoved)(seg) && wasRemovedBefore(seg, seqTime)) ||
            ((0, segmentInfos_js_1.isMoved)(seg) && wasMovedBefore(seg, seqTime))));
}
exports.wasRemovedOrMovedBefore = wasRemovedOrMovedBefore;
/**
 * Determines if the given segment is present in the given perspective.
 * @param seg - The segment to check.
 * @param seqTime - The latest sequence number and local sequence number to consider.
 * @returns true iff this segment was inserted before the given perspective,
 * and it was not removed or moved in the given perspective.
 * @privateRemarks
 * TODO:AB#29765: This function does not support non-local-client perspectives, but should.
 */
function isSegmentPresent(seg, seqTime) {
    const { refSeq, localSeq } = seqTime;
    // If seg.seq is undefined, then this segment has existed since minSeq.
    // It may have been moved or removed since.
    if ((0, segmentInfos_js_1.isInserted)(seg)) {
        if (seg.seq !== constants_js_1.UnassignedSequenceNumber) {
            if (!(0, mergeTreeNodes_js_1.seqLTE)(seg.seq, refSeq)) {
                return false;
            }
        }
        else if (seg.localSeq !== undefined && // seg.seq === UnassignedSequenceNumber
            // If the current perspective does not include local sequence numbers,
            // then this segment does not exist yet.
            (localSeq === undefined || seg.localSeq > localSeq)) {
            return false;
        }
    }
    if (wasRemovedOrMovedBefore(seg, seqTime)) {
        return false;
    }
    return true;
}
exports.isSegmentPresent = isSegmentPresent;
//# sourceMappingURL=perspective.js.map