/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { UnassignedSequenceNumber } from "./constants.js";
import { LeafAction, backwardExcursion, forwardExcursion } from "./mergeTreeNodeWalk.js";
import { seqLTE } from "./mergeTreeNodes.js";
import { isInserted, isMoved, isRemoved, } from "./segmentInfos.js";
/**
 * Implementation of {@link Perspective}.
 * @privateRemarks
 * TODO:AB#29765: This class does not support non-local-client perspectives, but should.
 */
export 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 LeafAction.Exit;
            }
        };
        (forward ? forwardExcursion : 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);
    }
}
/**
 * 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.
 */
export function wasRemovedBefore(seg, { refSeq, localSeq }) {
    if (seg.removedSeq === UnassignedSequenceNumber &&
        localSeq !== undefined &&
        seg.localRemovedSeq !== undefined) {
        return seg.localRemovedSeq <= localSeq;
    }
    return seg.removedSeq !== undefined && seqLTE(seg.removedSeq, refSeq);
}
/**
 * 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.
 */
export function wasMovedBefore(seg, { refSeq, localSeq }) {
    if (seg.movedSeq === UnassignedSequenceNumber &&
        localSeq !== undefined &&
        seg.localMovedSeq !== undefined) {
        return seg.localMovedSeq <= localSeq;
    }
    return seg.movedSeq !== undefined && seqLTE(seg.movedSeq, refSeq);
}
/**
 * See {@link wasRemovedBefore} and {@link wasMovedBefore}.
 * @privateRemarks
 * TODO:AB#29765: This function does not support non-local-client perspectives, but should.
 */
export function wasRemovedOrMovedBefore(seg, seqTime) {
    return (isInserted(seg) &&
        ((isRemoved(seg) && wasRemovedBefore(seg, seqTime)) ||
            (isMoved(seg) && wasMovedBefore(seg, seqTime))));
}
/**
 * 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.
 */
export 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 (isInserted(seg)) {
        if (seg.seq !== UnassignedSequenceNumber) {
            if (!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;
}
//# sourceMappingURL=perspective.js.map