/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { Heap } from "@fluidframework/core-utils";
import { IAttributionCollectionSerializer } from "./attributionCollection";
import { DoublyLinkedList } from "./collections";
import { LocalReferencePosition, SlidingPreference } from "./localReference";
import { CollaborationWindow, IMergeBlock, IMergeLeaf, IMergeNode, ISegment, ISegmentAction, Marker, MergeBlock, SegmentGroup } from "./mergeTreeNodes";
import { IMergeTreeDeltaOpArgs, MergeTreeDeltaCallback, MergeTreeMaintenanceCallback } from "./mergeTreeDeltaCallback";
import { IMergeTreeDeltaOp, IRelativePosition, ReferenceType } from "./ops";
import { PropertySet } from "./properties";
import { ReferencePosition } from "./referencePositions";
import { PropertiesRollback } from "./segmentPropertiesManager";
import { Client } from "./client";
import { EndOfTreeSegment, StartOfTreeSegment } from "./endOfTreeSegment";
/**
 * someday we may split tree leaves from segments, but for now they are the same
 * this is just a convenience type that makes it clear that we need something that is both a segment and a leaf node
 */
export type ISegmentLeaf = ISegment & IMergeLeaf;
/**
 * @alpha
 */
export interface IMergeTreeOptions {
    catchUpBlobName?: string;
    /**
     * Whether or not reference positions can slide to special endpoint segments
     * denoting the positions immediately before the start and immediately after
     * the end of the string.
     *
     * This is primarily useful in the case of interval stickiness.
     */
    mergeTreeReferencesCanSlideToEndpoint?: boolean;
    mergeTreeSnapshotChunkSize?: number;
    /**
     * Whether to use the SnapshotV1 format over SnapshotLegacy.
     *
     * SnapshotV1 stores a view of the merge-tree at the current sequence number, preserving merge metadata
     * (e.g. clientId, seq, etc.) only for segment changes within the collab window.
     *
     * SnapshotLegacy stores a view of the merge-tree at the minimum sequence number along with the ops between
     * the minimum sequence number and the current sequence number.
     *
     * Both formats merge segments where possible (see {@link ISegment.canAppend})
     *
     * default: false
     *
     * @remarks
     * Despite the "legacy"/"V1" naming, both formats are actively used at the time of writing. SharedString
     * uses legacy and Matrix uses V1.
     */
    newMergeTreeSnapshotFormat?: boolean;
    /**
     * Options related to attribution
     */
    attribution?: IMergeTreeAttributionOptions;
    /**
     * Enables support for the obliterate operation -- a stronger form of remove
     * which deletes concurrently inserted segments
     *
     * Obliterate is currently experimental and may not work in all scenarios.
     *
     * Default value: false
     */
    mergeTreeEnableObliterate?: boolean;
}
/**
 * @alpha
 */
export interface IMergeTreeAttributionOptions {
    /**
     * If enabled, segments will store attribution keys which can be used with the runtime to determine
     * attribution information (i.e. who created the content and when it was created).
     *
     * This flag only applied to new documents: if a snapshot is loaded, whether or not attribution keys
     * are tracked is determined by the presence of existing attribution keys in the snapshot.
     *
     * default: false
     */
    track?: boolean;
    /**
     * Provides a policy for how to track attribution data on segments.
     * This option must be provided if either:
     * - `track` is set to true
     * - a document containing existing attribution information is loaded
     */
    policyFactory?: () => AttributionPolicy;
}
/**
 * Implements policy dictating which kinds of operations should be attributed and how.
 * @sealed
 * @alpha
 */
export interface AttributionPolicy {
    /**
     * Enables tracking attribution information for operations on this merge-tree.
     * This function is expected to subscribe to appropriate change events in order
     * to manage any attribution data it stores on segments.
     *
     * This must be done in an eventually consistent fashion.
     */
    attach: (client: Client) => void;
    /**
     * Disables tracking attribution information on segments.
     */
    detach: () => void;
    /***/
    isAttached: boolean;
    /**
     * Serializer capable of serializing any attribution data this policy stores on segments.
     */
    serializer: IAttributionCollectionSerializer;
}
/**
 * @internal
 */
export interface LRUSegment {
    segment?: ISegmentLeaf;
    maxSeq: number;
}
export interface IRootMergeBlock extends IMergeBlock {
    mergeTree?: MergeTree;
}
export declare function findRootMergeBlock(segmentOrNode: IMergeNode | undefined): IRootMergeBlock | undefined;
/**
 * Returns the position to slide a reference to if a slide is required.
 * @param segoff - The segment and offset to slide from
 * @returns segment and offset to slide the reference to
 * @internal
 */
export declare function getSlideToSegoff(segoff: {
    segment: ISegment | undefined;
    offset: number | undefined;
}, slidingPreference?: SlidingPreference, useNewSlidingBehavior?: boolean): {
    segment: ISegment | undefined;
    offset: number | undefined;
};
/**
 * @internal
 */
export declare class MergeTree {
    options?: IMergeTreeOptions | undefined;
    static readonly options: {
        incrementalUpdate: boolean;
        insertAfterRemovedSegs: boolean;
        zamboniSegments: boolean;
    };
    private static readonly theUnfinishedNode;
    readonly collabWindow: CollaborationWindow;
    readonly pendingSegments: DoublyLinkedList<SegmentGroup>;
    readonly segmentsToScour: Heap<LRUSegment>;
    readonly attributionPolicy: AttributionPolicy | undefined;
    /**
     * Whether or not all blocks in the mergeTree currently have information about local partial lengths computed.
     * This information is only necessary on reconnect, and otherwise costly to bookkeep.
     * This field enables tracking whether partials need to be recomputed using localSeq information.
     */
    private localPartialsComputed;
    private readonly idToMarker;
    mergeTreeDeltaCallback?: MergeTreeDeltaCallback;
    mergeTreeMaintenanceCallback?: MergeTreeMaintenanceCallback;
    /**
     * Array containing the sequence number of all move operations within the
     * collab window
     *
     * When a segment is inserted, we must traverse to the left and right of it
     * to determine whether the segment was inserted into an obliterated range.
     * By keeping track of all move seqs, we can significantly reduce the search
     * space we must traverse.
     *
     * Sequence numbers in `moveSeqs` are sorted to accelerate bookkeeping.
     *
     * See https://github.com/microsoft/FluidFramework/blob/main/packages/dds/merge-tree/docs/Obliterate.md#remote-perspective
     * for additional context
     */
    private moveSeqs;
    /**
     * Similar to moveSeqs, but tracks local moves. These are not the move
     * operations within the collab window, but rather local moves that have
     * not been acked.
     */
    private readonly localMoveSeqs;
    /**
     * Groups of segments moved by local moves/obliterates
     *
     * When a local obliterate is acked, we must also ack segments that were
     * concurrently obliterated on insert. We check this segment group to find
     * such segments
     */
    private readonly locallyMovedSegments;
    constructor(options?: IMergeTreeOptions | undefined);
    private _root;
    get root(): IRootMergeBlock;
    set root(value: IRootMergeBlock);
    makeBlock(childCount: number): MergeBlock;
    /**
     * Compute the net length of this segment from a local perspective.
     * @param segment - Segment whose length to find
     * @param localSeq - localSeq at which to find the length of this segment. If not provided,
     * default is to consider the local client's current perspective. Only local sequence
     * numbers corresponding to un-acked operations give valid results.
     */
    localNetLength(segment: ISegment, refSeq?: number, localSeq?: number): number | undefined;
    unlinkMarker(marker: Marker): void;
    private addNode;
    reloadFromSegments(segments: ISegment[]): void;
    startCollaboration(localClientId: number, minSeq: number, currentSeq: number): void;
    private addToLRUSet;
    getLength(refSeq: number, clientId: number): number;
    /**
     * Returns the current length of the MergeTree for the local client.
     */
    get length(): number | undefined;
    getPosition(node: IMergeNode, refSeq: number, clientId: number, localSeq?: number): number;
    getContainingSegment<T extends ISegment>(pos: number, refSeq: number, clientId: number, localSeq?: number): {
        segment: T | undefined;
        offset: number | undefined;
    };
    /**
     * Slides or removes references from the provided list of segments.
     *
     * The order of the references is preserved for references of the same sliding
     * preference. Relative order between references that slide backward and those
     * that slide forward is not preserved, even in the case when they slide to
     * the same segment.
     *
     * @remarks
     *
     * 1. Preserving the order of the references is a useful property for reference-based undo/redo
     * (see revertibles.ts).
     *
     * 2. For use cases which necessitate eventual consistency across clients,
     * this method should only be called with segments for which the current client sequence number is
     * max(remove segment sequence number, add reference sequence number).
     * See `packages\dds\merge-tree\REFERENCEPOSITIONS.md`
     *
     * @param segments - An array of (not necessarily contiguous) segments with increasing ordinals.
     */
    private slideAckedRemovedSegmentReferences;
    private blockLength;
    /**
     * Compute local partial length information
     *
     * Public only for use by internal tests
     */
    computeLocalPartials(refSeq: number): void;
    private nodeLength;
    setMinSeq(minSeq: number): void;
    referencePositionToLocalPosition(refPos: ReferencePosition, refSeq?: number, clientId?: number): number;
    /**
     * Finds the nearest reference with ReferenceType.Tile to `startPos` in the direction dictated by `tilePrecedesPos`.
     * @deprecated Use searchForMarker instead.
     *
     * @param startPos - Position at which to start the search
     * @param clientId - clientId dictating the perspective to search from
     * @param tileLabel - Label of the tile to search for
     * @param tilePrecedesPos - Whether the desired tile comes before (true) or after (false) `startPos`
     */
    findTile(startPos: number, clientId: number, tileLabel: string, tilePrecedesPos?: boolean): {
        tile: ReferencePosition;
        pos: number;
    } | undefined;
    /**
     * Finds the nearest reference with ReferenceType.Tile to `startPos` in the direction dictated by `forwards`.
     * Uses depthFirstNodeWalk in addition to block-accelerated functionality. The search position will be included in
     * the nodes to walk, so searching on all positions, including the endpoints, can be considered inclusive.
     * Any out of bound search positions will return undefined, so in order to search the whole string, a forward
     * search can begin at 0, or a backward search can begin at length-1.
     *
     * @param startPos - Position at which to start the search
     * @param clientId - clientId dictating the perspective to search from
     * @param markerLabel - Label of the marker to search for
     * @param forwards - Whether the string should be searched in the forward or backward direction
     */
    searchForMarker(startPos: number, clientId: number, markerLabel: string, forwards?: boolean): Marker | undefined;
    private search;
    private searchBlock;
    private backwardSearch;
    private backwardSearchBlock;
    private updateRoot;
    /**
     * Assign sequence number to existing segment; update partial lengths to reflect the change
     * @param seq - sequence number given by server to pending segment
     */
    ackPendingSegment(opArgs: IMergeTreeDeltaOpArgs): void;
    private addToPendingList;
    getMarkerFromId(id: string): Marker | undefined;
    /**
     * Given a position specified relative to a marker id, lookup the marker
     * and convert the position to a character position.
     * @param relativePos - Id of marker (may be indirect) and whether position is before or after marker.
     * @param refseq - The reference sequence number at which to compute the position.
     * @param clientId - The client id with which to compute the position.
     */
    posFromRelativePos(relativePos: IRelativePosition, refseq?: number, clientId?: number): number;
    insertSegments(pos: number, segments: ISegment[], refSeq: number, clientId: number, seq: number, opArgs: IMergeTreeDeltaOpArgs | undefined): void;
    /**
     * Resolves a remote client's position against the local sequence
     * and returns the remote client's position relative to the local
     * sequence. The client ref seq must be above the minimum sequence number
     * or the return value will be undefined.
     * Generally this method is used in conjunction with signals which provide
     * point in time values for the below parameters, and is useful for things
     * like displaying user position. It should not be used with persisted values
     * as persisted values will quickly become invalid as the remoteClientRefSeq
     * moves below the minimum sequence number
     * @param remoteClientPosition - The remote client's position to resolve
     * @param remoteClientRefSeq - The reference sequence number of the remote client
     * @param remoteClientId - The client id of the remote client
     */
    resolveRemoteClientPosition(remoteClientPosition: number, remoteClientRefSeq: number, remoteClientId: number): number | undefined;
    private blockInsert;
    private readonly splitLeafSegment;
    private ensureIntervalBoundary;
    private breakTie;
    private getSmallestSeqMoveOp;
    private insertingWalk;
    private split;
    nodeUpdateOrdinals(block: IMergeBlock): void;
    /**
     * Annotate a range with properties
     * @param start - The inclusive start position of the range to annotate
     * @param end - The exclusive end position of the range to annotate
     * @param props - The properties to annotate the range with
     * @param refSeq - The reference sequence number to use to apply the annotate
     * @param clientId - The id of the client making the annotate
     * @param seq - The sequence number of the annotate operation
     * @param opArgs - The op args for the annotate op. this is passed to the merge tree callback if there is one
     * @param rollback - Whether this is for a local rollback and what kind
     */
    annotateRange(start: number, end: number, props: PropertySet, refSeq: number, clientId: number, seq: number, opArgs: IMergeTreeDeltaOpArgs, rollback?: PropertiesRollback): void;
    /**
     * @alpha
     */
    obliterateRange(start: number, end: number, refSeq: number, clientId: number, seq: number, overwrite: boolean | undefined, opArgs: IMergeTreeDeltaOpArgs): void;
    markRangeRemoved(start: number, end: number, refSeq: number, clientId: number, seq: number, overwrite: boolean | undefined, opArgs: IMergeTreeDeltaOpArgs): void;
    /**
     * Revert an unacked local op
     */
    rollback(op: IMergeTreeDeltaOp, localOpMetadata: SegmentGroup): void;
    /**
     * Walk the segments up to the current segment and calculate its position
     */
    private findRollbackPosition;
    nodeUpdateLengthNewStructure(node: IMergeBlock, recur?: boolean): void;
    removeLocalReferencePosition(lref: LocalReferencePosition): LocalReferencePosition | undefined;
    startOfTree: StartOfTreeSegment;
    endOfTree: EndOfTreeSegment;
    createLocalReferencePosition(_segment: ISegmentLeaf | "start" | "end", offset: number, refType: ReferenceType, properties: PropertySet | undefined, slidingPreference?: SlidingPreference, canSlideToEndpoint?: boolean): LocalReferencePosition;
    private normalizeAdjacentSegments;
    /**
     * Normalizes the segments nearby `segmentGroup` to be ordered as they would if the op submitting `segmentGroup`
     * is rebased to the current sequence number.
     * This primarily affects the ordering of adjacent segments that were removed between the original submission of
     * the local ops and now.
     * Consider the following sequence of events:
     * Initial state: "hi my friend" (seq: 0)
     * - Client 1 inserts "good " to make "hi my good friend" (op1, refSeq: 0)
     * - Client 2 deletes "my " to make "hi friend" (op2, refSeq: 0)
     * - op2 is sequenced giving seq 1
     * - Client 1 disconnects and reconnects at seq: 1.
     *
     * At this point in time, client 1 will have segments ["hi ", Removed"my ", Local"good ", "friend"].
     * However, the rebased op that it submits will cause client 2 to have segments
     * ["hi ", Local"good ", Removed"my ", "friend"].
     *
     * The difference in ordering can be problematic for tie-breaking concurrently inserted segments in some scenarios.
     * Rather than incur extra work tie-breaking these scenarios for all clients, when client 1 rebases its operation,
     * it can fix up its local state to align with what would be expected of the op it resubmits.
     */
    normalizeSegmentsOnRebase(): void;
    private addNodeReferences;
    private blockUpdate;
    blockUpdatePathLengths(startBlock: IMergeBlock | undefined, seq: number, clientId: number, newStructure?: boolean): void;
    private blockUpdateLength;
    /**
     * Map over all visible segments in a given range
     *
     * A segment is visible if its length is greater than 0
     *
     * See `this.nodeMap` for additional documentation
     */
    mapRange<TClientData>(handler: ISegmentAction<TClientData>, refSeq: number, clientId: number, accum: TClientData, start?: number, end?: number, splitRange?: boolean, visibilitySeq?: number): void;
    /**
     * Map over all visible segments in a given range
     *
     * A segment is visible if its length is greater than 0
     *
     * @param refSeq - The sequence number used to determine the range (start
     * and end positions) of segments to iterate over.
     *
     * @param visibilitySeq - An additional sequence number to further configure
     * segment visibility during traversal. This is the same as refSeq, except
     * in the case of obliterate.
     *
     * In the case where `refSeq == visibilitySeq`, mapping is done on all
     * visible segments from `start` to `end`.
     *
     * If a segment is invisible at both `visibilitySeq` and `refSeq`, then it
     * will not be traversed and mapped. Otherwise, if the segment is visible at
     * either seq, it will be mapped.
     *
     * If a segment is only visible at `visibilitySeq`, it will still be mapped,
     * but it will not count as a segment within the range. That is, it will be
     * ignored for the purposes of tracking when traversal should end.
     */
    private nodeMap;
}
//# sourceMappingURL=mergeTree.d.ts.map