// Copyright 2024 The Lynx Authors. All rights reserved.
// Licensed under the Apache License Version 2.0 that can be found in the
// LICENSE file in the root directory of this source tree.
import { onWorkletCtxUpdate } from '@lynx-js/react/worklet-runtime/bindings';
import { GestureTypeInner } from './types.js';
function isSerializedGesture(gesture) {
    return gesture.__isSerialized ?? false;
}
function getSerializedBaseGesture(gesture) {
    if (!gesture || !isSerializedGesture(gesture)) {
        return undefined;
    }
    if (gesture.type !== GestureTypeInner.COMPOSED) {
        return gesture;
    }
    return undefined;
}
function appendUniqueSerializedBaseGestures(gesture, out, seenIds) {
    if (!gesture || !isSerializedGesture(gesture)) {
        return;
    }
    if (gesture.type === GestureTypeInner.COMPOSED) {
        for (const subGesture of gesture.gestures) {
            appendUniqueSerializedBaseGestures(subGesture, out, seenIds);
        }
        return;
    }
    const baseGesture = gesture;
    if (seenIds.has(baseGesture.id)) {
        return;
    }
    seenIds.add(baseGesture.id);
    out.push(baseGesture);
}
function collectOldGestureInfo(oldGesture) {
    const uniqOldBaseGestures = [];
    const oldBaseGesturesById = new Map();
    appendOldGestureInfo(oldGesture, uniqOldBaseGestures, oldBaseGesturesById);
    return {
        uniqOldBaseGestures,
        oldBaseGesturesById,
    };
}
function appendOldGestureInfo(gesture, out, byId) {
    if (!gesture || !isSerializedGesture(gesture)) {
        return;
    }
    if (gesture.type === GestureTypeInner.COMPOSED) {
        for (const subGesture of gesture.gestures) {
            appendOldGestureInfo(subGesture, out, byId);
        }
        return;
    }
    const oldBaseGesture = gesture;
    if (!byId.has(oldBaseGesture.id)) {
        byId.set(oldBaseGesture.id, oldBaseGesture);
        out.push(oldBaseGesture);
    }
}
function consumeOldBaseGesture(baseGesture, uniqOldBaseGestures, oldBaseGesturesById) {
    const idMatchedOldBaseGesture = oldBaseGesturesById.get(baseGesture.id);
    if (idMatchedOldBaseGesture) {
        oldBaseGesturesById.delete(baseGesture.id);
        return idMatchedOldBaseGesture;
    }
    const fallbackOldBaseGesture = uniqOldBaseGestures.find(oldBaseGesture => oldBaseGesturesById.has(oldBaseGesture.id));
    if (!fallbackOldBaseGesture) {
        return undefined;
    }
    oldBaseGesturesById.delete(fallbackOldBaseGesture.id);
    return fallbackOldBaseGesture;
}
function removeGestureDetector(dom, id) {
    // Keep compatibility with old runtimes where remove API is not exposed.
    if (typeof __RemoveGestureDetector === 'function') {
        __RemoveGestureDetector(dom, id);
    }
}
function clearLegacyGestureState(dom) {
    __SetAttribute(dom, 'has-react-gesture', null);
    // `flatten` may still be required by unrelated attrs from the same spread
    // (e.g. `clip-radius`), so only clear the gesture-specific legacy state here.
    // When `__RemoveGestureDetector` is available, let it own the detector cleanup
    // so we do not clobber an unrelated user-provided `gesture` attr.
    if (typeof __RemoveGestureDetector !== 'function') {
        __SetAttribute(dom, 'gesture', null);
    }
}
function getGestureInfo(gesture, oldGesture, isFirstScreen, dom) {
    const config = {
        callbacks: [],
    };
    const baseGesture = gesture;
    if (baseGesture.config) {
        config.config = baseGesture.config;
    }
    for (const key of Object.keys(baseGesture.callbacks)) {
        const callback = baseGesture.callbacks[key];
        const oldCallback = oldGesture?.callbacks[key];
        onWorkletCtxUpdate(callback, oldCallback, isFirstScreen, dom);
        config.callbacks.push({
            name: key,
            callback: callback,
        });
    }
    const relationMap = {
        waitFor: baseGesture?.waitFor?.map(subGesture => subGesture.id) ?? [],
        simultaneous: baseGesture?.simultaneousWith?.map(subGesture => subGesture.id) ?? [],
        continueWith: baseGesture?.continueWith?.map(subGesture => subGesture.id) ?? [],
    };
    return {
        config,
        relationMap,
    };
}
export function processGesture(dom, gesture, oldGesture, isFirstScreen, gestureOptions) {
    const domSet = gestureOptions?.domSet === true;
    if (!gesture || !isSerializedGesture(gesture)) {
        const { oldBaseGesturesById } = collectOldGestureInfo(oldGesture);
        for (const oldBaseGesture of oldBaseGesturesById.values()) {
            removeGestureDetector(dom, oldBaseGesture.id);
        }
        // Clearing the attrs keeps the legacy main-thread state in sync when
        // gesture props disappear during spread/key-removal updates.
        if (!domSet && oldBaseGesturesById.size > 0) {
            clearLegacyGestureState(dom);
        }
        return;
    }
    const { uniqOldBaseGestures, oldBaseGesturesById } = collectOldGestureInfo(oldGesture);
    // Fast path for the most common case: single base gesture update.
    const singleBaseGesture = getSerializedBaseGesture(gesture);
    const singleOldBaseGesture = getSerializedBaseGesture(oldGesture);
    if (singleBaseGesture && (!oldGesture || singleOldBaseGesture)) {
        if (!domSet) {
            __SetAttribute(dom, 'has-react-gesture', true);
            __SetAttribute(dom, 'flatten', false);
        }
        if (singleOldBaseGesture) {
            // On update, remove old detector first to avoid stale callbacks.
            removeGestureDetector(dom, singleOldBaseGesture.id);
        }
        const { config, relationMap } = getGestureInfo(singleBaseGesture, singleOldBaseGesture, isFirstScreen, dom);
        __SetGestureDetector(dom, singleBaseGesture.id, singleBaseGesture.type, config, relationMap);
        return;
    }
    const uniqBaseGestures = [];
    appendUniqueSerializedBaseGestures(gesture, uniqBaseGestures, new Set());
    if (uniqBaseGestures.length === 0) {
        for (const oldBaseGesture of oldBaseGesturesById.values()) {
            removeGestureDetector(dom, oldBaseGesture.id);
        }
        if (!domSet && oldBaseGesturesById.size > 0) {
            clearLegacyGestureState(dom);
        }
        return;
    }
    if (!domSet) {
        __SetAttribute(dom, 'has-react-gesture', true);
        __SetAttribute(dom, 'flatten', false);
    }
    // On update, remove old detectors first to avoid stale callbacks.
    for (const oldBaseGesture of oldBaseGesturesById.values()) {
        removeGestureDetector(dom, oldBaseGesture.id);
    }
    for (const baseGesture of uniqBaseGestures) {
        const oldBaseGesture = consumeOldBaseGesture(baseGesture, uniqOldBaseGestures, oldBaseGesturesById);
        const { config, relationMap } = getGestureInfo(baseGesture, oldBaseGesture, isFirstScreen, dom);
        __SetGestureDetector(dom, baseGesture.id, baseGesture.type, config, relationMap);
    }
}
//# sourceMappingURL=processGesture.js.map