import '../../../globals.js';
// Copyright 2024, University of Colorado Boulder
/**
 * A drag listener that supports both pointer and keyboard input. It is composed with a DragListener and a
 * KeyboardDragListener to support pointer input and alternative input. In the future it can support other
 * input modalities or PhET-specific features.
 *
 * Be sure to dispose of this listener when it is no longer needed.
 *
 * Options that are common to both listeners are provided directly to this listener. Options that are specific to
 * a particular listener can be provided through the dragListenerOptions or keyboardDragListenerOptions.
 *
 * Typical PhET usage will use a position Property in a model coordinate frame and look like this:
 *
 *     // A focusable Node that can be dragged with pointer or keyboard.
 *     const draggableNode = new Node( {
 *       tagName: 'div',
 *       focusable: true
 *     } );
 *
 *     const richDragListener = new RichDragListener( {
 *       positionProperty: someObject.positionProperty,
 *       transform: modelViewTransform
 *     } );
 *
 *     draggableNode.addInputListener( richDragListener );
 *
 * This listener works by implementing TInputListener and forwarding input events to the specific listeners. This is
 * how we support adding this listener through the scenery input listener API.
 *
 * @author Jesse Greenberg
 */
import { DragListener, KeyboardDragListener, scenery } from '../imports.js';
import optionize, { combineOptions } from '../../../phet-core/js/optionize.js';
import DerivedProperty from '../../../axon/js/DerivedProperty.js';
export default class RichDragListener {
    constructor(providedOptions) {
        const options = optionize()({
            // RichDragListenerOptions
            positionProperty: null,
            // Called when the drag is started, for any input type. If you want to determine the type of input, you can check
            // SceneryEvent.isFromPDOM or SceneryEvent.type. If you need a start behavior for a specific form of input,
            // provide a start callback for that listener's options. It will be called IN ADDITION to this callback.
            start: null,
            // Called when the drag is ended, for any input type. If you want to determine the type of input, you can check
            // SceneryEvent.isFromPDOM or SceneryEvent.type. If you need an end behavior for a specific form of input,
            // provide an end callback for that listener's options. It will be called IN ADDITION to this callback. The event
            // may be null for cases of interruption.
            end: null,
            // Called during the drag event, for any input type. If you want to determine the type of input, you can check
            // SceneryEvent.isFromPDOM or SceneryEvent.type. If you need a drag behavior for a specific form of input,
            // provide a drag callback for that listener's options. It will be called IN ADDITION to this callback.
            drag: null,
            transform: null,
            dragBoundsProperty: null,
            mapPosition: null,
            translateNode: false,
            dragListenerOptions: {},
            keyboardDragListenerOptions: {}
        }, providedOptions);
        // Options that will apply to both listeners.
        const sharedOptions = {
            positionProperty: options.positionProperty,
            transform: options.transform,
            dragBoundsProperty: options.dragBoundsProperty || undefined,
            mapPosition: options.mapPosition || undefined,
            translateNode: options.translateNode
        };
        //---------------------------------------------------------------------------------
        // Construct the DragListener and combine its options.
        //---------------------------------------------------------------------------------
        const wrappedDragListenerStart = (event, listener) => {
            // when the drag listener starts, interrupt the keyboard dragging
            this.keyboardDragListener.interrupt();
            options.start && options.start(event, listener);
            options.dragListenerOptions.start && options.dragListenerOptions.start(event, listener);
        };
        const wrappedDragListenerDrag = (event, listener) => {
            options.drag && options.drag(event, listener);
            options.dragListenerOptions.drag && options.dragListenerOptions.drag(event, listener);
        };
        const wrappedDragListenerEnd = (event, listener) => {
            options.end && options.end(event, listener);
            options.dragListenerOptions.end && options.dragListenerOptions.end(event, listener);
        };
        const dragListenerOptions = combineOptions(
        // target object
        {}, 
        // Options that apply to both, but can be overridden by provided listener-specific options
        sharedOptions, 
        // Provided listener-specific options
        options.dragListenerOptions, 
        // Options that cannot be overridden - see wrapped callbacks above
        {
            start: wrappedDragListenerStart,
            drag: wrappedDragListenerDrag,
            end: wrappedDragListenerEnd
        });
        this.dragListener = new DragListener(dragListenerOptions);
        //---------------------------------------------------------------------------------
        // Construct the KeyboardDragListener and combine its options.
        //---------------------------------------------------------------------------------
        const wrappedKeyboardListenerStart = (event, listener) => {
            // when the drag listener starts, interrupt the pointer dragging
            this.dragListener.interrupt();
            options.start && options.start(event, listener);
            options.keyboardDragListenerOptions.start && options.keyboardDragListenerOptions.start(event, listener);
        };
        const wrappedKeyboardListenerDrag = (event, listener) => {
            options.drag && options.drag(event, listener);
            options.keyboardDragListenerOptions.drag && options.keyboardDragListenerOptions.drag(event, listener);
        };
        const wrappedKeyboardListenerEnd = (event, listener) => {
            options.end && options.end(event, listener);
            options.keyboardDragListenerOptions.end && options.keyboardDragListenerOptions.end(event, listener);
        };
        const keyboardDragListenerOptions = combineOptions(
        // target object
        {}, 
        // Options that apply to both, but can be overridden by provided listener-specific options
        sharedOptions, 
        // Provided listener-specific options
        options.keyboardDragListenerOptions, 
        // Options that cannot be overridden - see wrapped callbacks above
        {
            start: wrappedKeyboardListenerStart,
            drag: wrappedKeyboardListenerDrag,
            end: wrappedKeyboardListenerEnd
        });
        this.keyboardDragListener = new KeyboardDragListener(keyboardDragListenerOptions);
        // The hotkeys from the keyboard listener are assigned to this listener so that they are activated for Nodes
        // where this listener is added.
        this.hotkeys = this.keyboardDragListener.hotkeys;
        this.isPressedProperty = DerivedProperty.or([this.dragListener.isPressedProperty, this.keyboardDragListener.isPressedProperty]);
    }
    get isPressed() {
        return this.dragListener.isPressed || this.keyboardDragListener.isPressed;
    }
    dispose() {
        this.isPressedProperty.dispose();
        this.dragListener.dispose();
        this.keyboardDragListener.dispose();
    }
    /**
     * ********************************************************************
     * Forward input to both listeners
     * ********************************************************************
     */
    interrupt() {
        this.dragListener.interrupt();
        this.keyboardDragListener.interrupt();
    }
    /**
     * ********************************************************************
     * Forward to the KeyboardListener
     * ********************************************************************
     */
    keydown(event) {
        this.keyboardDragListener.keydown(event);
    }
    focusout(event) {
        this.keyboardDragListener.focusout(event);
    }
    focusin(event) {
        this.keyboardDragListener.focusin(event);
    }
    cancel() {
        this.keyboardDragListener.cancel();
    }
    /**
     * ********************************************************************
     * Forward to the DragListener
     * ********************************************************************
     */
    click(event) {
        this.dragListener.click(event);
    }
    touchenter(event) {
        this.dragListener.touchenter(event);
    }
    touchmove(event) {
        this.dragListener.touchmove(event);
    }
    focus(event) {
        this.dragListener.focus(event);
    }
    blur() {
        this.dragListener.blur();
    }
    down(event) {
        this.dragListener.down(event);
    }
    up(event) {
        this.dragListener.up(event);
    }
    enter(event) {
        this.dragListener.enter(event);
    }
    move(event) {
        this.dragListener.move(event);
    }
    exit(event) {
        this.dragListener.exit(event);
    }
    pointerUp(event) {
        this.dragListener.pointerUp(event);
    }
    pointerCancel(event) {
        this.dragListener.pointerCancel(event);
    }
    pointerMove(event) {
        this.dragListener.pointerMove(event);
    }
    pointerInterrupt() {
        this.dragListener.pointerInterrupt();
    }
}
scenery.register('RichDragListener', RichDragListener);
//# sourceMappingURL=RichDragListener.js.map