import _ from 'lodash';
import '../../../../assert/js/assert.js';
import '../../../../globals.js';
// Copyright 2022-2024, University of Colorado Boulder
/**
 * Supertype for LayoutConstraints that are based on an actual Node where the layout takes place. Generally used with
 * layout containers that are subtypes of LayoutNode.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import Property from '../../../../axon/js/Property.js';
import TinyProperty from '../../../../axon/js/TinyProperty.js';
import Bounds2 from '../../../../dot/js/Bounds2.js';
import { LayoutConstraint, scenery } from '../../imports.js';
import optionize from '../../../../phet-core/js/optionize.js';
import Vector2 from '../../../../dot/js/Vector2.js';
// Position changes smaller than this will be ignored
const CHANGE_POSITION_THRESHOLD = 1e-9;
export default class NodeLayoutConstraint extends LayoutConstraint {
    /**
     * Recommended for ancestorNode to be the layout container, and that the layout container extends LayoutNode.
     * (scenery-internal)
     */
    constructor(ancestorNode, providedOptions) {
        // The omitted options are set to proper defaults below
        const options = optionize()({
            // As options, so we could hook into a Node's preferred/minimum sizes if desired
            preferredWidthProperty: new TinyProperty(null),
            preferredHeightProperty: new TinyProperty(null),
            minimumWidthProperty: new TinyProperty(null),
            minimumHeightProperty: new TinyProperty(null),
            layoutOriginProperty: new TinyProperty(Vector2.ZERO)
        }, providedOptions);
        super(ancestorNode);
        this._excludeInvisible = true;
        this.layoutBoundsProperty = new Property(Bounds2.NOTHING, {
            valueComparisonStrategy: 'equalsFunction'
        });
        this.preferredWidthProperty = options.preferredWidthProperty;
        this.preferredHeightProperty = options.preferredHeightProperty;
        this.minimumWidthProperty = options.minimumWidthProperty;
        this.minimumHeightProperty = options.minimumHeightProperty;
        this.layoutOriginProperty = options.layoutOriginProperty;
        this.preferredWidthProperty.lazyLink(this._updateLayoutListener);
        this.preferredHeightProperty.lazyLink(this._updateLayoutListener);
        this.layoutOriginProperty.lazyLink(this._updateLayoutListener);
    }
    /**
     * Filters out cells to only those that will be involved in layout
     */
    filterLayoutCells(cells) {
        // We'll check to make sure cells are disposed in a common place, so it's not duplicated
        assert && assert(_.every(cells, cell => !cell.node.isDisposed), 'A cell\'s node should not be disposed when layout happens');
        return cells.filter(cell => {
            return cell.isConnected() && cell.proxy.bounds.isValid() && (!this.excludeInvisible || cell.node.visible);
        });
    }
    get excludeInvisible() {
        return this._excludeInvisible;
    }
    set excludeInvisible(value) {
        if (this._excludeInvisible !== value) {
            this._excludeInvisible = value;
            this.updateLayoutAutomatically();
        }
    }
    /**
     * Sets preferred size of content in a central location (so we could hook in animation in the future)
     * (scenery-internal)
     */
    setProxyPreferredSize(orientation, proxy, preferredSize) {
        proxy[orientation.preferredSize] = preferredSize;
    }
    /**
     * Sets position of content in a central location (so we could hook in animation in the future)
     * (scenery-internal)
     */
    setProxyMinSide(orientation, proxy, minSide) {
        if (Math.abs(proxy[orientation.minSide] - minSide) > CHANGE_POSITION_THRESHOLD) {
            proxy[orientation.minSide] = minSide;
        }
    }
    /**
     * Sets origin-based position of content in a central location (so we could hook in animation in the future)
     * (scenery-internal)
     */
    setProxyOrigin(orientation, proxy, origin) {
        if (Math.abs(proxy[orientation.coordinate] - origin) > CHANGE_POSITION_THRESHOLD) {
            proxy[orientation.coordinate] = origin;
        }
    }
    /**
     * Releases references
     */
    dispose() {
        // In case they're from external sources (since these constraints can be used without a dedicated Node that is also
        // being disposed.
        this.preferredWidthProperty.unlink(this._updateLayoutListener);
        this.preferredHeightProperty.unlink(this._updateLayoutListener);
        this.layoutOriginProperty.unlink(this._updateLayoutListener);
        super.dispose();
    }
}
scenery.register('NodeLayoutConstraint', NodeLayoutConstraint);
//# sourceMappingURL=NodeLayoutConstraint.js.map