import { PointerEventTypes } from './loader/babylonjs-import';
import { PickOptionType, PredefinedCanvasPosition } from './loader/Selectables';
/** Predefined point button id's */
export var PredefinedPointerButtonId;
(function (PredefinedPointerButtonId) {
    /** Main button , usually the left button */
    PredefinedPointerButtonId[PredefinedPointerButtonId["Main"] = 0] = "Main";
    /** Auxiliary button pressed, usually the wheel button or the middle button (if present) */
    PredefinedPointerButtonId[PredefinedPointerButtonId["Auxiliary"] = 1] = "Auxiliary";
    /** Secondary button pressed, usually the right button */
    PredefinedPointerButtonId[PredefinedPointerButtonId["Secondary"] = 2] = "Secondary";
    /** Fourth button, typically the Browser Back button */
    PredefinedPointerButtonId[PredefinedPointerButtonId["Fourth"] = 3] = "Fourth";
    /** Fifth button, typically the Browser Forward button */
    PredefinedPointerButtonId[PredefinedPointerButtonId["Fifth"] = 4] = "Fifth";
})(PredefinedPointerButtonId || (PredefinedPointerButtonId = {}));
/**
 * Parse the `MouseEvent.buttons` property into a collection of buttons ids corresponding to possible ids
 * that `MouseEvent.button` can assume. In essence a collection of currently pressed pointer event buttons.
 * @param mouseEvent `MouseEvent.buttons`
 * @returns Collection of buttons ids.
 */
export function getMouseEventPressedButtons(mouseEvent) {
    const pressedButtons = new Set();
    for (let button = 0; button < 32; ++button) {
        const buttonsIdMask = 1 << button;
        if (buttonsIdMask > mouseEvent.buttons) {
            break;
        }
        if ((mouseEvent.buttons & buttonsIdMask) > 0) {
            // Flip Auxiliary and Secondary as the spec is like that and the values differ between buttons and button
            // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/buttons
            // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
            if (buttonsIdMask === 2) {
                pressedButtons.add(2);
            }
            else if (buttonsIdMask === 4) {
                pressedButtons.add(1);
            }
            else {
                pressedButtons.add(button);
            }
        }
    }
    return pressedButtons;
}
/**
 * Creates a
 * @hidden
 * @internal
 * @param scene Scene
 * @returns Observable
 */
export function createPointerInfoEnricherAndForwarder(camera) {
    // Contains timestamps for when a button was pressed and released
    const pointerButtonTimings = new Map();
    const noPointerButtonInfo = Object.freeze({ event: 'none' });
    return (eventData, dstObservable) => {
        try {
            // Enrich original PointerInfo instance with additional useful methods
            // which can be used to find out which buttons are pressed, how long they have been pressed
            // etc.
            const eD = eventData;
            eD.twinfinity = {
                button: (buttonId) => {
                    return pointerButtonTimings.get(buttonId) ?? noPointerButtonInfo;
                },
                pick: (isGeometryIntersectionEnabled) => {
                    return camera.twinfinity.pick({
                        type: PickOptionType.Canvas,
                        position: PredefinedCanvasPosition.Mouse,
                        isGeometryIntersectionEnabled
                    });
                }
            };
            const now = performance.now();
            if (eventData.type === PointerEventTypes.POINTERDOWN) {
                pointerButtonTimings.set(eventData.event.button, {
                    event: 'down',
                    button: eventData.event.button,
                    downAt: now,
                    duration: 0
                });
            }
            else if (eventData.type === PointerEventTypes.POINTERUP) {
                const existingButtonTiming = pointerButtonTimings.get(eventData.event.button);
                if (existingButtonTiming?.event === 'down') {
                    pointerButtonTimings.set(eventData.event.button, {
                        event: 'up',
                        button: eventData.event.button,
                        downAt: existingButtonTiming.downAt,
                        upAt: now,
                        duration: now - existingButtonTiming.downAt
                    });
                }
            }
            else {
                // Attempt to update timings for all currently pressed buttons
                // We end up here when other pointer events occur, wheel, tap, double tap and move etc.
                for (const pointerButtonTiming of pointerButtonTimings.values()) {
                    if (pointerButtonTiming.event !== 'none') {
                        pointerButtonTiming.duration = now - pointerButtonTiming.downAt;
                    }
                }
            }
            dstObservable.notifyObservers(eD);
        }
        finally {
            // Clear pointer timings for button when its released. We rely on the fact
            // that buttons will contain the pressed button during all pointer events up until its released
            // so when PointerEventTypes.POINTERUP occurs then eventData.event.buttons no longer contains
            // the button. Therefore we know that we can remove the button from pointerButtonTimings. We do it like
            // this because it is more robust in cases where canvas looses focus and we therefore loose a PointerEventTypes.POINTERUP
            // event.
            // const pressedButtons = getMouseEventPressedButtons(eventData.event);
            // const buttonTimingKeys = [...pointerButtonTimings.keys()];
            // for (const buttonTimingId of buttonTimingKeys) {
            //     if (!pressedButtons.has(buttonTimingId)) {
            //         pointerButtonTimings.delete(buttonTimingId);
            //     }
            // }
            if (eventData.type === PointerEventTypes.POINTERUP) {
                pointerButtonTimings.delete(eventData.event.button);
            }
        }
    };
}
//# sourceMappingURL=PointerInfoWithTimings.js.map