/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { NonCollabClient } from "./constants.js";
import { seqLTE } from "./mergeTreeNodes.js";
import { isInserted, isRemoved } from "./segmentInfos.js";
import * as opstampUtils from "./stamps.js";
class PerspectiveBase {
    isSegmentPresent(seg) {
        if (isInserted(seg) && !this.hasOccurred(seg.insert)) {
            return false;
        }
        if (isRemoved(seg) && seg.removes.some((remove) => this.hasOccurred(remove))) {
            return false;
        }
        return true;
    }
}
/**
 * A perspective which includes edits at or before some reference sequence number alongside all edits from some particular client.
 *
 * @remarks
 * This works for both the local client as well as remote clients since refSeq-based checks disallow unacked edits, but the clientId check
 * catches unacked edits from the local client.
 */
export class PriorPerspective extends PerspectiveBase {
    constructor(refSeq, clientId) {
        super();
        this.refSeq = refSeq;
        this.clientId = clientId;
    }
    hasOccurred(stamp) {
        const predatesViaRefSeq = seqLTE(stamp.seq, this.refSeq);
        const predatesViaSameClient = stamp.clientId === this.clientId;
        return predatesViaRefSeq || predatesViaSameClient;
    }
}
/**
 * A perspective which includes edits which were either:
 * - acked and at or before some reference sequence number
 * - unacked, but at or before some local sequence number
 *
 * This is a useful perspective when the local client is in the process of reconnecting, since it must
 * rederive positions for unacked ops while only considering a portion of its own edits as having been applied.
 */
export class LocalReconnectingPerspective extends PerspectiveBase {
    constructor(refSeq, clientId, localSeq) {
        super();
        this.refSeq = refSeq;
        this.clientId = clientId;
        this.localSeq = localSeq;
    }
    hasOccurred(stamp) {
        const predatesViaRefSeq = seqLTE(stamp.seq, this.refSeq);
        const predatesViaLocalSeq = stamp.localSeq !== undefined && stamp.localSeq <= this.localSeq;
        return predatesViaRefSeq || predatesViaLocalSeq;
    }
}
/**
 * This perspective is used when rebasing obliterate endpoints to find the segment to slide to when squash is enabled.
 *
 * TODO:AB#39357: This class would not be necessary if obliterate rebasing occurred as resubmit was called rather than
 * precomputed before segment normalization. It also adds more dependencies on all ops being resubmitted (the squash
 * parameter coming from rebasing an obliterate does not necessarily align with an inserted segment), which is not
 * fully correct.
 */
export class LocalSquashPerspective extends LocalReconnectingPerspective {
    constructor(refSeq, clientId, localSeq) {
        super(refSeq, clientId, localSeq);
        this.refSeq = refSeq;
        this.clientId = clientId;
        this.localSeq = localSeq;
    }
    isSegmentPresent(seg) {
        // Avoid sliding to segments whose insertion will be squashed.
        if (isInserted(seg) && opstampUtils.isLocal(seg.insert) && isRemoved(seg)) {
            return false;
        }
        return super.isSegmentPresent(seg);
    }
}
/**
 * A perspective which includes edits which were either:
 * - acked and at or before some reference sequence number
 * - unacked, but at or before some local sequence number
 *
 * @internal
 */
export function createLocalReconnectingPerspective(refSeq, clientId, localSeq, squash = false) {
    return new (squash ? LocalSquashPerspective : LocalReconnectingPerspective)(refSeq, clientId, localSeq);
}
/**
 * A perspective which includes all known edits.
 *
 * This is the perspective that the application sees.
 * @remarks
 * This can be represented using {@link PriorPerspective} with a refSeq of `Number.MAX_SAFE_INTEGER`, but having an explicit
 * variant of this perspective renders extra refSeq checks unnecessary and is a bit easier to read.
 */
export class LocalDefaultPerspective extends PerspectiveBase {
    constructor(clientId) {
        super();
        this.clientId = clientId;
        this.refSeq = Number.MAX_SAFE_INTEGER;
    }
    hasOccurred(_stamp) {
        return true;
    }
}
/**
 * A perspective dictating whether segments are 'visible' to a remote obliterate operation.
 *
 * NOTE: Beware that partial lengths doesn't support this perspective, in the sense that consulting partial lengths' for the length of a block
 * can give different results than summing the lengths of present segments in that block.
 * This ends up not affecting the current obliterate implementation (which has some special casing in the mapRange calls it uses),
 * but use with caution.
 */
export class RemoteObliteratePerspective extends PerspectiveBase {
    constructor(clientId) {
        super();
        this.clientId = clientId;
        this.refSeq = Number.MAX_SAFE_INTEGER;
    }
    hasOccurred(stamp) {
        // Local-only removals are not visible to an obliterate operation, since this means the local removal was concurrent
        // to a remote obliterate and we may need to mark the segment appropriately to reflect this overlapping remove.
        // Every other type of operation is visible: obliterates do not affect segments that have already been removed and acked,
        // and they always affect segments within their range that have not been removed, even if those segments were inserted
        // after the obliterate's refSeq.
        if (isRemoveOperationStamp(stamp) && opstampUtils.isLocal(stamp)) {
            return false;
        }
        return true;
    }
}
function isRemoveOperationStamp(stamp) {
    const { type } = stamp;
    return type === "setRemove" || type === "sliceRemove";
}
export const allAckedChangesPerspective = new PriorPerspective(Number.MAX_SAFE_INTEGER, NonCollabClient);
//# sourceMappingURL=perspective.js.map