// Copyright 2014-2025, University of Colorado Boulder
/**
 * Basic model for a push button, including over/down/enabled properties.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 * @author John Blanco (PhET Interactive Simulations)
 * @author Chris Malley (PixelZoom, Inc.)
 */
import '../../../globals.js';
import BooleanProperty from '../../../axon/js/BooleanProperty.js';
import CallbackTimer from '../../../axon/js/CallbackTimer.js';
import Emitter from '../../../axon/js/Emitter.js';
import optionize from '../../../phet-core/js/optionize.js';
import EventType from '../../../tandem/js/EventType.js';
import PhetioObject from '../../../tandem/js/PhetioObject.js';
import Tandem from '../../../tandem/js/Tandem.js';
import ButtonModel from './ButtonModel.js';
export default class PushButtonModel extends ButtonModel {
    constructor(providedOptions) {
        const options = optionize()({
            fireOnDown: false,
            listener: null,
            interruptListener: null,
            fireOnHold: false,
            fireOnHoldDelay: 400,
            fireOnHoldInterval: 100,
            tandem: Tandem.REQUIRED,
            phetioReadOnly: PhetioObject.DEFAULT_OPTIONS.phetioReadOnly
        }, providedOptions);
        super(options);
        // the event that kicked off the latest fire (including delayed fire-on-hold cases)
        this.startEvent = null;
        this.isFiringProperty = new BooleanProperty(false);
        this.firedEmitter = new Emitter({
            tandem: options.tandem.createTandem('firedEmitter'),
            phetioDocumentation: 'Emits when the button is fired',
            phetioReadOnly: options.phetioReadOnly,
            phetioEventType: EventType.USER,
            // Order dependencies, so that we can fire our interruptListener before any other listeners without having to
            // create and maintain other emitters.
            hasListenerOrderDependencies: true
        });
        // This should not be an issue from user input, but could occur from the fire() command, or emitting the
        // firedEmitter via PhET-iO, see https://github.com/phetsims/studio/issues/323
        ;
        if (options.interruptListener) {
            this.firedEmitter.addListener(() => {
                options.interruptListener(this.startEvent);
            });
        }
        if (options.listener !== null) {
            this.firedEmitter.addListener(options.listener);
        }
        // Create a timer to handle the optional fire-on-hold feature.
        // When that feature is enabled, calling this.fire is delegated to the timer.
        if (options.fireOnHold) {
            this.timer = new CallbackTimer({
                callback: this.fire.bind(this),
                delay: options.fireOnHoldDelay,
                interval: options.fireOnHoldInterval
            });
        }
        // Point down
        const downPropertyObserver = (down) => {
            if (down) {
                if (this.enabledProperty.get()) {
                    this.startEvent = self.phet?.joist?.display?._input?.currentSceneryEvent || null;
                    if (options.fireOnDown) {
                        this.fire();
                    }
                    if (this.timer) {
                        this.timer.start();
                    }
                    if (options.fireOnDown || this.timer) {
                        this.produceSoundEmitter.emit();
                    }
                }
            }
            else {
                // should the button fire?
                const fire = (!options.fireOnDown && (this.overProperty.get() || this.focusedProperty.get()) && this.enabledProperty.get() && !this.interrupted);
                if (this.timer) {
                    this.timer.stop(fire);
                }
                else if (fire) {
                    // Produce sound before firing, in case firing causes the disposal of this PushButtonModel
                    this.produceSoundEmitter.emit();
                    this.fire();
                }
            }
        };
        this.downProperty.link(downPropertyObserver);
        // Stop the timer when the button is disabled.
        const enabledPropertyObserver = (enabled) => {
            if (!enabled && this.timer) {
                this.timer.stop(false); // Stop the timer, don't fire if we haven't already
            }
        };
        this.enabledProperty.link(enabledPropertyObserver);
        this.disposePushButtonModel = () => {
            // If the button was firing, we must complete the PhET-iO transaction before disposing.
            // see https://github.com/phetsims/energy-skate-park-basics/issues/380
            this.isFiringProperty.value = false;
            this.isFiringProperty.dispose();
            this.firedEmitter.dispose();
            this.downProperty.unlink(downPropertyObserver);
            this.enabledProperty.unlink(enabledPropertyObserver);
            if (this.timer) {
                this.timer.dispose();
                this.timer = null;
            }
        };
    }
    dispose() {
        this.disposePushButtonModel();
        super.dispose();
    }
    /**
     * Adds a listener. If already a listener, this is a no-op.
     * @param listener - function called when the button is pressed, no args
     */
    addListener(listener) {
        this.firedEmitter.addListener(listener);
    }
    /**
     * Removes a listener. If not a listener, this is a no-op.
     */
    removeListener(listener) {
        this.firedEmitter.removeListener(listener);
    }
    /**
     * Fires all listeners.  Public for phet-io and a11y use.
     */
    fire() {
        // Make sure the button is not already firing, see https://github.com/phetsims/energy-skate-park-basics/issues/380
        ;
        this.isFiringProperty.value = true;
        this.firedEmitter.emit();
        this.isFiringProperty.value = false;
    }
}
//# sourceMappingURL=PushButtonModel.js.map