import { Group } from './group';
import { Node } from './node';
type ValidId = string | number;
type NodeConstructor<TNode extends Node> = new () => TNode;
type NodeFactory<TDatum, TNode extends Node<TDatum>> = (datum: TDatum) => TNode;
type NodeConstructorOrFactory<TDatum, TNode extends Node<TDatum>> = NodeConstructor<TNode> | NodeFactory<TDatum, TNode>;
/**
 * `Selection['nodeFactory']` causes some Selections to unintuitively become unassignable to something that looks like
 * it should be.
 *
 * Example:
 *
 *     type A = { opacity: number, yValue: number };
 *     type B = { opacity: number };
 *     type U = unknown;
 *     const myA = new Selection<A, Node<A>>(new Group, Rect<A>);
 *     const myB: Selection<B, Node<B>> = myA;            // error.
 *     const myU: Selection<U, Node<U>> = myA;            // also an error (same reason).
 *     const myI: SelectionInterface<B, Node<B>> = myA;   // OK
 *
 * Error for `myB` assignment
 *
 *     Type 'Selection<A, Node<A>>' is not assignable to type 'Selection<B, Node<B>>'.
 *      Types of property 'nodeFactory' are incompatible.
 *        Type 'NodeFactory<A, Node<A>>' is not assignable to type 'NodeFactory<B, Node<B>>'.
 *          Type 'B' is not assignable to type 'A'.
 *
 * Which basically mean: this is unsafe, because `myB` might end up in a situation where you create a `TDatum` that does
 * not include the `yValue` property, which would break the assumptions that you can make about `myA`.
 *
 * However, assignments like `myB` and `myU` are genuine cases that we want to support:
 *
 * 1.   `myB` Animation: Partially update a scene-node
 * 2.   `myU` Highlight: Call `Node.getBBox()` (we do not intend to access `Node.datum`).
 *
 * Using `SelectionInterface` works because you are telling Typescript that you only care about the methods `Selection`
 * (interface) and not about the properties used internally (implementation).
 */
export interface SelectionInterface<TDatum, TChild extends Node<TDatum>> {
    [Symbol.iterator](): IterableIterator<{
        node: TChild;
    }>;
    readonly length: number;
    nodes(): TChild[];
    cleanup(): void;
    isGarbage(node: TChild): boolean;
    batchedUpdate(fn: () => void): void;
}
export declare class Selection<TDatum, TChild extends Node<TDatum>> implements SelectionInterface<TDatum, TChild> {
    private readonly parentNode;
    private readonly autoCleanup;
    static select<N extends Node<any>>(parent: Group, classOrFactory: NodeConstructorOrFactory<any, N>, garbageCollection?: boolean): Selection<N extends Node<infer Datum> ? Datum : never, N>;
    static selectNoInference<DExplicit, N extends Node<DExplicit>>(parent: Group, classOrFactory: NodeConstructorOrFactory<DExplicit, N>, garbageCollection?: boolean): Selection<DExplicit, N>;
    static selectAll<TChild extends Node = Node>(parent: Node, predicate: (node: Node) => node is TChild): TChild[];
    static selectByClass<TChild extends Node = Node>(node: Node, ...Classes: Array<new () => TChild>): TChild[];
    static selectByTag<TChild extends Node = Node>(node: Node, tag: number): TChild[];
    private readonly nodeFactory;
    private readonly garbageBin;
    private readonly _nodesMap;
    private _nodes;
    private data;
    private readonly debug;
    constructor(parentNode: Group, classOrFactory: NodeConstructorOrFactory<TDatum, TChild>, autoCleanup?: boolean);
    private createNode;
    /**
     * Update the data in a selection. If an `getDatumId()` function is provided, maintain a list of ids related to
     * the nodes. Otherwise, take the more efficient route of simply creating and destroying nodes at the end
     * of the array.
     */
    update(data: TDatum[], initializer?: (node: TChild) => void, getDatumId?: (datum: TDatum) => ValidId): this;
    cleanup(): this;
    clear(): this;
    isGarbage(node: TChild): boolean;
    each(iterate: (node: TChild, datum: TDatum, index: number) => void): this;
    [Symbol.iterator](): IterableIterator<{
        node: TChild;
        datum: TDatum;
        index: number;
    }>;
    select<TChild2 extends Node>(predicate: (node: Node) => node is TChild2): TChild2[];
    selectByClass<TChild2 extends Node>(Class: new () => TChild2): TChild2[];
    selectByTag<TChild2 extends Node>(tag: number): TChild2[];
    nodes(): TChild[];
    at(index: number): TChild | undefined;
    get length(): number;
    batchedUpdate(fn: () => void): void;
}
export {};
