import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { getLoggerFromState } from "../../../../../../core/index.js";
import { findParentNodeClosestToPos } from "../../../../../../pm/utils.js";
import { getReactRendererFromState } from "../../../../../behavior/ReactRenderer/index.js";
import { range as iterate } from "../../../../../../lodash.js";
import { ErrorLoggerBoundary } from "../../../../../../react-utils/ErrorBoundary.js";
import { isTableNode } from "../../../../../../table-utils/index.js";
import { TableDesc, } from "../../../../../../table-utils/table-desc.js";
import { YfmTableAttr } from "../../../const.js";
import { removeYfmTable } from "../actions.js";
import { clearCells } from "../commands/clear-cells.js";
import { insertEmptyColumn } from "../commands/insert-empty-column.js";
import { insertEmptyRow } from "../commands/insert-empty-row.js";
import { removeColumnRange } from "../commands/remove-column-range.js";
import { removeRowRange } from "../commands/remove-row-range.js";
import { setCellBg } from "../commands/set-cell-bg.js";
import { canMakeRowHeader, toggleHeaderRows } from "../commands/toggle-header-rows.js";
import { FloatingMenuControl } from "../components/FloatingMenuControl/index.js";
import { YfmTableDecorationType as DecoType, YfmTableDecorationTypeKey as decoTypeKey, } from "../const.js";
import { YfmTableDnDHandler } from "../dnd/dnd.js";
import { activateColumns, activateRows, deactivateColumn, deactivateRow, } from "../plugins/dnd-plugin.js";
import { getSelectedCellsForColumns, getSelectedCellsForRows } from "../utils.js";
import "./yfm-table-cell-view.css";
const dropCursorParams = {
    color: 'var(--g-color-line-brand)',
    width: 2,
};
export const yfmTableCellView = (opts) => (node, view, getPos, decorations) => {
    return new YfmTableCellView(node, view, getPos, decorations, opts);
};
class YfmTableCellView {
    dom;
    contentDOM;
    _node;
    _view;
    _getPos;
    _renderer;
    _logger;
    _dndEnabled;
    _headerRowsEnabled;
    _cellBackgroundEnabled;
    _isHeader;
    _decoRowUniqKey = null;
    _decoColumnUniqKey = null;
    _cellInfo;
    _dndHandler;
    constructor(node, view, getPos, decorations, opts) {
        this._node = node;
        this._view = view;
        this._getPos = getPos;
        this._logger = getLoggerFromState(view.state).nested({
            node: 'yfm-table',
        });
        this._dndEnabled = opts.dndEnabled;
        this._headerRowsEnabled = opts.headerRowsEnabled;
        this._cellBackgroundEnabled = opts.cellBackgroundEnabled;
        this._isHeader = this._computeIsHeader(undefined);
        this.dom = document.createElement('td');
        this._updateDom();
        this.contentDOM = this.dom;
        this._cellInfo = null;
        this._dndHandler = null;
        this._renderer = getReactRendererFromState(view.state).createItem('yfm-table-cell-view', () => {
            if (!this._cellInfo)
                return null;
            const { showRowControl, showColumnControl, rowRange, columnRange, tablePos } = this._cellInfo;
            const tableElem = this._view.domAtPos(tablePos + 1).node;
            const currentCellBg = this._node.attrs[YfmTableAttr.CellBg] ?? null;
            return (_jsxs(ErrorLoggerBoundary, { children: [showRowControl && (_jsx(FloatingMenuControl, { type: "row", cellElement: this.dom, tableElement: tableElem, dndHandler: this._dndHandler?.row, multiple: rowRange.rowsCount > 1, onMenuOpenToggle: this._onRowControlOpenToggle, onClearCellsClick: this._onRowClearCellsClick, onInsertBeforeClick: this._onRowInsertBeforeClick, onInsertAfterClick: this._onRowInsertAfterClick, onRemoveRangeClick: this._onRowRemoveRangeClick, onRemoveTableClick: this._onRemoveTableClick, isRowHeader: this._cellInfo.isRowHeader, onRowHeaderChange: this._cellInfo.canToggleRowHeader
                            ? this._onRowHeaderChange
                            : undefined, currentCellBg: currentCellBg, onCellBgChange: this._cellBackgroundEnabled ? this._onRowSetCellBg : undefined })), showColumnControl && (_jsx(FloatingMenuControl, { type: "column", cellElement: this.dom, tableElement: tableElem, dndHandler: this._dndHandler?.column, multiple: columnRange.columnsCount > 1, onMenuOpenToggle: this._onColumnControlOpenToggle, onClearCellsClick: this._onColumnClearCellsClick, onInsertBeforeClick: this._onColumnInsertBeforeClick, onInsertAfterClick: this._onColumnInsertAfterClick, onRemoveRangeClick: this._onColumnRemoveRangeClick, onRemoveTableClick: this._onRemoveTableClick, currentCellBg: currentCellBg, onCellBgChange: this._cellBackgroundEnabled
                            ? this._onColumnSetCellBg
                            : undefined }))] }));
        });
        this.update(node, decorations);
    }
    update(node, decorations) {
        const prev = this._node;
        this._node = node;
        const cellInfo = this._getCellInfo();
        this._isHeader = this._computeIsHeader(cellInfo);
        this._updateDom(prev);
        if (cellInfo && (cellInfo.cell.row === 0 || cellInfo.cell.column === 0)) {
            const desc = cellInfo.tableDesc.base;
            const isRowHeader = desc.isHeaderRow(cellInfo.cell.row);
            const info = (this._cellInfo = {
                tablePos: cellInfo.table.pos,
                rowIndex: cellInfo.cell.row,
                columnIndex: cellInfo.cell.column,
                showRowControl: false,
                showColumnControl: false,
                rowRange: desc.getRowRangeByRowIdx(cellInfo.cell.row),
                columnRange: desc.getColumnRangeByColumnIdx(cellInfo.cell.column),
                isRowHeader,
                canToggleRowHeader: this._headerRowsEnabled &&
                    (isRowHeader || canMakeRowHeader(desc, cellInfo.cell.row)),
            });
            for (const deco of decorations) {
                const type = deco.spec[decoTypeKey];
                info.showRowControl ||= type === DecoType.ShowRowControl;
                info.showRowControl ||= type === DecoType.OpenRowMenu;
                info.showColumnControl ||= type === DecoType.ShowColumnControl;
                info.showColumnControl ||= type === DecoType.OpenColumnMenu;
            }
            if (this._dndEnabled && !this._dndHandler)
                this._dndHandler = new YfmTableDnDHandler(this._view, {
                    cellNode: node,
                    cellGetPos: this._getPos,
                    logger: this._logger,
                    dropCursor: dropCursorParams,
                });
            this._dndHandler?.update(node);
        }
        else {
            this._cellInfo = null;
            this._dndHandler?.destroy();
            this._dndHandler = null;
        }
        this._renderer.rerender();
        return true;
    }
    destroy() {
        this._dndHandler?.destroy();
        this._dndHandler = null;
        this._renderer.remove();
        this._onRowControlOpenToggle(false);
        this._onColumnControlOpenToggle(false);
    }
    _updateDom(prev) {
        if (this._isHeader) {
            if (this.dom.getAttribute('role') !== 'columnheader')
                this.dom.setAttribute('role', 'columnheader');
        }
        else if (this.dom.hasAttribute('role')) {
            this.dom.removeAttribute('role');
        }
        if (prev?.attrs[YfmTableAttr.CellAlign]) {
            this.dom.classList.remove(prev.attrs[YfmTableAttr.CellAlign]);
        }
        if (prev?.attrs[YfmTableAttr.CellBg]) {
            this.dom.classList.remove(`cell-bg-${prev.attrs[YfmTableAttr.CellBg]}`);
        }
        if (this._node.attrs[YfmTableAttr.Colspan])
            this.dom.setAttribute('colspan', this._node.attrs[YfmTableAttr.Colspan]);
        else
            this.dom.removeAttribute('colspan');
        if (this._node.attrs[YfmTableAttr.Rowspan])
            this.dom.setAttribute('rowspan', this._node.attrs[YfmTableAttr.Rowspan]);
        else
            this.dom.removeAttribute('rowspan');
        if (this._node.attrs[YfmTableAttr.CellAlign]) {
            this.dom.classList.add(this._node.attrs[YfmTableAttr.CellAlign]);
            this.dom.setAttribute(YfmTableAttr.CellAlign, this._node.attrs[YfmTableAttr.Rowspan]);
        }
        else {
            this.dom.removeAttribute(YfmTableAttr.CellAlign);
        }
        if (this._node.attrs[YfmTableAttr.CellBg] && this._cellBackgroundEnabled) {
            this.dom.classList.add(`cell-bg-${this._node.attrs[YfmTableAttr.CellBg]}`);
            this.dom.setAttribute(YfmTableAttr.CellBg, this._node.attrs[YfmTableAttr.CellBg]);
        }
        else {
            this.dom.removeAttribute(YfmTableAttr.CellBg);
        }
    }
    _onRowControlOpenToggle = (open) => {
        if (open === false) {
            if (this._decoRowUniqKey) {
                this._view.dispatch(deactivateRow(this._view.state.tr, this._decoRowUniqKey));
                this._decoRowUniqKey = null;
            }
            return;
        }
        this._logger.event({ event: 'open-row-menu' });
        const info = this._getCellInfo();
        if (!info)
            return;
        this._decoRowUniqKey = Date.now();
        const rowRange = info.tableDesc.base.getRowRangeByRowIdx(info.cell.row);
        const tr = activateRows(this._view.state.tr, {
            controlCell: { from: info.pos, to: info.pos + this._node.nodeSize },
            cells: getSelectedCellsForRows(info.tableDesc, rowRange),
            uniqKey: this._decoRowUniqKey,
        });
        this._view.dispatch(tr);
    };
    _onColumnControlOpenToggle = (open) => {
        if (open === false) {
            if (this._decoColumnUniqKey) {
                this._view.dispatch(deactivateColumn(this._view.state.tr, this._decoColumnUniqKey));
                this._decoColumnUniqKey = null;
            }
            return;
        }
        this._logger.event({ event: 'open-column-menu' });
        const info = this._getCellInfo();
        if (!info)
            return;
        this._decoColumnUniqKey = Date.now();
        const columnRange = info.tableDesc.base.getColumnRangeByColumnIdx(info.cell.column);
        const tr = activateColumns(this._view.state.tr, {
            controlCell: { from: info.pos, to: info.pos + this._node.nodeSize },
            cells: getSelectedCellsForColumns(info.tableDesc, columnRange),
            uniqKey: this._decoColumnUniqKey,
        });
        this._view.dispatch(tr);
    };
    _onRowClearCellsClick = () => {
        this._logger.event({ event: 'row-clear-cells', source: 'row-menu' });
        const info = this._getCellInfo();
        if (info) {
            const rowRange = info.tableDesc.base.getRowRangeByRowIdx(info.cell.row);
            clearCells({
                tablePos: info.table.pos,
                rows: iterate(rowRange.startIdx, rowRange.endIdx + 1),
            })(this._view.state, this._view.dispatch);
        }
        this._view.focus();
    };
    _onColumnClearCellsClick = () => {
        this._logger.event({ event: 'column-clear-cells', source: 'column-menu' });
        const info = this._getCellInfo();
        if (info) {
            const colRange = info.tableDesc.base.getColumnRangeByColumnIdx(info.cell.column);
            clearCells({
                tablePos: info.table.pos,
                cols: iterate(colRange.startIdx, colRange.endIdx + 1),
            })(this._view.state, this._view.dispatch);
        }
        this._view.focus();
    };
    _onRowSetCellBg = (bg) => {
        this._logger.event({
            event: 'row-set-cell-bg',
            source: 'row-menu',
            value: bg || 'no-color',
        });
        const info = this._getCellInfo();
        if (info) {
            const rowRange = info.tableDesc.base.getRowRangeByRowIdx(info.cell.row);
            setCellBg({
                tablePos: info.table.pos,
                rows: iterate(rowRange.startIdx, rowRange.endIdx + 1),
                bg,
            })(this._view.state, this._view.dispatch);
        }
    };
    _onColumnSetCellBg = (bg) => {
        this._logger.event({
            event: 'column-set-cell-bg',
            source: 'column-menu',
            value: bg || 'no-color',
        });
        const info = this._getCellInfo();
        if (info) {
            const colRange = info.tableDesc.base.getColumnRangeByColumnIdx(info.cell.column);
            setCellBg({
                tablePos: info.table.pos,
                cols: iterate(colRange.startIdx, colRange.endIdx + 1),
                bg,
            })(this._view.state, this._view.dispatch);
        }
    };
    _onRowInsertBeforeClick = () => {
        this._logger.event({ event: 'row-insert-before', source: 'row-menu' });
        const info = this._getCellInfo();
        if (info) {
            const rowRange = info.tableDesc.base.getRowRangeByRowIdx(info.cell.row);
            this._insertRow(info.tableDesc, rowRange.startIdx, rowRange.startIdx);
        }
        this._view.focus();
    };
    _onRowInsertAfterClick = () => {
        this._logger.event({ event: 'row-insert-after', source: 'column-menu' });
        const info = this._getCellInfo();
        if (info) {
            const rowRange = info.tableDesc.base.getRowRangeByRowIdx(info.cell.row);
            this._insertRow(info.tableDesc, rowRange.endIdx + 1, rowRange.endIdx);
        }
        this._view.focus();
    };
    _insertRow(tableDesc, rowIndex, sourceRowIndex) {
        insertEmptyRow({ tablePos: tableDesc.pos, rowIndex, sourceRowIndex })(this._view.state, this._view.dispatch, this._view);
    }
    _onColumnInsertBeforeClick = () => {
        this._logger.event({ event: 'column-insert-before', source: 'column-menu' });
        const info = this._getCellInfo();
        if (info) {
            const colRange = info.tableDesc.base.getColumnRangeByColumnIdx(info.cell.column);
            this._insertColumn(info.tableDesc, colRange.startIdx, colRange.startIdx);
        }
        this._view.focus();
    };
    _onColumnInsertAfterClick = () => {
        this._logger.event({ event: 'column-insert-after', source: 'column-menu' });
        const info = this._getCellInfo();
        if (info) {
            const colRange = info.tableDesc.base.getColumnRangeByColumnIdx(info.cell.column);
            this._insertColumn(info.tableDesc, colRange.endIdx + 1, colRange.endIdx);
        }
        this._view.focus();
    };
    _insertColumn(tableDesc, colIndex, sourceColIndex) {
        insertEmptyColumn({ tablePos: tableDesc.pos, colIndex, sourceColIndex })(this._view.state, this._view.dispatch, this._view);
    }
    _onRowRemoveRangeClick = () => {
        this._logger.event({ event: 'row-remove', source: 'row-menu' });
        const info = this._getCellInfo();
        if (info) {
            const rangeIdx = info.tableDesc.base.getRowRangeIdxByRowIdx(info.cell.row);
            removeRowRange({ tablePos: info.tableDesc.pos, rangeIdx })(this._view.state, this._view.dispatch, this._view);
        }
        this._view.focus();
    };
    _onColumnRemoveRangeClick = () => {
        this._logger.event({ event: 'column-remove', source: 'column-menu' });
        const info = this._getCellInfo();
        if (info) {
            const rangeIdx = info.tableDesc.base.getColumnRangeIdxByColumnIdx(info.cell.column);
            removeColumnRange({ tablePos: info.tableDesc.pos, rangeIdx })(this._view.state, this._view.dispatch, this._view);
        }
        this._view.focus();
    };
    _toggleHeaderRows(event, source, getValue) {
        this._logger.event({ event, source });
        const info = this._getCellInfo();
        if (info) {
            const rowRange = info.tableDesc.base.getRowRangeByRowIdx(info.cell.row);
            toggleHeaderRows({
                tablePos: info.table.pos,
                value: getValue(rowRange),
            })(this._view.state, this._view.dispatch);
        }
        this._view.focus();
    }
    _onRowHeaderChange = (value) => {
        this._toggleHeaderRows(value ? 'row-set-header' : 'row-unset-header', 'row-menu', (range) => (value ? range.endIdx + 1 : range.startIdx));
    };
    _onRemoveTableClick = () => {
        this._logger.event({ event: 'table-remove' });
        const info = this._getCellInfo();
        if (info) {
            removeYfmTable(this._view.state, this._view.dispatch, this._view, {
                tablePos: info.table.pos,
                tableNode: info.table.node,
            });
        }
        this._view.focus();
    };
    _computeIsHeader(cellInfo) {
        if (!this._headerRowsEnabled)
            return false;
        if (!cellInfo)
            return false;
        return cellInfo.tableDesc.base.isHeaderRow(cellInfo.cell.row);
    }
    _getCellInfo(node = this._node) {
        const table = this._getParentTable();
        const tableDesc = table ? TableDesc.create(table.node)?.bind(table.pos) : undefined;
        const cellInfo = tableDesc?.base.getCellInfo(node);
        return cellInfo
            ? { pos: this._getPos(), table: table, tableDesc: tableDesc, cell: cellInfo }
            : undefined;
    }
    _getParentTable = () => {
        const pos = this._getPos();
        return pos !== undefined
            ? findParentNodeClosestToPos(this._view.state.doc.resolve(pos), isTableNode)
            : undefined;
    };
}
//# sourceMappingURL=yfm-table-cell-view.js.map
