// Copyright 2024, University of Colorado Boulder
/**
 * The model of the grab drag interaction. This has the current interaction state, and the cueing state.
 * Scenery-phet internal, GrabDragInteraction should be the only one to create this.
 *
 * @author Michael Kauzmann (PhET Interactive Simulations)
 * @author Jesse Greenberg (PhET Interactive Simulations)
 * @author Sam Reid (PhET Interactive Simulations)
 */
import '../../../../globals.js';
import _ from 'lodash';
import { assert } from '../../../../scenerystack/assert.js';
import Emitter from '../../../../axon/js/Emitter.js';
import EnabledComponent from '../../../../axon/js/EnabledComponent.js';
import Property from '../../../../axon/js/Property.js';
import optionize from '../../../../phet-core/js/optionize.js';
import sceneryPhet from '../../sceneryPhet.js';
import GrabDragUsageTracker from './GrabDragUsageTracker.js';
export default class GrabDragModel extends EnabledComponent {
    constructor(grabDragUsageTracker = new GrabDragUsageTracker(), providedOptions) {
        const options = optionize()({}, providedOptions);
        super(options);
        this.grabDragUsageTracker = grabDragUsageTracker;
        this.interactionStateProperty = new Property('idle');
        // called on reset()
        this.resetEmitter = new Emitter();
        this._releasedEmitter = new Emitter(); // called after setting to "idle" state
        this._grabbedEmitter = new Emitter(); // called after setting to "grabbed" state
    }
    get grabbedEmitter() { return this._grabbedEmitter; }
    get releasedEmitter() { return this._releasedEmitter; }
    /**
     * Grab with keyboard usage tracking
     */
    keyboardGrab(onBeforeEmit = _.noop) {
        // increment before grab in case something during the state change reads this value.
        this.grabDragUsageTracker.numberOfKeyboardGrabs++;
        this.grab(onBeforeEmit);
    }
    /**
     * Turn from idle into grabbed interaction state.
     * This updates accessibility representation in the PDOM and changes input listeners. This function can be called
     * while already grabbed, because of nuance in how we support multi-input and gestureDescription.
     */
    grab(onBeforeEmit = _.noop) {
        // Increment this even if we are already in the grabbed state, to indicate user intention in our usage tracker.
        this.grabDragUsageTracker.numberOfGrabs++;
        this.interactionStateProperty.value = 'grabbed';
        onBeforeEmit();
        this._grabbedEmitter.emit();
    }
    /**
     * Release from being grabbed. This function will set the interaction back to the "idle" state and should only be called
     * when in "grabbed" state. It also behaves as though it was released from user input, for example sound effect
     * and description will occur may occur.
     */
    release() {
        assert && assert(this.interactionStateProperty.value === 'grabbed', 'cannot set to interactionState if already set that way');
        this.interactionStateProperty.value = 'idle';
        this._releasedEmitter.emit();
    }
    dispose() {
        this._grabbedEmitter.dispose();
        this._releasedEmitter.dispose();
        this.resetEmitter.dispose();
        this.interactionStateProperty.dispose();
        super.dispose();
    }
    reset() {
        // This should go first, so that state logic listening to the interaction state respects the resetted usage tracker.
        this.grabDragUsageTracker.reset();
        this.interactionStateProperty.value = 'idle';
        this.resetEmitter.emit();
        assert && assert(this.grabDragUsageTracker.numberOfGrabs === 0, 'numberOfGrabs should be 0, but it was: ' + this.grabDragUsageTracker.numberOfGrabs);
        assert && assert(this.grabDragUsageTracker.numberOfKeyboardGrabs === 0, 'numberOfKeyboardGrabs should be 0, but it was ' + this.grabDragUsageTracker.numberOfKeyboardGrabs);
    }
}
sceneryPhet.register('GrabDragModel', GrabDragModel);
