// 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 { ListUpdateInfoRecording } from '../listUpdateInfo.js';
import { __pendingListUpdates } from '../pendingListUpdates.js';
import { SnapshotInstance } from '../snapshot.js';
import { isDirectOrDeepEqual, isEmptyObject, pick } from '../utils.js';
import { updateEvent } from './event.js';
import { updateGesture } from './gesture.js';
import { platformInfoAttributes, updateListItemPlatformInfo } from './platformInfo.js';
import { transformRef, updateRef } from './ref.js';
import { updateWorkletEvent } from './workletEvent.js';
import { updateWorkletRef } from './workletRef.js';
// eslint-disable-next-line regexp/no-unused-capturing-group
const eventRegExp = /^(([A-Za-z-]*):)?(bind|catch|capture-bind|capture-catch|global-bind)([A-Za-z]+)$/;
const eventTypeMap = {
    bind: 'bindEvent',
    catch: 'catchEvent',
    'capture-bind': 'capture-bind',
    'capture-catch': 'capture-catch',
    'global-bind': 'global-bindEvent',
};
const noFlattenAttributes = /* @__PURE__ */ new Set([
    'name',
    'clip-radius',
    'overlap',
    'exposure-scene',
    'exposure-id',
]);
function updateSpread(snapshot, index, oldValue, elementIndex) {
    oldValue ??= {};
    let newValue = snapshot.__values[index]; // compiler guarantee this must be an object;
    const list = snapshot.parentNode;
    if (list?.__snapshot_def.isListHolder) {
        const oldPlatformInfo = pick(oldValue, platformInfoAttributes);
        const platformInfo = pick(newValue, platformInfoAttributes);
        if (!isDirectOrDeepEqual(oldPlatformInfo, platformInfo)) {
            if (__pendingListUpdates.values) {
                (__pendingListUpdates.values[list.__id] ??= new ListUpdateInfoRecording(list)).onSetAttribute(snapshot, platformInfo, oldPlatformInfo);
            }
            snapshot.__listItemPlatformInfo = platformInfo;
            // The fakeSnapshot is missing `__parent`, so no `ListUpdateInfoRecording#onSetAttribute` will be called
            const fakeSnapshot = {
                __values: {
                    get [index]() {
                        return platformInfo;
                    },
                },
                __id: snapshot.__id,
                __elements: snapshot.__elements,
            };
            updateListItemPlatformInfo(fakeSnapshot, index, oldPlatformInfo, elementIndex);
        }
    }
    if ('__spread' in newValue) {
        // first screen
        newValue = transformSpread(snapshot, index, newValue);
        snapshot.__values[index] = newValue;
    }
    if (!snapshot.__elements) {
        return;
    }
    const dataset = {};
    let match = null;
    for (const key in newValue) {
        const v = newValue[key];
        if (v !== oldValue[key]) {
            if (key === 'className') {
                __SetClasses(snapshot.__elements[elementIndex], v);
            }
            else if (key === 'style') {
                if (!isDirectOrDeepEqual(v, oldValue[key])) {
                    __SetInlineStyles(snapshot.__elements[elementIndex], v);
                }
            }
            else if (key === 'id') {
                __SetID(snapshot.__elements[elementIndex], v);
            }
            else if (key.startsWith('data-')) {
                // collected below
            }
            else if (key === 'ref') {
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return v;
                        },
                        set [index](value) {
                            // Modifications to the ref value should be reflected in the corresponding position of the spread.
                            newValue[key] = value;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                };
                updateRef(fakeSnapshot, index, oldValue[key], elementIndex);
            }
            else if (key.endsWith(':ref')) {
                snapshot.__worklet_ref_set ??= new Set();
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return v;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                    __worklet_ref_set: snapshot.__worklet_ref_set,
                };
                updateWorkletRef(fakeSnapshot, index, oldValue[key], elementIndex, key.slice(0, -4));
            }
            else if (key.endsWith(':gesture')) {
                const workletType = key.slice(0, -8);
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return v;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                };
                updateGesture(fakeSnapshot, index, oldValue[key], elementIndex, workletType);
            }
            else if ((match = eventRegExp.exec(key))) {
                const workletType = match[2];
                const eventType = eventTypeMap[match[3]];
                const eventName = match[4];
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return v;
                        },
                        set [index](value) {
                            // Modifications to the event value should be reflected in the corresponding position of the spread.
                            newValue[key] = value;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                };
                if (workletType) {
                    updateWorkletEvent(fakeSnapshot, index, oldValue[key], elementIndex, workletType, eventType, eventName);
                }
                else {
                    updateEvent(fakeSnapshot, index, oldValue[key], elementIndex, eventType, eventName, key);
                }
            }
            else if (platformInfoAttributes.has(key)) {
                // ignore
            }
            else {
                __SetAttribute(snapshot.__elements[elementIndex], key, v);
            }
        }
        // collect data regardless of whether it has changed
        if (key.startsWith('data-')) {
            dataset[key.slice(5)] = v;
        }
    }
    let hasOldDataset = false;
    for (const key in oldValue) {
        if (!(key in newValue)) {
            if (key === 'className') {
                __SetClasses(snapshot.__elements[elementIndex], '');
            }
            else if (key === 'style') {
                __SetInlineStyles(snapshot.__elements[elementIndex], '');
            }
            else if (key === 'id') {
                __SetID(snapshot.__elements[elementIndex], null);
            }
            else if (key.startsWith('data-')) {
                // collected below
            }
            else if (key === 'ref') {
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return undefined;
                        },
                        set [index](value) {
                            // Modifications to the ref value should be reflected in the corresponding position of the spread.
                            newValue[key] = value;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                };
                updateRef(fakeSnapshot, index, oldValue[key], elementIndex);
            }
            else if (key.endsWith(':ref')) {
                snapshot.__worklet_ref_set ??= new Set();
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return undefined;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                    __worklet_ref_set: snapshot.__worklet_ref_set,
                };
                updateWorkletRef(fakeSnapshot, index, oldValue[key], elementIndex, key.slice(0, -4));
            }
            else if (key.endsWith(':gesture')) {
                const workletType = key.slice(0, -8);
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return undefined;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                };
                updateGesture(fakeSnapshot, index, oldValue[key], elementIndex, workletType);
            }
            else if ((match = eventRegExp.exec(key))) {
                const workletType = match[2];
                const eventType = eventTypeMap[match[3]];
                const eventName = match[4];
                const fakeSnapshot = {
                    __values: {
                        get [index]() {
                            return undefined;
                        },
                        set [index](value) {
                            newValue[key] = value;
                        },
                    },
                    __id: snapshot.__id,
                    __elements: snapshot.__elements,
                };
                if (workletType) {
                    updateWorkletEvent(fakeSnapshot, index, oldValue[key], elementIndex, workletType, eventType, eventName);
                }
                else {
                    updateEvent(fakeSnapshot, index, oldValue[key], elementIndex, eventType, eventName, key);
                }
            }
            else if (platformInfoAttributes.has(key)) {
                // ignore
            }
            else {
                __SetAttribute(snapshot.__elements[elementIndex], key, null);
            }
        }
        // collect data regardless of whether it has changed
        if (key.startsWith('data-')) {
            hasOldDataset = true;
        }
    }
    // TODO: compare dataset before commit it to native?
    if (hasOldDataset || !isEmptyObject(dataset)) {
        __SetDataset(snapshot.__elements[elementIndex], dataset);
    }
}
function transformSpread(snapshot, index, spread) {
    const result = {};
    let hasNoFlattenAttributes = false;
    for (const key in spread) {
        let value = spread[key];
        if (key === '__spread') { }
        else if (key === 'class' || key === 'className') {
            value ??= '';
            result['className'] = value;
        }
        else if (key === 'ref') {
            if (__LEPUS__) {
                result[key] = value ? 1 : undefined;
            }
            else {
                result[key] = transformRef(value)?.__ref;
            }
        }
        else if (typeof value === 'function') {
            result[key] = `${snapshot.__id}:${index}:${key}`;
        }
        else if (key === '__self' || key === '__source') {
            // for react debug tools
        }
        else {
            if (!hasNoFlattenAttributes && noFlattenAttributes.has(key)) {
                hasNoFlattenAttributes = true;
            }
            result[key] = value;
        }
    }
    if (hasNoFlattenAttributes) {
        result['flatten'] = false;
    }
    return result;
}
export { transformSpread, updateSpread };
//# sourceMappingURL=spread.js.map