"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.DndLayout = exports.DndDropableNode = exports.DndUndropableNode = void 0;
const react_1 = __importDefault(require("react"));
const emotion_css_1 = require("../utils/emotion-css");
const react_dnd_1 = require("react-dnd");
const dom_utils_1 = require("../utils/dom-utils");
const floating_ui_1 = require("./floating-ui");
const dom_api_1 = require("./dom-api");
const provider_1 = require("./provider");
const get_from_main_1 = require("../utils/get-from-main");
const backlist_dom_1 = require("../config/backlist-dom");
const { api } = (0, get_from_main_1.getSharedFromMain)();
function isDropableDom(dom) {
    if (dom.localName === EMPTY_PLACEHOLDER_NAME) {
        return false;
    }
    const res = (0, dom_utils_1.parseReactDomNodeDeep)(dom);
    // return !!res?.mometa
    // 寻找第一个 fiber 节点 (stateNode === dom)
    let f = res === null || res === void 0 ? void 0 : res.fiber;
    while (f && f.stateNode !== dom) {
        f = f.child;
    }
    return !!f;
}
const EMPTY_PLACEHOLDER_NAME = 'mometa-empty-placeholder';
function useDfsDom(dom) {
    const [domChildren, setDomChildren] = react_1.default.useState([]);
    const domChildrenRef = react_1.default.useRef();
    domChildrenRef.current = domChildren;
    const getDomChildren = react_1.default.useCallback(() => {
        if (dom === null || dom === void 0 ? void 0 : dom.parentNode) {
            return Array.from((dom === null || dom === void 0 ? void 0 : dom.children) || []);
        }
        return [];
    }, [dom]);
    react_1.default.useLayoutEffect(() => {
        if (!dom) {
            setDomChildren(getDomChildren());
            return;
        }
        const ob = new MutationObserver((records) => {
            const newChildren = domChildrenRef.current.slice();
            let isUpdate = false;
            for (const record of records) {
                if (record.removedNodes) {
                    // eslint-disable-next-line no-loop-func
                    record.removedNodes.forEach((item) => {
                        const i = newChildren.indexOf(item);
                        if (i >= 0) {
                            isUpdate = true;
                            newChildren.splice(i, 1);
                        }
                    });
                }
                if (record.addedNodes) {
                    // eslint-disable-next-line no-loop-func
                    record.addedNodes.forEach((item) => {
                        const i = newChildren.indexOf(item);
                        if (i < 0) {
                            isUpdate = true;
                            newChildren.push(item);
                        }
                    });
                }
            }
            if (isUpdate) {
                setDomChildren(newChildren);
            }
        });
        ob.observe(dom, {
            subtree: false,
            attributes: false,
            childList: true
        });
        setDomChildren(getDomChildren());
        return () => {
            ob.disconnect();
        };
    }, [dom, getDomChildren]);
    return [domChildren];
}
exports.DndUndropableNode = react_1.default.memo(({ dom }) => {
    const [domChildren] = useDfsDom(dom);
    if (!(dom === null || dom === void 0 ? void 0 : dom.parentNode)) {
        return null;
    }
    return (
    // eslint-disable-next-line react/jsx-no-useless-fragment
    react_1.default.createElement(react_1.default.Fragment, null, domChildren === null || domChildren === void 0 ? void 0 : domChildren.map((child, key) => (
    // eslint-disable-next-line react/no-array-index-key
    react_1.default.createElement(DndNode, { key: key, dom: child })))));
});
const DndNode = react_1.default.memo(function ({ dom }) {
    if (dom && dom.parentNode && !(0, backlist_dom_1.getBlackListDom)().includes(dom)) {
        return isDropableDom(dom) ? react_1.default.createElement(exports.DndDropableNode, { dom: dom }) : react_1.default.createElement(exports.DndUndropableNode, { dom: dom });
    }
    return null;
});
function useMometaDomInject(dom) {
    react_1.default.useLayoutEffect(() => {
        var _a;
        if (!dom) {
            return;
        }
        const mometaApi = (dom.__mometa = new dom_api_1.MometaDomApi(dom));
        (_a = dom.removeAttribute) === null || _a === void 0 ? void 0 : _a.call(dom, '__mometa');
        if (dom.dataset) {
            dom.dataset.mometaKey = mometaApi.getKey();
        }
        return () => {
            var _a;
            (_a = dom.dataset) === null || _a === void 0 ? true : delete _a.mometaKey;
            delete dom.__mometa;
        };
    }, [dom]);
}
exports.DndDropableNode = react_1.default.memo(({ dom }) => {
    const [overingNode, setOveringNode] = (0, get_from_main_1.useOveringNode)();
    const [selNode, setSelNode] = (0, get_from_main_1.useSelectedNode)();
    const isSelected = selNode === dom && !!dom;
    const overingUiRef = react_1.default.useRef(null);
    const [str, drop] = (0, react_dnd_1.useDrop)(() => ({
        accept: ['asset', 'dom'],
        drop(item, monitor) {
            var _a, _b, _c, _d;
            if (monitor.isOver({ shallow: true }) && overingUiRef.current) {
                const clientOffset = monitor.getClientOffset();
                const children = overingUiRef.current.children;
                const matchedDom = Array.from(children).find((dom) => {
                    const rect = dom.getBoundingClientRect();
                    if (clientOffset.x >= rect.x &&
                        clientOffset.x <= rect.x + rect.width &&
                        clientOffset.y >= rect.y &&
                        clientOffset.y <= rect.y + rect.height) {
                        return true;
                    }
                });
                const itemType = monitor.getItemType();
                console.log('itemType', { itemType, item, pos: (_a = matchedDom === null || matchedDom === void 0 ? void 0 : matchedDom.dataset) === null || _a === void 0 ? void 0 : _a.pos });
                if ((_b = matchedDom === null || matchedDom === void 0 ? void 0 : matchedDom.dataset) === null || _b === void 0 ? void 0 : _b.pos) {
                    if (itemType === 'asset') {
                        return api.handleViewOp('insert-asset', dom, {
                            asset: item.data,
                            direction: (_c = matchedDom === null || matchedDom === void 0 ? void 0 : matchedDom.dataset) === null || _c === void 0 ? void 0 : _c.pos
                        });
                    }
                    else if (itemType === 'dom') {
                        return api.handleViewOp('move-dom', item, {
                            toDom: dom,
                            direction: (_d = matchedDom === null || matchedDom === void 0 ? void 0 : matchedDom.dataset) === null || _d === void 0 ? void 0 : _d.pos
                        });
                    }
                }
            }
        },
        collect: (monitor) => {
            const itemType = monitor.getItemType();
            const item = monitor.getItem();
            let isOverCurrent = false;
            if (itemType !== 'dom') {
                isOverCurrent = monitor.isOver({ shallow: true });
            }
            else {
                // dom 下只能允许同名文件内移动 & 不同 dom
                isOverCurrent =
                    item !== dom &&
                        monitor.isOver({ shallow: true }) &&
                        !!(item === null || item === void 0 ? void 0 : item.__mometa) &&
                        item.__mometa.getMometaData().filename === dom.__mometa.getMometaData().filename;
            }
            // dnd 在 window.parent 环境中，当前 useDrog 在 iframe 环境中，使用 collect 返回 object，对比会出现不匹配的情况
            // {}.valueOf !== window.parent.Object.prototype.valueOf
            return JSON.stringify({
                isOverCurrent,
                isOver: monitor.isOver()
            });
        }
    }), [dom, isSelected]);
    const [{ canSelect }] = (0, get_from_main_1.useHeaderStatus)();
    const { isOverCurrent, isOver } = react_1.default.useMemo(() => JSON.parse(str), [str]);
    // eslint-disable-next-line consistent-return
    react_1.default.useLayoutEffect(() => {
        if (isOver) {
            return (0, dom_utils_1.addCss)(dom, (0, emotion_css_1.css) `
          ${EMPTY_PLACEHOLDER_NAME} {
            display: block !important;
          }
        `);
        }
    }, [isOver, dom]);
    const [isEnter, setIsEnter] = react_1.default.useState(false);
    useMometaDomInject(dom);
    // eslint-disable-next-line consistent-return
    react_1.default.useEffect(() => {
        if (!dom || !dom.__mometa) {
            return;
        }
        if (canSelect) {
            setIsEnter(overingNode === dom);
            const enterHandler = (evt) => {
                setOveringNode(dom);
                evt.stopPropagation();
            };
            const leaveHandler = (evt) => {
                if (dom === overingNode) {
                    setOveringNode(null);
                    evt.stopPropagation();
                }
            };
            dom.addEventListener('mouseover', enterHandler);
            dom.addEventListener('mouseout', leaveHandler);
            const disposes = [
                dom.__mometa.preventDefaultPop('click', true),
                dom.__mometa.preventDefaultPop('mousedown', true),
                dom.__mometa.preventDefaultPop('beforeinput', true)
            ];
            return () => {
                dom.removeEventListener('mouseover', enterHandler);
                dom.removeEventListener('mouseout', leaveHandler);
                disposes.forEach((fn) => fn());
            };
        }
        setSelNode(null);
        setOveringNode(null);
        setIsEnter(false);
    }, [dom, canSelect, setIsEnter, overingNode, setOveringNode, setSelNode]);
    const dropRef = react_1.default.useRef();
    dropRef.current = drop;
    react_1.default.useLayoutEffect(() => {
        if (!dom) {
            return;
        }
        dropRef.current(dom);
    }, [dom]);
    if (!(dom === null || dom === void 0 ? void 0 : dom.parentNode)) {
        return null;
    }
    return (react_1.default.createElement(react_1.default.Fragment, null,
        (isEnter || isSelected || isOverCurrent) && !!dom && (react_1.default.createElement(floating_ui_1.OveringFloat, { ref: overingUiRef, isOverCurrent: isOverCurrent, onSelect: () => setSelNode(dom), isSelected: isSelected, dom: dom })),
        !!dom && react_1.default.createElement(exports.DndUndropableNode, { dom: dom })));
});
function DndLayout({ dom }) {
    return (react_1.default.createElement(provider_1.Provider, null,
        react_1.default.createElement(DndNode, { dom: dom })));
}
exports.DndLayout = DndLayout;
//# sourceMappingURL=dnd.js.map