import _ from 'lodash';
import '../../../globals.js';
// Copyright 2023-2024, University of Colorado Boulder
/**
 * A Property that will contain a set of all ancestor Nodes of a given Node.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import TinyEmitter from '../../../axon/js/TinyEmitter.js';
import TinyProperty from '../../../axon/js/TinyProperty.js';
import { scenery } from '../imports.js';
const valueComparisonStrategy = (a, b) => {
    // Don't fire notifications if it hasn't changed.
    return a.size === b.size && _.every([...a], node => b.has(node));
};
export default class AncestorNodesProperty extends TinyProperty {
    constructor(node) {
        super(new Set());
        this.node = node;
        // A set of nodes where we are listening to whether their parents change
        this.listenedNodeSet = new Set();
        // Fired whenever we need to update the internal value (i.e. a parent was added or removed somewhere in the chain)
        this.updateEmitter = new TinyEmitter();
        this.valueComparisonStrategy = valueComparisonStrategy;
        this._nodeUpdateListener = this.update.bind(this);
        // Listen to our own parent changes too (even though we aren't an ancestor)
        this.addNodeListener(node);
        this.update();
    }
    update() {
        // Nodes that were touched in the scan (we should listen to changes to ANY of these to see if there is a connection
        // or disconnection). This could potentially cause our Property to change
        const nodeSet = new Set();
        // Recursively scan to identify all ancestors
        (function recurse(node) {
            const parents = node.parents;
            parents.forEach(parent => {
                nodeSet.add(parent);
                recurse(parent);
            });
        })(this.node);
        // Add in new needed listeners
        nodeSet.forEach(node => {
            if (!this.listenedNodeSet.has(node)) {
                this.addNodeListener(node);
            }
        });
        // Remove listeners not needed anymore
        this.listenedNodeSet.forEach(node => {
            // NOTE: do NOT remove the listener that is listening to our node for changes (it's not an ancestor, and won't
            // come up in this list)
            if (!nodeSet.has(node) && node !== this.node) {
                this.removeNodeListener(node);
            }
        });
        this.value = nodeSet;
        this.updateEmitter.emit();
    }
    addNodeListener(node) {
        this.listenedNodeSet.add(node);
        node.parentAddedEmitter.addListener(this._nodeUpdateListener);
        node.parentRemovedEmitter.addListener(this._nodeUpdateListener);
    }
    removeNodeListener(node) {
        this.listenedNodeSet.delete(node);
        node.parentAddedEmitter.removeListener(this._nodeUpdateListener);
        node.parentRemovedEmitter.removeListener(this._nodeUpdateListener);
    }
    dispose() {
        this.listenedNodeSet.forEach(node => this.removeNodeListener(node));
        super.dispose();
    }
}
scenery.register('AncestorNodesProperty', AncestorNodesProperty);
//# sourceMappingURL=AncestorNodesProperty.js.map