import { Plugin, PluginKey } from "../../../../../../pm/state.js";
import { findParentNode, findParentNodeClosestToPos, findSelectedNodeOfType } from "../../../../../../pm/utils.js";
import { Decoration, DecorationSet } from "../../../../../../pm/view.js";
import { throttle } from "../../../../../../lodash.js";
import { isTableCellNode, isTableNode } from "../../../../../../table-utils/index.js";
import { TableDesc } from "../../../../../../table-utils/table-desc.js";
import { YfmTableNode, yfmTableType } from "../../../YfmTableSpecs/index.js";
import { YfmTableDecorationType as DecoType, YfmTableDecorationTypeKey as decoTypeKey, } from "../const.js";
import { yfmTableCellView } from "../nodeviews/yfm-table-cell-view.js";
import { yfmTableView } from "../nodeviews/yfm-table-view.js";
const pluginKey = new PluginKey('TableControlsPlugin');
const FOCUSED_CLASSNAME = 'yfm-table-focused';
function shouldUpdateState(prev, curr) {
    if (prev === null && curr === null)
        return false;
    if (prev === null || curr === null)
        return true;
    if (prev.rowIdx === curr.rowIdx &&
        prev.columnIdx === curr.columnIdx &&
        prev.tablePos === curr.tablePos)
        return false;
    return true;
}
export function hideHoverDecos(tr) {
    const meta = { newState: null };
    return tr.setMeta(pluginKey, meta);
}
export const yfmTableFocusPlugin = (opts) => {
    return new Plugin({
        key: pluginKey,
        state: {
            init(_config, state) {
                return updateAfterChanges(state);
            },
            apply: (tr, prev) => {
                if (tr.selectionSet || tr.docChanged) {
                    return updateAfterChanges(tr, prev);
                }
                const meta = tr.getMeta(pluginKey);
                if (meta) {
                    const plState = { ...prev };
                    plState.decorations = plState.decorations.map(tr.mapping, tr.doc);
                    {
                        const decosToRemove = plState.decorations.find(undefined, undefined, (spec) => spec[decoTypeKey] === DecoType.ShowRowControl ||
                            spec[decoTypeKey] === DecoType.ShowColumnControl);
                        plState.decorations = plState.decorations.remove(decosToRemove);
                    }
                    plState.hover = meta.newState;
                    if (plState.hover && plState.activeTablePos === plState.hover.tablePos) {
                        const table = tr.doc.nodeAt(plState.activeTablePos);
                        const tableDesc = TableDesc.create(table);
                        const rowCell = tableDesc.getRelativePosForCell(plState.hover.rowIdx, 0);
                        const columnCell = tableDesc.getRelativePosForCell(0, plState.hover.columnIdx);
                        if (rowCell.type === 'real') {
                            plState.decorations = plState.decorations.add(tr.doc, [
                                Decoration.node(plState.activeTablePos + rowCell.from, plState.activeTablePos + rowCell.to, {}, { [decoTypeKey]: DecoType.ShowRowControl }),
                            ]);
                        }
                        if (columnCell.type === 'real') {
                            plState.decorations = plState.decorations.add(tr.doc, [
                                Decoration.node(plState.activeTablePos + columnCell.from, plState.activeTablePos + columnCell.to, {}, { [decoTypeKey]: DecoType.ShowColumnControl }),
                            ]);
                        }
                    }
                    return plState;
                }
                return { ...prev, decorations: prev.decorations.map(tr.mapping, tr.doc) };
            },
        },
        props: {
            nodeViews: {
                [YfmTableNode.Table]: yfmTableView,
                [YfmTableNode.Cell]: yfmTableCellView(opts),
            },
            decorations(state) {
                return pluginKey.getState(state)?.decorations;
            },
            handleDOMEvents: {
                mousemove: throttle((view, e) => {
                    let hover = null;
                    if (e.target instanceof Element &&
                        e.target.closest('td') &&
                        e.target.closest(`.${FOCUSED_CLASSNAME}`)) {
                        const $pos = (() => {
                            const td = e.target.closest('td');
                            const domPos = td && view.posAtDOM(td, 0);
                            return domPos === null ? null : view.state.doc.resolve(domPos);
                        })() ??
                            (() => {
                                const pos = view.posAtCoords({ left: e.x, top: e.y });
                                return pos && view.state.doc.resolve(pos.pos);
                            })();
                        const tcell = $pos && findParentNodeClosestToPos($pos, isTableCellNode);
                        const table = $pos && findParentNodeClosestToPos($pos, isTableNode);
                        const tableDesc = table && TableDesc.create(table.node);
                        const cellInfo = tcell && tableDesc?.getCellInfo(tcell.node);
                        if (cellInfo) {
                            hover = {
                                tablePos: table.pos,
                                rowIdx: cellInfo.row,
                                columnIdx: cellInfo.column,
                            };
                            {
                                const rowFirstCell = tableDesc.rowsDesc[hover.rowIdx].cells[0];
                                if (rowFirstCell.type === 'virtual')
                                    hover.rowIdx = rowFirstCell.rowspan[0];
                            }
                            {
                                const colFirstCell = tableDesc.rowsDesc[0].cells[hover.columnIdx];
                                if (colFirstCell.type === 'virtual')
                                    hover.columnIdx = colFirstCell.colspan[1];
                            }
                        }
                    }
                    if (shouldUpdateState(pluginKey.getState(view.state)?.hover || null, hover)) {
                        const meta = { newState: hover };
                        view.dispatch(view.state.tr.setMeta(pluginKey, meta));
                    }
                }, 100),
            },
        },
    });
};
function updateAfterChanges(tr, prev) {
    const table = findParentNode(isTableNode)(tr.selection) ||
        findSelectedNodeOfType(yfmTableType(tr.doc.type.schema))(tr.selection);
    if (table) {
        const tableFrom = table.pos;
        const tableTo = tableFrom + table.node.nodeSize;
        const decos = prev
            ? prev.decorations
                .map(tr.mapping, tr.doc)
                .find(tableFrom, tableTo, (spec) => spec[decoTypeKey] === DecoType.ShowRowControl ||
                spec[decoTypeKey] === DecoType.ShowColumnControl)
            : [];
        return {
            hover: null,
            activeTablePos: table.pos,
            decorations: DecorationSet.create(tr.doc, [
                ...decos,
                Decoration.node(table.pos, table.pos + table.node.nodeSize, { class: FOCUSED_CLASSNAME }, { [decoTypeKey]: DecoType.FocusTable }),
            ]),
        };
    }
    else {
        return {
            hover: null,
            activeTablePos: null,
            decorations: DecorationSet.empty,
        };
    }
}
//# sourceMappingURL=focus-plugin.js.map
