/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { AttributionKey } from "@fluidframework/runtime-definitions";
import { IAttributionCollection } from "./attributionCollection";
import { LocalReferenceCollection } from "./localReference";
import { IMergeTreeDeltaOpArgs } from "./mergeTreeDeltaCallback";
import { TrackingGroupCollection } from "./mergeTreeTracking";
import { ICombiningOp, IJSONSegment, IMarkerDef, ReferenceType } from "./ops";
import { PartialSequenceLengths } from "./partialLengths";
import { MapLike, PropertySet } from "./properties";
import { RangeStackMap, ReferencePosition } from "./referencePositions";
import { SegmentGroupCollection } from "./segmentGroupCollection";
import { PropertiesManager, PropertiesRollback } from "./segmentPropertiesManager";
/**
 * Common properties for a node in a merge tree.
 */
export interface IMergeNodeCommon {
    parent?: IMergeBlock;
    /**
     * The length of the contents of the node.
     */
    cachedLength: number;
    /**
     * The index of this node in its parent's list of children.
     */
    index: number;
    /**
     * A string that can be used for comparing the location of this node to other `MergeNode`s in the same tree.
     * `a.ordinal < b.ordinal` if and only if `a` comes before `b` in a pre-order traversal of the tree.
     */
    ordinal: string;
    isLeaf(): this is ISegment;
}
export declare type IMergeNode = IMergeBlock | ISegment;
/**
 * Internal (i.e. non-leaf) node in a merge tree.
 */
export interface IMergeBlock extends IMergeNodeCommon {
    needsScour?: boolean;
    /**
     * Number of direct children of this node
     */
    childCount: number;
    /**
     * Array of child nodes.
     *
     * @remarks To avoid reallocation, this is always initialized to have maximum length as deemed by
     * the merge tree's branching factor. Use `childCount` to determine how many children this node actually has.
     */
    children: IMergeNode[];
    /**
     * Supports querying the total length of all descendants of this IMergeBlock from the perspective of any
     * (clientId, seq) within the collab window.
     *
     * @remarks This is only optional for implementation reasons (internal nodes can be created/moved without
     * immediately initializing the partial lengths). Aside from mid-update on tree operations, these lengths
     * objects are always defined.
     */
    partialLengths?: PartialSequenceLengths;
    hierBlock(): IHierBlock | undefined;
    assignChild(child: IMergeNode, index: number, updateOrdinal?: boolean): void;
    setOrdinal(child: IMergeNode, index: number): void;
}
export interface IHierBlock extends IMergeBlock {
    hierToString(indentCount: number): string;
    rightmostTiles: MapLike<ReferencePosition>;
    leftmostTiles: MapLike<ReferencePosition>;
    rangeStacks: RangeStackMap;
}
/**
 * Contains removal information associated to an {@link ISegment}.
 */
export interface IRemovalInfo {
    /**
     * Local seq at which this segment was removed, if the removal is yet-to-be acked.
     */
    localRemovedSeq?: number;
    /**
     * Seq at which this segment was removed.
     */
    removedSeq: number;
    /**
     * List of client IDs that have removed this segment.
     * The client that actually removed the segment (i.e. whose removal op was sequenced first) is stored as the first
     * client in this list. Other clients in the list have all issued concurrent ops to remove the segment.
     * @remarks When this list has length \> 1, this is referred to as the "overlapping remove" case.
     */
    removedClientIds: number[];
}
export declare function toRemovalInfo(maybe: Partial<IRemovalInfo> | undefined): IRemovalInfo | undefined;
/**
 * A segment representing a portion of the merge tree.
 * Segments are leaf nodes of the merge tree and contain data.
 */
export interface ISegment extends IMergeNodeCommon, Partial<IRemovalInfo> {
    readonly type: string;
    readonly segmentGroups: SegmentGroupCollection;
    readonly trackingCollection: TrackingGroupCollection;
    /**
     * Stores attribution keys associated with offsets of this segment.
     * This data is only persisted if MergeTree's `attributions.track` flag is set to true.
     * Pending segments (i.e. ones that only exist locally and haven't been acked by the server) also have
     * `attribution === undefined` until ack.
     *
     * Keys can be used opaquely with an IAttributor or a container runtime that provides attribution.
     *
     * @alpha
     *
     * @remarks - There are plans to make the shape of the data stored extensible in a couple ways:
     *
     * 1. Injection of custom attribution information associated with the segment (ex: copy-paste of
     * content but keeping the old attribution information).
     * 2. Storage of multiple "channels" of information (ex: track property changes separately from insertion,
     * or only attribute certain property modifications, etc.)
     */
    attribution?: IAttributionCollection<AttributionKey>;
    /**
     * Manages pending local state for properties on this segment.
     */
    propertyManager?: PropertiesManager;
    /**
     * Local seq at which this segment was inserted. If this is defined, `seq` will be UnassignedSequenceNumber.
     * Once the segment is acked, this field is cleared.
     */
    localSeq?: number;
    /**
     * Local seq at which this segment was removed. If this is defined, `removedSeq` will initially be set to
     * UnassignedSequenceNumber. However, if another client concurrently removes the same segment, `removedSeq`
     * will be updated to the seq at which that client removed this segment.
     *
     * Like `localSeq`, this field is cleared once the local removal of the segment is acked.
     */
    localRemovedSeq?: number;
    /**
     * Seq at which this segment was inserted.
     * If undefined, it is assumed the segment was inserted prior to the collab window's minimum sequence number.
     */
    seq?: number;
    /**
     * Short clientId for the client that inserted this segment.
     */
    clientId: number;
    /**
     * Local references added to this segment.
     */
    localRefs?: LocalReferenceCollection;
    /**
     * Properties that have been added to this segment via annotation.
     */
    properties?: PropertySet;
    addProperties(newProps: PropertySet, op?: ICombiningOp, seq?: number, collabWindow?: CollaborationWindow, rollback?: PropertiesRollback): PropertySet | undefined;
    clone(): ISegment;
    canAppend(segment: ISegment): boolean;
    append(segment: ISegment): void;
    splitAt(pos: number): ISegment | undefined;
    toJSONObject(): any;
    /**
     * Acks the current segment against the segment group, op, and merge tree.
     *
     * @param segmentGroup - Pending segment group associated with this op.
     * @param opArgs - Information about the op that was acked
     * @returns - true if the op modifies the segment, otherwise false.
     * The only current false case is overlapping remove, where a segment is removed
     * by a previously sequenced operation before the current operation is acked.
     * @throws - error if the segment state doesn't match segment group or op.
     * E.g. if the segment group is not first in the pending queue, or
     * an inserted segment does not have unassigned sequence number.
     */
    ack(segmentGroup: SegmentGroup, opArgs: IMergeTreeDeltaOpArgs): boolean;
}
export interface IMarkerModifiedAction {
    (marker: Marker): void;
}
export interface ISegmentAction<TClientData> {
    (segment: ISegment, pos: number, refSeq: number, clientId: number, start: number, end: number, accum: TClientData): boolean;
}
export interface ISegmentChanges {
    next?: ISegment;
    replaceCurrent?: ISegment;
}
export interface BlockAction<TClientData> {
    (block: IMergeBlock, pos: number, refSeq: number, clientId: number, start: number | undefined, end: number | undefined, accum: TClientData): boolean;
}
export interface NodeAction<TClientData> {
    (node: IMergeNode, pos: number, refSeq: number, clientId: number, start: number | undefined, end: number | undefined, clientData: TClientData): boolean;
}
export interface IncrementalSegmentAction<TContext> {
    (segment: ISegment, state: IncrementalMapState<TContext>): any;
}
export interface IncrementalBlockAction<TContext> {
    (state: IncrementalMapState<TContext>): any;
}
export interface BlockUpdateActions {
    child: (block: IMergeBlock, index: number) => void;
}
export interface InsertContext {
    candidateSegment?: ISegment;
    prepareEvents?: boolean;
    structureChange?: boolean;
    leaf: (segment: ISegment | undefined, pos: number, ic: InsertContext) => ISegmentChanges;
    continuePredicate?: (continueFromBlock: IMergeBlock) => boolean;
}
export interface SegmentActions<TClientData> {
    leaf?: ISegmentAction<TClientData>;
    shift?: NodeAction<TClientData>;
    contains?: NodeAction<TClientData>;
    pre?: BlockAction<TClientData>;
    post?: BlockAction<TClientData>;
}
export interface IncrementalSegmentActions<TContext> {
    leaf: IncrementalSegmentAction<TContext>;
    pre?: IncrementalBlockAction<TContext>;
    post?: IncrementalBlockAction<TContext>;
}
export interface SearchResult {
    text: string;
    pos: number;
}
export interface SegmentGroup {
    segments: ISegment[];
    previousProps?: PropertySet[];
    localSeq: number;
    refSeq: number;
}
export declare class MergeNode implements IMergeNodeCommon {
    index: number;
    ordinal: string;
    parent?: IMergeBlock;
    cachedLength: number;
    isLeaf(): boolean;
}
export declare function ordinalToArray(ord: string): number[];
export declare const MaxNodesInBlock = 8;
export declare class MergeBlock extends MergeNode implements IMergeBlock {
    childCount: number;
    children: IMergeNode[];
    constructor(childCount: number);
    hierBlock(): IHierBlock | undefined;
    setOrdinal(child: IMergeNode, index: number): void;
    assignChild(child: IMergeNode, index: number, updateOrdinal?: boolean): void;
}
export declare abstract class BaseSegment extends MergeNode implements ISegment {
    clientId: number;
    seq: number;
    removedSeq?: number;
    removedClientIds?: number[];
    readonly segmentGroups: SegmentGroupCollection;
    readonly trackingCollection: TrackingGroupCollection;
    /**
     * @alpha
     */
    attribution?: IAttributionCollection<AttributionKey>;
    propertyManager?: PropertiesManager;
    properties?: PropertySet;
    localRefs?: LocalReferenceCollection;
    abstract readonly type: string;
    localSeq?: number;
    localRemovedSeq?: number;
    addProperties(newProps: PropertySet, op?: ICombiningOp, seq?: number, collabWindow?: CollaborationWindow, rollback?: PropertiesRollback): PropertySet | undefined;
    hasProperty(key: string): boolean;
    isLeaf(): boolean;
    protected cloneInto(b: ISegment): void;
    canAppend(segment: ISegment): boolean;
    protected addSerializedProps(jseg: IJSONSegment): void;
    abstract toJSONObject(): any;
    ack(segmentGroup: SegmentGroup, opArgs: IMergeTreeDeltaOpArgs): boolean;
    splitAt(pos: number): ISegment | undefined;
    private copyPropertiesTo;
    abstract clone(): ISegment;
    append(other: ISegment): void;
    protected abstract createSplitSegmentAt(pos: number): BaseSegment | undefined;
}
export declare const reservedMarkerIdKey = "markerId";
export declare const reservedMarkerSimpleTypeKey = "markerSimpleType";
export interface IJSONMarkerSegment extends IJSONSegment {
    marker: IMarkerDef;
}
export declare class Marker extends BaseSegment implements ReferencePosition {
    refType: ReferenceType;
    static readonly type = "Marker";
    static is(segment: ISegment): segment is Marker;
    readonly type = "Marker";
    static make(refType: ReferenceType, props?: PropertySet): Marker;
    constructor(refType: ReferenceType);
    toJSONObject(): IJSONMarkerSegment;
    static fromJSONObject(spec: any): Marker | undefined;
    clone(): Marker;
    getSegment(): this;
    getOffset(): number;
    hasSimpleType(simpleTypeName: string): boolean;
    getProperties(): PropertySet | undefined;
    getId(): string | undefined;
    toString(): string;
    protected createSplitSegmentAt(pos: number): undefined;
    canAppend(segment: ISegment): boolean;
    append(): void;
}
export declare enum IncrementalExecOp {
    Go = 0,
    Stop = 1,
    Yield = 2
}
export declare class IncrementalMapState<TContext> {
    block: IMergeBlock;
    actions: IncrementalSegmentActions<TContext>;
    pos: number;
    refSeq: number;
    clientId: number;
    context: TContext;
    start: number;
    end: number;
    childIndex: number;
    op: IncrementalExecOp;
    constructor(block: IMergeBlock, actions: IncrementalSegmentActions<TContext>, pos: number, refSeq: number, clientId: number, context: TContext, start: number, end: number, childIndex?: number);
}
export declare class CollaborationWindow {
    clientId: number;
    collaborating: boolean;
    minSeq: number;
    currentSeq: number;
    localSeq: number;
    loadFrom(a: CollaborationWindow): void;
}
export declare const compareNumbers: (a: number, b: number) => number;
export declare const compareStrings: (a: string, b: string) => number;
export declare function internedSpaces(n: number): string;
export interface IConsensusInfo {
    marker: Marker;
    callback: (m: Marker) => void;
}
export interface SegmentAccumulator {
    segments: ISegment[];
}
export interface MinListener {
    minRequired: number;
    onMinGE(minSeq: number): void;
}
export declare function debugMarkerToString(marker: Marker): string;
//# sourceMappingURL=mergeTreeNodes.d.ts.map