/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import type { Listenable } from "@fluidframework/core-interfaces/internal";
import { type Brand, type BrandedKey, type BrandedMapSubset, type Opaque, ReferenceCountedBase } from "../../util/index.js";
import type { FieldKey } from "../schema-stored/index.js";
import type { UpPath } from "./pathTree.js";
import type { DeltaVisitor } from "./visitDelta.js";
import type { PathVisitor } from "./visitPath.js";
import type { AnnouncedVisitor } from "./visitorUtils.js";
/**
 * A way to refer to a particular tree location within an {@link AnchorSet}.
 * Associated with a ref count on the underlying {@link AnchorNode}.
 */
export type Anchor = Brand<number, "rebaser.Anchor">;
/**
 * Maps anchors (which must be ones this locator knows about) to paths.
 */
export interface AnchorLocator {
    /**
     * Get the current location of an Anchor.
     * The returned value should not be used after an edit has occurred.
     *
     * TODO: support extra/custom return types for specific/custom anchor types:
     * for now caller must rely on data in anchor + returned node location
     * (not ideal for anchors for places or ranges instead of nodes).
     */
    locate(anchor: Anchor): AnchorNode | undefined;
}
/**
 * Stores arbitrary, user-defined data on an {@link Anchor}.
 * This data is preserved over the course of that anchor's lifetime.
 * @see {@link anchorSlot} for creation and an example use case.
 */
export type AnchorSlot<TContent> = BrandedKey<Opaque<Brand<number, "AnchorSlot">>, TContent>;
/**
 * Events for {@link AnchorNode}.
 * These events are triggered while the internal data structures are being updated.
 * Thus these events must not trigger reading of the anchorSet or forest.
 *
 * TODO:
 * - Include sub-deltas in events.
 * - Add more events.
 */
export interface AnchorEvents {
    /**
     * When the anchor node will never get reused by its AnchorSet.
     * This means that the content it corresponds to has been permanently destroyed.
     *
     * @remarks
     * When this happens depends entirely on how the anchorSet is used.
     * It's possible nodes removed from the tree will be kept indefinitely, and thus never trigger this event, or they may be discarded immediately.
     *
     * @privateRemarks
     * The specifics of the delta visit algorithm can impact the behavior of these events.
     * Refer to the privateRemarks of specific events and/or the documentation of the delta visit algorithm (as of
     * 2024-04-02, src/core/tree/visitDelta.ts) for more information.
     */
    afterDestroy(anchor: AnchorNode): void;
    /**
     * Emitted in the middle of applying a batch of changes (i.e. during a delta a visit), if one or more of this node's
     * direct children are about to change due to updates from the batch.
     *
     * @remarks
     * Does not include edits of child subtrees: instead only includes changes to nodes which are direct children in this
     * node's fields.
     */
    childrenChanging(anchor: AnchorNode): void;
    /**
     * Emitted in the middle of applying a batch of changes (i.e. during a delta a visit), if one or more of this node's
     * direct children just changed due to updates from the batch.
     *
     * @remarks
     * Does not include edits of child subtrees: instead only includes changes to nodes which are direct children in this
     * node's fields.
     *
     * Compare to {@link AnchorEvents.childrenChangedAfterBatch} which is emitted after the whole batch has been applied.
     */
    childrenChanged(anchor: AnchorNode): void;
    /**
     * Emitted after a batch of changes has been applied (i.e. when a delta visit completes), if one or more of this node's
     * direct children changed due to updates from the batch.
     *
     * @remarks
     * Does not include edits of child subtrees: instead only includes changes to nodes which are direct children in this
     * node's fields.
     *
     * This event is guaranteed to be emitted on a given node only once per batch.
     *
     * Compare to {@link AnchorEvents.childrenChanged} which is emitted in the middle of the batch/delta-visit.
     */
    childrenChangedAfterBatch(arg: {
        changedFields: ReadonlySet<FieldKey>;
    }): void;
    /**
     * Emitted in the middle of applying a batch of changes (i.e. during a delta a visit), if something in the subtree
     * rooted at `anchor` _may_ be about to change due to updates from the batch.
     *
     * @remarks
     * The event can optionally return a {@link PathVisitor} to traverse the subtree.
     * Called on every parent (transitively) when a change is occurring.
     */
    subtreeChanging(anchor: AnchorNode): PathVisitor | void;
    /**
     * Emitted in the middle of applying a batch of changes (i.e. during a delta a visit), if something in the subtree
     * rooted at `anchor` _may_ have just changed due to updates from the batch.
     *
     * @remarks
     * While this event is always emitted in the presence of changes to the subtree,
     * it may also be emitted even though no changes have been made to the subtree.
     * It may be emitted multiple times within the application of a single edit or transaction.
     *
     * If this event is emitted by a node, it will later be emitted by all its ancestors up to the root as well, at
     * least once on each ancestor.
     *
     * Compare to {@link AnchorEvents.subtreeChangedAfterBatch} which is emitted after the whole batch has been applied.
     *
     * @privateRemarks
     * The delta visit algorithm is complicated and it may fire this event multiple times for the same change to a node.
     * The change to the tree may not be visible until the event fires for the last time.
     * Refer to the documentation of the delta visit algorithm for more details.
     */
    subtreeChanged(anchor: AnchorNode): void;
    /**
     * Emitted after a batch of changes has been applied (i.e. when a delta visit completes), if something in the subtree
     * rooted at `anchor` changed due to updates from the batch.
     *
     * @remarks
     * If this event is emitted by a node, it will later be emitted by all its ancestors up to the root as well, from bottom to top.
     *
     * This event is guaranteed to be emitted on a given node only once per batch.
     *
     * Compare to {@link AnchorEvents.subtreeChanged} which is emitted in the middle of the batch/delta-visit.
     *
     * @privateRemarks
     * Note that because this is fired after the full batch of changes is applied, it guarantees that something in the
     * subtree changed, compared to {@link AnchorEvents.subtreeChanged} or {@link AnchorEvents.subtreeChanging} which
     * fire when something _may_ have changed or _may_ be about to change.
     */
    subtreeChangedAfterBatch(): void;
}
/**
 * Events for {@link AnchorSet}.
 * These events are triggered while the internal data structures are being updated.
 * Thus these events must not trigger reading of the anchorSet or forest.
 *
 * TODO:
 * - Design how events should be ordered.
 * - Include sub-deltas in events.
 * - Add more events.
 */
export interface AnchorSetRootEvents {
    /**
     * What children are at the root is changing.
     */
    childrenChanging(anchors: AnchorSet): void;
    /**
     * Something in the tree is changing.
     */
    treeChanging(anchors: AnchorSet): void;
}
/**
 * Node in a tree of anchors.
 */
export interface AnchorNode extends UpPath<AnchorNode> {
    /**
     * Events for this anchor node.
     */
    readonly events: Listenable<AnchorEvents>;
    /**
     * Allows access to data stored on the Anchor in "slots".
     * Use {@link anchorSlot} to create slots.
     */
    readonly slots: BrandedMapSubset<AnchorSlot<any>>;
    /**
     * The set this anchor node is part of.
     */
    readonly anchorSet: AnchorSet;
    /**
     * Gets a child of this node.
     *
     * @remarks
     * This does not return an AnchorNode since there might not be one, and lazily creating one here would have messy lifetime management (See {@link AnchorNode#getOrCreateChildRef})
     * If an AnchorNode is required, use the AnchorSet to track then locate the returned path.
     * TODO:
     * Revisit this API.
     * Perhaps if we use weak down pointers and remove ref counting, we can make this return a AnchorNode.
     *
     */
    child(key: FieldKey, index: number): UpPath<AnchorNode>;
    /**
     * Gets the child AnchorNode if already exists.
     *
     * Does NOT add a ref, so the returned AnchorNode must be used with care.
     */
    childIfAnchored(key: FieldKey, index: number): AnchorNode | undefined;
    /**
     * Gets a child AnchorNode (creating it if needed), and an Anchor owning a ref to it.
     * Caller is responsible for freeing the returned Anchor, and must not use the AnchorNode after that.
     */
    getOrCreateChildRef(key: FieldKey, index: number): [Anchor, AnchorNode];
}
/**
 * Define a strongly typed slot on anchors in which data can be stored.
 *
 * @remarks
 * This is mainly useful for caching data associated with a location in the tree.
 *
 * Example usage:
 * ```typescript
 * const counterSlot = anchorSlot<number>();
 *
 * function useSlot(anchor: AnchorNode): void {
 * 	anchor.slots.set(counterSlot, 1 + anchor.slots.get(counterSlot) ?? 0);
 * }
 * ```
 */
export declare function anchorSlot<TContent>(): AnchorSlot<TContent>;
/**
 * Collection of Anchors at a specific revision.
 *
 * See `Rebaser` for how to update across revisions.
 *
 * TODO: this should not be package exported.
 * If it's needed outside the package an Interface should be used instead which can reduce its
 * API surface to a small subset.
 *
 * @sealed
 */
export declare class AnchorSet implements AnchorLocator {
    #private;
    readonly events: Listenable<AnchorSetRootEvents>;
    /**
     * Incrementing counter to give each anchor in this set a unique index for its identifier.
     * "0" is reserved for the `NeverAnchor`.
     */
    private anchorCounter;
    /**
     * Incrementing number that is bumped each time that the {@link AnchorSet} is changed.
     * This allows consumers to cache state associated with a particular generation number and later determine if that state may have been invalidated using a comparison with the current generation number.
     * For example, anchor slots can be used to cache the removal status of a node to memoize repeated walks up the tree.
     */
    generationNumber: number;
    /**
     * Special root node under which all anchors in this anchor set are transitively parented.
     * This does not appear in the UpPaths (instead they use undefined for the root).
     * Immediate children of this root are in detached fields (which have their identifiers used as the field keys).
     *
     * This is allocated with refCount one, which is never freed so it is never cleaned up
     * (as long as this AnchorSet is not garbage collected).
     *
     * There should never be any children other than the special root detached field under this between transactions:
     * TODO: check for and enforce this.
     */
    private readonly root;
    private readonly anchorToPath;
    private activeVisitor?;
    constructor();
    /**
     * Allows access to data stored on the AnchorSet in "slots".
     * Use {@link anchorSlot} to create slots.
     *
     * @privateRemarks
     * This forwards to the slots of the special above root anchor which locate can't access.
     */
    get slots(): BrandedMapSubset<AnchorSlot<any>>;
    [Symbol.iterator](): IterableIterator<AnchorNode>;
    /**
     * Check if there are currently no anchors tracked.
     * Mainly for testing anchor cleanup.
     */
    isEmpty(): boolean;
    locate(anchor: Anchor): AnchorNode | undefined;
    forget(anchor: Anchor): void;
    /**
     * TODO: Add APIs need to allow callers of this function to reduce copying here.
     * Ex: maybe return something extending UpPath here.
     * @param path - the path to the node to be tracked. If null, returns an anchor
     * which is permanently invalid.
     */
    track(path: UpPath | null): Anchor;
    /**
     * Finds a path node, creating if needed, and adds a ref count to it.
     */
    private trackInner;
    /**
     * Finds a path node if it already exists.
     *
     * Does not add a ref!
     */
    find(path: UpPath): PathNode | undefined;
    /**
     * Returns an equivalent path making as much of it with PathNodes as possible.
     * This allows future operations (like find, track, locate) on this path (and derived ones) to be faster.
     * Note that the returned path may use AnchorNodes from this AnchorSet,
     * but does not have a tracked reference to them, so this should not be held onto across anything that might free an AnchorNode.
     *
     * @remarks
     * Also ensures that any PathNode in the path is from this AnchorSet.
     */
    internalizePath(originalPath: UpPath): UpPath;
    /**
     * Recursively marks the given `nodes` and their descendants as disposed and pointing to a deleted node.
     * Note that this does NOT detach the nodes.
     */
    private deepDelete;
    /**
     * Decouple nodes from their parent.
     * This removes the reference from the parent to the decoupled children, and updates the indexes of the remaining children accordingly.
     * This does NOT update the decoupled children: both their index and parent are left at their existing values.
     * To decouple and fixup the children, see `removeChildren` and `moveChildren`.
     * @param startPath - The path to the first node that is being decoupled.
     * @param count - number of siblings that are decoupled from the original tree.
     *
     * TODO: tests
     */
    private decoupleNodes;
    /**
     * Couple nodes to a parent.
     * @param destination - where the siblings are coupled to.
     * @param count - number of siblings that are coupled in the original tree.
     * @param coupleInfo - this object contains the nodes to couple and the parent index of the first node that is coupled in the original tree.
     *
     * TODO: tests
     */
    private coupleNodes;
    /**
     * Updates the parent indexes within `field` to account for `count` children being inserted at `fromParentIndex`. Note that
     * `fromParentIndex` is the logical position within the field, not the index with the sparse PathNode array.
     *
     * @param field - the field to update.
     * @param fromParentIndex - the logical index within the field to start updating from.
     * @param count - the number to increase parent indexes.
     * @returns the number of items in the field that are not increased.
     *
     * TODO: tests
     */
    private increaseParentIndexes;
    /**
     * Updates paths for a range move (including re-parenting path items and updating indexes).
     * @param sourceStart - where the siblings are removed from.
     * @param destination - where the siblings are moved to.
     * @param count - number of siblings to move.
     *
     * TODO:
     * How should anchors that become invalid, then valid again (ex: into content that was deleted, then undone) work?
     * Add an API to resurrect them? Store them in special detached fields? Store them in special non-detached fields?
     *
     * TODO:
     * How should custom anchors work (ex: ones not just tied to a specific Node)?
     * This design assumes they can be expressed in terms of a Node anchor + some extra stuff,
     * but we don't have an API for the extra stuff yet.
     *
     * TODO: tests
     */
    private moveChildren;
    private removeChildren;
    /**
     * Updates the parent indexes of all the nodes located at right side of the given path by the given offset.
     * @param firstSiblingToOffset - the path to offset children of.
     * @param offset - the offset to apply to the children.
     *
     */
    private offsetChildren;
    /**
     * Provides a visitor that can be used to mutate this {@link AnchorSet}.
     *
     * @returns A visitor that can be used to mutate this {@link AnchorSet}.
     *
     * @remarks
     * Mutating the {@link AnchorSet} does NOT update the forest.
     * The visitor must be released after use by calling {@link DeltaVisitor.free} on it.
     * It is invalid to acquire a visitor without releasing the previous one,
     * and this method will throw an error if this is attempted.
     */
    acquireVisitor(): AnnouncedVisitor & DeltaVisitor;
}
/**
 * Indicates the status of a `NodePath`.
 */
declare enum Status {
    /**
     * Indicates the `NodePath` is being maintained and corresponds to a valid
     * (i.e., not removed) node in the document.
     */
    Alive = 0,
    /**
     * Indicates the `NodePath` is not being maintained by the `AnchorSet`.
     * The `NodePath` may or may not correspond to a valid node in the document.
     *
     * Accessing such a node is invalid.
     * Nodes in this state are retained to detect use-after-free bugs.
     */
    Disposed = 1,
    /**
     * Indicates the `NodePath` corresponds to a removed node in the document.
     * Such `NodePath`s are not maintained by the `AnchorSet` (other than updating
     * their status to `Disposed` when appropriate).
     *
     * Accessing such a node is invalid.
     * Nodes in this state are retained to detect use-after-free bugs.
     */
    Dangling = 2
}
/**
 * Tree of anchors.
 *
 * Contains both child and parent pointers, which are kept in sync.
 *
 * Each anchor is equivalent to a path through the tree.
 * This tree structure stores a collection of these paths, but deduplicating the common prefixes of the tree
 * prefix-tree style.
 *
 * These anchors are used instead of just holding onto the node objects in forests for several reasons:
 *
 * - Update policy might be more complex than just tracking a node object in the forest.
 *
 * - Not all forests will have node objects: some may use compressed binary formats with no objects to reference.
 *
 * - Anchors are needed even when not using forests, and for nodes that are outside the currently loaded part of the
 * forest.
 *
 * - Forest in general do not need to support up pointers, but they are needed for anchors.
 *
 * Thus this can be thought of as a sparse copy of the subset of trees which are used as anchors,
 * plus the parent paths for them.
 *
 * ReferenceCountedBase tracks the number of references to this from external sources (`Anchors` via `AnchorSet`.).
 * Kept alive as if any of the follow are true:
 * 1. there are children.
 * 2. refcount is non-zero.
 * 3. events are registered.
 */
declare class PathNode extends ReferenceCountedBase implements UpPath<PathNode>, AnchorNode {
    readonly anchorSet: AnchorSet;
    parentField: FieldKey;
    parentIndex: number;
    /**
     * The parent of this `PathNode` (an up pointer in the `PathNode` tree).
     * If the status of this node is `Alive`, then there must be a corresponding down pointer from the
     * `parentPath` node to this node.
     * When undefined, this node is the {@link AnchorSet.root} for `this.anchorSet` and thus has no parent.
     *
     * When updating the tree, `AnchorSet` may transiently leave the up and down pointers inconsistent
     * (updating down pointers first), but must ensure they are consistent before the editing operation returns
     * to non-`AnchorSet` code.
     * This consistency guarantee only applies to nodes that are `Alive`.
     */
    parentPath: PathNode | undefined;
    status: Status;
    /**
     * Event emitter for this anchor.
     */
    readonly events: Listenable<AnchorEvents> & import("@fluidframework/core-interfaces/internal").IEmitter<AnchorEvents> & import("@fluidframework/core-interfaces/internal").HasListeners<AnchorEvents>;
    /**
     * PathNode arrays are kept sorted the PathNode's parentIndex for efficient search.
     * Users of this field must take care to maintain invariants (correct parent pointers, not empty child arrays etc.)
     *
     * Performance Note:
     * Large child lists could be updated more efficiently here using a data-structure optimized
     * for efficient prefix sum updates, such as a Fenwick tree or Finger tree.
     * This would be complicated by the need for parent pointers (including indexes),
     * but is possible to do.
     */
    readonly children: Map<FieldKey, PathNode[]>;
    readonly slots: BrandedMapSubset<AnchorSlot<any>>;
    /**
     * Construct a PathNode with refcount 1.
     * @param anchorSet - used to determine if this PathNode is already part of a specific anchorSet
     * to early out UpPath walking.
     */
    constructor(anchorSet: AnchorSet, parentField: FieldKey, parentIndex: number, 
    /**
     * The parent of this `PathNode` (an up pointer in the `PathNode` tree).
     * If the status of this node is `Alive`, then there must be a corresponding down pointer from the
     * `parentPath` node to this node.
     * When undefined, this node is the {@link AnchorSet.root} for `this.anchorSet` and thus has no parent.
     *
     * When updating the tree, `AnchorSet` may transiently leave the up and down pointers inconsistent
     * (updating down pointers first), but must ensure they are consistent before the editing operation returns
     * to non-`AnchorSet` code.
     * This consistency guarantee only applies to nodes that are `Alive`.
     */
    parentPath: PathNode | undefined);
    child(key: FieldKey, index: number): UpPath<AnchorNode>;
    getOrCreateChildRef(key: FieldKey, index: number): [Anchor, AnchorNode];
    /**
     * @returns true iff this PathNode is the special root node that sits above all the detached fields.
     * In this case, the fields are detached sequences.
     * Note that the special root node should never appear in an UpPath
     * since UpPaths represent this root as `undefined`.
     */
    private isRoot;
    get parent(): PathNode | undefined;
    addRef(count?: number): void;
    removeRef(count?: number): void;
    protected onUnreferenced(): void;
    /**
     * Gets a child, adding a ref to it.
     * Creates child (with 1 ref) if needed.
     */
    getOrCreateChild(key: FieldKey, index: number): PathNode;
    childIfAnchored(key: FieldKey, index: number): PathNode | undefined;
    /**
     * Removes reference from this to `child`.
     * Since PathNodes are doubly linked,
     * the caller must ensure that the reference from child to parent is also removed (or the child is no longer used).
     */
    removeChild(child: PathNode): void;
    /**
     * Call this after directly editing the child array for a field to be empty.
     * Handles cleaning up unneeded data
     * (like the field in the map, and possibly this entire PathNode and its parents if they are no longer needed.)
     */
    afterEmptyField(key: FieldKey): void;
    /**
     * If node is no longer needed (has no references, no children and no events):
     * removes this from parent if alive, and sets this to disposed.
     * Must only be called when .
     *
     * Allowed when dangling (but not when disposed).
     */
    private considerDispose;
}
export {};
//# sourceMappingURL=anchorSet.d.ts.map