"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.makeClickListener = makeClickListener;
exports.listenClick = listenClick;
const callAll_1 = require("../../../util/call/callAll");
const addListener_1 = require("../../addListener");
const stopPropagation_1 = require("../../stopPropagation");
const geometry_1 = require("../../util/geometry");
const setTimeoutRAF_1 = require("../../util/timer/setTimeoutRAF");
const constants_1 = require("./constants");
const pointercancel_1 = require("./pointercancel");
const pointerdown_1 = require("./pointerdown");
const pointerleave_1 = require("./pointerleave");
const pointermove_1 = require("./pointermove");
const pointerup_1 = require("./pointerup");
function makeClickListener(handlers) {
    return (component) => {
        return listenClick(component, handlers);
    };
}
function listenClick(component, handlers) {
    const { $element, $system } = component;
    const { onClick, onClickCancel, onDoubleClick, onLongPress, onLongClickCancel, onLongClick, onClickHold, } = handlers;
    let tapCount = 0;
    let pointerDown = {};
    let lastPointerDownPosition;
    let pointerDownCount = 0;
    let pointerPosition = {};
    let pointerDownMaxDistance = {};
    let lastTapPosition;
    let longClickCancelPointerId = new Set();
    let longPress = {};
    let doubleClickTimeout = null;
    let longClickTimeout = null;
    let unlisten = undefined;
    let unlistenPointerMove = undefined;
    let unlistenPointerUp = undefined;
    let unlistenPointerLeave = undefined;
    let unlistenPointerCancel = undefined;
    const pointerDownListener = (event, _event) => {
        // console.log('pointerDownListener')
        const { pointerId } = event;
        if (!pointerDown[pointerId]) {
            const { clientX, clientY } = event;
            pointerDownCount++;
            if (pointerDownCount === 1) {
                unlistenPointerMove = (0, addListener_1.addListener)(component, (0, pointermove_1.makePointerMoveListener)(pointerMoveListener));
                unlistenPointerUp = (0, addListener_1.addListener)(component, (0, pointerup_1.makePointerUpListener)(pointerUpListener));
                unlistenPointerLeave = (0, addListener_1.addListener)(component, (0, pointerleave_1.makePointerLeaveListener)(pointerLeaveListener));
                unlistenPointerCancel = (0, addListener_1.addListener)(component, (0, pointercancel_1.makePointerCancelListener)(pointerCancelListener));
                unlisten = (0, callAll_1.callAll)([
                    unlistenPointerMove,
                    unlistenPointerUp,
                    unlistenPointerLeave,
                    unlistenPointerCancel,
                ]);
            }
            pointerPosition[pointerId] = { x: clientX, y: clientY };
            pointerDown[pointerId] = pointerPosition[pointerId];
            pointerDownMaxDistance[pointerId] = -Infinity;
            if (pointerDownCount === 1) {
                if (tapCount === 0 ||
                    (lastTapPosition &&
                        (0, geometry_1.pointDistance)(lastTapPosition, { x: clientX, y: clientY }) >
                            constants_1.POINTER_CLICK_RADIUS)) {
                    if (longClickTimeout) {
                        longClickTimeout();
                    }
                    longClickTimeout = (0, setTimeoutRAF_1.setTimeoutRAF)($system, () => {
                        if (tapCount === 0 &&
                            pointerDown[pointerId] &&
                            pointerDownCount === 1) {
                            longPress[pointerId] = true;
                            if (pointerDownMaxDistance[pointerId] <
                                constants_1.POINTER_LONG_PRESS_MAX_DELTA) {
                                onLongPress && onLongPress(event, _event);
                            }
                        }
                        longClickTimeout = null;
                    }, constants_1.LONG_CLICK_TIMEOUT);
                }
                if (tapCount === 1 &&
                    (0, geometry_1.pointDistance)(pointerPosition[pointerId], lastPointerDownPosition) <=
                        constants_1.POINTER_CLICK_RADIUS) {
                    (0, setTimeoutRAF_1.setTimeoutRAF)($system, () => {
                        if (pointerDown[pointerId] &&
                            pointerDownMaxDistance[pointerId] < constants_1.POINTER_CLICK_RADIUS) {
                            onClickHold && onClickHold(event);
                        }
                    }, constants_1.LONG_CLICK_TIMEOUT);
                }
            }
            else {
                if (longClickTimeout) {
                    longClickTimeout();
                    longPress = {};
                    longClickTimeout = null;
                }
            }
        }
    };
    const pointerMoveListener = (event) => {
        // console.log('pointerMoveListener')
        const { clientX, clientY, pointerId } = event;
        pointerPosition[pointerId] = { x: clientX, y: clientY };
        if (pointerDown[pointerId]) {
            const d = Math.max(pointerDownMaxDistance[pointerId], (0, geometry_1.pointDistance)(pointerDown[pointerId], pointerPosition[pointerId]));
            pointerDownMaxDistance[pointerId] = d;
            if (!longClickCancelPointerId.has(pointerId) &&
                d >= constants_1.POINTER_LONG_PRESS_MAX_DELTA) {
                longClickCancelPointerId.add(pointerId);
                onLongClickCancel && onLongClickCancel(event);
            }
            else if (d >= constants_1.POINTER_CLICK_RADIUS) {
                onClickCancel && onClickCancel(event);
            }
        }
    };
    const pointerCancelListener = ({ pointerId }) => {
        // console.log('pointerCancelListener')
        releasePointerDown(pointerId);
    };
    const pointerUpListener = (event, _event) => {
        // console.log('pointerUpListener', event.pointerId)
        const { pointerId, clientX, clientY } = event;
        const position = { x: clientX, y: clientY };
        if (pointerDown[pointerId]) {
            const pointerDownMovedDistance = (0, geometry_1.pointDistance)(pointerDown[pointerId], position);
            lastPointerDownPosition = pointerDown[pointerId];
            tapCount++;
            const longClickCancel = longClickCancelPointerId.has(pointerId);
            if (longClickCancel) {
                longClickCancelPointerId.delete(pointerId);
            }
            if (tapCount === 2 &&
                pointerDownMaxDistance[pointerId] > constants_1.POINTER_CLICK_RADIUS) {
                tapCount = 1;
                pointerDownMaxDistance[pointerId] = 0;
                if (doubleClickTimeout) {
                    doubleClickTimeout();
                    doubleClickTimeout = null;
                }
            }
            const d = (0, geometry_1.pointDistance)(position, lastPointerDownPosition);
            if (tapCount > 0 && tapCount % 2 === 1) {
                if (pointerDownMovedDistance < constants_1.POINTER_CLICK_RADIUS) {
                    doubleClickTimeout = (0, setTimeoutRAF_1.setTimeoutRAF)($system, () => {
                        tapCount = 0;
                        doubleClickTimeout = null;
                    }, constants_1.CLICK_TIMEOUT);
                }
                else {
                    tapCount = 0;
                    doubleClickTimeout = null;
                }
                if (d <= constants_1.POINTER_CLICK_RADIUS && !longClickCancel) {
                    if (longPress[pointerId]) {
                        onLongClick && onLongClick(event, _event);
                    }
                    else {
                        onClick && onClick(event, _event);
                    }
                }
                else {
                }
            }
            if (tapCount > 0 && tapCount % 2 === 0) {
                if (d <= constants_1.POINTER_CLICK_RADIUS &&
                    pointerDownMaxDistance[pointerId] <= constants_1.POINTER_CLICK_RADIUS &&
                    (0, geometry_1.pointDistance)(lastTapPosition, position) <= constants_1.POINTER_CLICK_RADIUS) {
                    if (onDoubleClick) {
                        onDoubleClick(event, _event);
                    }
                    else if (onClick) {
                        onClick(event, _event);
                    }
                }
                else {
                    onClick && onClick(event, _event);
                }
            }
            lastTapPosition = pointerPosition[pointerId];
            releasePointerDown(event);
        }
    };
    const pointerLeaveListener = (event) => {
        releasePointerDown(event);
    };
    const releasePointerDown = (event) => {
        const { pointerId } = event;
        if (pointerDown[pointerId]) {
            delete pointerDown[pointerId];
            pointerDownCount--;
            if (pointerDownCount === 0) {
                unlisten && unlisten();
                unlisten = undefined;
            }
            delete longPress[pointerId];
        }
        longClickCancelPointerId.delete(pointerId);
    };
    $element.addEventListener('click', stopPropagation_1.stopPropagation);
    $element.addEventListener('dblclick', stopPropagation_1.stopPropagation);
    const unlistenPointerDown = (0, pointerdown_1.listenPointerDown)(component, pointerDownListener);
    return () => {
        unlistenPointerDown();
        unlisten && unlisten();
        unlisten = undefined;
    };
}
//# sourceMappingURL=click.js.map