/**
 * Creates a Property that does synchronization of values with a swappable Property that itself can change.
 * Handles the case where you need a Property that can switch between acting like multiple other Properties.
 *
 * With no other options specified, the value of this Property is:
 * - null, if valuePropertyProperty.value === null
 * - valuePropertyProperty.value.value otherwise
 *
 * The value of this Property (generalized, with the options available) is:
 * - derive( defaultValue ), if valuePropertyProperty.value === null
 * - map( derive( valuePropertyProperty.value ).value ) otherwise
 *
 * Generally, this DynamicProperty uses one-way synchronization (it only listens to the source), but if the
 * 'bidirectional' option is true, it will use two-way synchronization (changes to this Property will change the active
 * source). Thus when this Property changes value (when bidirectional is true), it will set:
 * - derive( valuePropertyProperty.value ).value = inverseMap( this.value ), if valuePropertyProperty.value !== null
 *
 *******************************
 * General example
 *******************************
 *   const firstProperty = new Property( Color.RED );
 *   const secondProperty = new Property( Color.BLUE );
 *   const currentProperty = new Property( firstProperty ); // {Property.<Property.<Color>>}
 *
 *   const backgroundFill = new DynamicProperty( currentProperty ) // Turns into a {Property.<Color>}
 *   backgroundFill.value; // Color.RED, since: currentProperty.value === firstProperty and
 *                                              firstProperty.value === Color.RED
 *   firstProperty.value = Color.YELLOW;
 *   backgroundFill.value; // Color.YELLOW - It's connected to firstProperty right now
 *
 *   currentProperty.value = secondProperty;
 *   backgroundFill.value; // Color.BLUE - It's the secondProperty's value
 *
 *   secondProperty.value = Color.MAGENTA;
 *   backgroundFill.value; // Color.MAGENTA - Yes, it's listening to the other Property now.
 *
 * Also supports falling back to null if our main Property is set to null:
 *   currentProperty.value = null;
 *   backgroundFill.value; // null
 *
 *******************************
 * 'derive' option
 *******************************
 * Additionally, DynamicProperty supports the ability to derive the Property value from our main Property's value.
 * For example, say you have multiple scenes each with the type:
 *   scene: {
 *     backgroundColorProperty: {Property.<Color>}
 *   }
 * and you have a currentSceneProperty: {Property.<Scene>}, you may want to create:
 *   const currentBackgroundColorProperty = new DynamicProperty( currentSceneProperty, {
 *     derive: 'backgroundColorProperty'
 *   } );
 * This would always report the current scene's current background color.
 * What if you sometimes don't have a scene active, e.g. {Property.<Scene|null>}? You can provide a default value:
 *  new DynamicProperty( currentSceneProperty, {
 *    derive: 'backgroundColorProperty',
 *    defaultValue: Color.BLACK
 *  } );
 * So that if the currentSceneProperty's value is null, the value of our DynamicProperty will be Color.BLACK.
 * NOTE there are constraints using derive: 'string' when using parametric type parameters. See https://github.com/phetsims/projectile-data-lab/issues/10
 *
 *******************************
 * 'bidirectional' option
 *******************************
 * If you would like for direct changes to this Property to change the original source (bidirectional synchronization),
 * then pass bidirectional:true:
 *   const firstProperty = new Property( 5 );
 *   const secondProperty = new Property( 10 );
 *   const numberPropertyProperty = new Property( firstProperty );
 *   const dynamicProperty = new DynamicProperty( numberPropertyProperty, { bidirectional: true } );
 *   dynamicProperty.value = 2; // allowed now that it is bidirectional, otherwise prohibited
 *   firstProperty.value; // 2
 *   numberPropertyProperty.value = secondProperty; // change which Property is active
 *   dynamicProperty.value; // 10, from the new Property
 *   dynamicProperty.value = 0;
 *   secondProperty.value; // 0, set above.
 *   firstProperty.value; // still 2 from above, since our dynamic Property switched to the other Property
 *
 *******************************
 * 'map' and 'inverseMap' options
 *******************************
 * DynamicProperty also supports mapping values to different types. For example, say we have a
 * numberPropertyProperty {Property.<Property.<number>>}, but want to have a {Property.<string>} as the output. Then:
 *   new DynamicProperty( numberPropertyProperty, {
 *     map: function( number ) { return '' + number; }
 *   } );
 * will do the trick. If this needs to be done with a bidirectional DynamicProperty, also include inverseMap:
 *   new DynamicProperty( numberPropertyProperty, {
 *     bidirectional: true,
 *     map: function( number ) { return '' + number; },
 *     inverseMap: function( string ) { return Number.parseFloat( string ); }
 *   } );
 * so that changes to the dynamic Property will result in a change in the numberPropertyProperty's value.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */
import '../../globals.js';
import type KeysMatching from '../../phet-core/js/types/KeysMatching.js';
import { type PropertyOptions } from './Property.js';
import ReadOnlyProperty from './ReadOnlyProperty.js';
import type TProperty from './TProperty.js';
import type TReadOnlyProperty from './TReadOnlyProperty.js';
export type TNullableProperty<T> = TReadOnlyProperty<T | null> | TReadOnlyProperty<T>;
type SelfOptions<ThisValueType, InnerValueType, OuterValueType> = {
    bidirectional?: boolean;
    defaultValue?: InnerValueType;
    derive?: ((outerValue: OuterValueType) => TReadOnlyProperty<InnerValueType>) | KeysMatching<OuterValueType, TReadOnlyProperty<InnerValueType>>;
    map?: ((innerValue: InnerValueType) => ThisValueType) | KeysMatching<InnerValueType, ThisValueType>;
    inverseMap?: ((value: ThisValueType) => InnerValueType) | KeysMatching<ThisValueType, InnerValueType>;
};
export type DynamicPropertyOptions<ThisValueType, InnerValueType, OuterValueType> = SelfOptions<ThisValueType, InnerValueType, OuterValueType> & PropertyOptions<ThisValueType>;
export default class DynamicProperty<ThisValueType, InnerValueType, OuterValueType> extends ReadOnlyProperty<ThisValueType> implements TProperty<ThisValueType> {
    private isExternallyChanging;
    private readonly defaultValue;
    protected readonly derive: (u: OuterValueType) => TReadOnlyProperty<InnerValueType>;
    protected readonly map: (v: InnerValueType) => ThisValueType;
    protected readonly inverseMap: (t: ThisValueType) => InnerValueType;
    protected readonly bidirectional: boolean;
    private readonly valuePropertyProperty;
    private readonly propertyPropertyListener;
    private readonly propertyListener;
    /**
     * @param valuePropertyProperty - If the value is null, it is considered disconnected.
     * @param [providedOptions] - options
     */
    constructor(valuePropertyProperty: TNullableProperty<OuterValueType> | TReadOnlyProperty<OuterValueType>, providedOptions?: DynamicPropertyOptions<ThisValueType, InnerValueType, OuterValueType>);
    /**
     * Listener added to the active inner Property.
     *
     * @param value - Should be either our defaultValue (if valuePropertyProperty.value is null), or
     *                derive( valuePropertyProperty.value ).value otherwise.
     * @param oldValue - Ignored for our purposes, but is the 2nd parameter for Property listeners.
     * @param innerProperty
     */
    private onPropertyPropertyChange;
    /**
     * Listener added to the outer Property.
     *
     * @param newPropertyValue - If derive is not provided then it should be a {Property.<*>|null}
     * @param oldPropertyValue - If derive is not provided then it should be a {Property.<*>|null}.
     *                                              We additionally handle the initial link() case where this is
     *                                              undefined.
     */
    private onPropertyChange;
    /**
     * Listener added to ourself when we are bidirectional
     */
    private onSelfChange;
    /**
     * Disposes this Property
     */
    dispose(): void;
    /**
     * Resets the current property (if it's a Property instead of a TinyProperty)
     */
    reset(): void;
    /**
     * Prevent setting this Property manually if it is not marked as bidirectional.
     */
    set(value: ThisValueType): void;
    /**
     * Overridden to make public
     */
    get value(): ThisValueType;
    /**
     * Overridden to make public
     * We ran performance tests on Chrome, and determined that calling super.value = newValue is statistically significantly
     * slower at the p = 0.10 level( looping over 10,000 value calls). Therefore, we prefer this optimization.
     */
    set value(value: ThisValueType);
    /**
     * Returns true if this Property value can be set externally, by set() or .value =
     */
    isSettable(): boolean;
}
export {};
//# sourceMappingURL=DynamicProperty.d.ts.map