import '../../globals.js';
// Copyright 2020-2024, University of Colorado Boulder
/**
 * A lightweight version of Property (that satisfies some of the interface), meant for high-performance applications
 * where validation, phet-io support and other things are not needed.
 *
 * This directly extends TinyEmitter in order to save memory.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import axon from './axon.js';
import TinyEmitter from './TinyEmitter.js';
import Validation from './Validation.js';
export default class TinyProperty extends TinyEmitter {
    constructor(value, onBeforeNotify, hasListenerOrderDependencies, reentrantNotificationStrategy) {
        // Defaults to "queue" for Properties so that we notify all listeners for a value change
        // before notifying for the next value change. For example, if we change from a->b, and one listener changes the value
        // from b->c, that reentrant value change will queue its listeners for after all listeners have fired for a->b. For
        // specifics see documentation in TinyEmitter.
        super(onBeforeNotify, hasListenerOrderDependencies, reentrantNotificationStrategy || 'queue');
        this._value = value;
    }
    /**
     * Returns the value.
     *
     * You can also use the es5 getter (property.value) but this means is provided for inner loops
     * or internal code that must be fast.
     */
    get() {
        return this._value;
    }
    /**
     * Returns the value.
     */
    get value() {
        return this.get();
    }
    /**
     * Sets the value.
     */
    set value(newValue) {
        this.set(newValue);
    }
    /**
     * Sets the value and notifies listeners, unless deferred or disposed. You can also use the es5 getter
     * (property.value) but this means is provided for inner loops or internal code that must be fast. If the value
     * hasn't changed, this is a no-op.
     */
    set(value) {
        // It is very important that `equalsValue` holds all logic about if the value should change AND if listeners
        // are notified.
        if (!this.equalsValue(value)) {
            const oldValue = this._value;
            this.setPropertyValue(value);
            this.notifyListeners(oldValue);
        }
    }
    /**
     * Sets the value without notifying any listeners. This is a place to override if a subtype performs additional work
     * when setting the value.
     */
    setPropertyValue(value) {
        this._value = value;
    }
    /**
     * Returns true if and only if the specified value equals the value of this property. This is used to determine if
     * a Property's value should change and if listeners should be notified. In general, this implementation should
     * not be overridden except to provide more correct "value"s as parameters for the areValuesEqual() function.
     */
    equalsValue(value) {
        return this.areValuesEqual(value, this._value);
    }
    /**
     * Central logic for determining value equality for Property. This determines if a value should change, and if
     * listeners should notify based on set() call.
     *
     * Determines equality semantics for value comparison, including whether notifications are sent out when the
     * wrapped value changes, and whether onValue() is triggered. See Validation.equalsForValidationStrategy for details
     * and doc on ValueComparisonStrategy
     *
     * Overriding this function is deprecated, instead provide a custom valueComparisonStrategy.
     */
    areValuesEqual(a, b) {
        return Validation.equalsForValidationStrategy(a, b, this.valueComparisonStrategy);
    }
    /**
     * Directly notifies listeners of changes.
     */
    notifyListeners(oldValue) {
        // We use this._value here for performance, AND to avoid calling onAccessAttempt unnecessarily.
        this.emit(this._value, oldValue, this);
    }
    /**
     * Adds listener and calls it immediately. If listener is already registered, this is a no-op. The initial
     * notification provides the current value for newValue and null for oldValue.
     */
    link(listener) {
        this.addListener(listener);
        listener(this._value, null, this); // null should be used when an object is expected but unavailable
    }
    /**
     * Add an listener to the TinyProperty, without calling it back right away. This is used when you need to register a
     * listener without an immediate callback.
     */
    lazyLink(listener) {
        this.addListener(listener); // Because it's a lazy link, it will never be called with null
    }
    /**
     * Removes a listener. If listener is not registered, this is a no-op.
     */
    unlink(listener) {
        this.removeListener(listener);
    }
    /**
     * Removes all listeners. If no listeners are registered, this is a no-op.
     */
    unlinkAll() {
        this.removeAllListeners();
    }
    /**
     * Links an object's named attribute to this TinyProperty.  Returns a handle so it can be removed using
     * TinyProperty.unlink();
     * Example: modelVisibleProperty.linkAttribute(view, 'visible');
     *
     * NOTE: Duplicated with Property.linkAttribute
     */
    linkAttribute(object, attributeName) {
        const handle = (value) => { object[attributeName] = value; };
        this.link(handle);
        return handle;
    }
    /**
     * Returns true if the value can be set externally, using .value= or set()
     */
    isSettable() {
        return true;
    }
    get valueComparisonStrategy() {
        return this._valueComparisonStrategy || 'reference';
    }
    set valueComparisonStrategy(valueComparisonStrategy) {
        this._valueComparisonStrategy = valueComparisonStrategy;
    }
    /**
     * Releases references.
     */
    dispose() {
        // Remove any listeners that are still attached (note that the emitter dispose would do this also, but without the
        // potentially-needed extra logic of changeCount, etc.)
        this.unlinkAll();
        super.dispose();
    }
}
axon.register('TinyProperty', TinyProperty);
//# sourceMappingURL=TinyProperty.js.map