/*!
 * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
 * Licensed under the MIT License.
 */
import { ISubscribable } from "../../events/index.js";
import { Brand, BrandedKey, BrandedMapSubset, Opaque } from "../../util/index.js";
import { FieldKey } from "../schema-stored/index.js";
import { UpPath } from "./pathTree.js";
import { Value } from "./types.js";
import { DeltaVisitor } from "./visitDelta.js";
import { PathVisitor } from "./visitPath.js";
import { 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}.
 * @internal
 */
export type Anchor = Brand<number, "rebaser.Anchor">;
/**
 * Maps anchors (which must be ones this locator knows about) to paths.
 * @internal
 */
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.
 * @internal
 */
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.
 *
 * @internal
 */
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;
    /**
     * One or more of the children of this node is just about to change.
     *
     * @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;
    /**
     * One or more of the children of this node has just changed.
     *
     * @remarks
     * Does not include edits of child subtrees: instead only includes changes to nodes which are direct children in this node's fields.
     */
    childrenChanged(anchor: AnchorNode): void;
    /**
     * Something in this tree is changing.
     * The event can optionally return a {@link PathVisitor} to traverse the subtree.
     * Called on every parent (transitively) when a change is occurring.
     * Includes changes to this node itself.
     */
    subtreeChanging(anchor: AnchorNode): PathVisitor | void;
    /**
     * Emitted after the subtree rooted at `anchor` may have been changed.
     *
     * @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.
     *
     * @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.
     *
     * TODO: can we make it so this event is guaranteed to only fire once during the delta visit? Specifically when
     * changes to the tree did happen and are visible to the listener.
     */
    subtreeChanged(anchor: AnchorNode): void;
    /**
     * Value on this node is changing.
     */
    valueChanging(anchor: AnchorNode, value: Value): 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.
 *
 * @internal
 */
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.
 * @internal
 */
export interface AnchorNode extends UpPath<AnchorNode>, ISubscribable<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 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);
 * }
 * ```
 * @internal
 */
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
 * @internal
 */
export declare class AnchorSet implements ISubscribable<AnchorSetRootEvents>, AnchorLocator {
    private readonly events;
    /**
     * 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>>;
    on<K extends keyof AnchorSetRootEvents>(eventName: K, listener: AnchorSetRootEvents[K]): () => void;
    /**
     * 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.
     */
    private find;
    /**
     * 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;
}
//# sourceMappingURL=anchorSet.d.ts.map