import { filter, map } from 'rxjs/operators';
import { coord, toClipboard } from '../common';
const CLIPBOARD_COMMANDS = ['CUT', 'COPY', 'PASTE'];
export function init(args) {
    const { grid, command$, fire } = args;
    const clipboard$ = command$.pipe(filter((e) => CLIPBOARD_COMMANDS.includes(e.command)), filter((e) => !e.isCancelled), map((e) => e.command));
    clipboard$.pipe(filter((e) => e === 'CUT')).subscribe((e) => read({ grid, fire, action: 'CUT' }));
    clipboard$
        .pipe(filter((e) => e === 'COPY'))
        .subscribe((e) => read({ grid, fire, action: 'COPY' }));
    clipboard$.pipe(filter((e) => e === 'PASTE')).subscribe((e) => write({ grid, fire }));
}
async function read(args) {
    const { grid, action } = args;
    const wait = [];
    args.fire({
        type: 'GRID/clipboard/before/read',
        payload: {
            action,
            wait: (promise) => wait.push(promise),
        },
    });
    if (wait.length > 0) {
        await Promise.all(wait);
    }
    const payload = toClipboard({ grid, action });
    await navigator.clipboard.writeText(payload.text);
    grid.clipboard = Object.assign(Object.assign({}, payload), { pasted: 0 });
    args.fire({ type: 'GRID/clipboard', payload });
}
async function write(args) {
    const { grid } = args;
    const selection = grid.selection;
    const targetCell = selection.cell;
    if (!targetCell) {
        return;
    }
    let redraw = false;
    const text = await navigator.clipboard.readText();
    let pending = grid.clipboard;
    const before = {
        text,
        pending,
        isModified: false,
        modify: (change) => {
            before.isModified = true;
            pending = Object.assign(Object.assign({}, (change || {})), { pasted: 0 });
        },
    };
    args.fire({ type: 'GRID/clipboard/before/paste', payload: before });
    const isPendingCurrent = pending ? pending.text === text : false;
    if (!isPendingCurrent) {
        grid.clipboard = undefined;
        pending = undefined;
    }
    if (pending && pending.action === 'CUT') {
        const empty = Object.keys(pending.cells).reduce((acc, key) => {
            acc[key] = { value: undefined };
            return acc;
        }, {});
        grid.changeCells(empty, { source: 'CLIPBOARD/cut' });
    }
    const cellsFromString = () => coord.table
        .fromString({ key: targetCell, text })
        .map(({ key, value }) => ({ key, value: { value } }));
    const items = pending && pending.selection.cell
        ? shiftCells({
            sourceCell: pending.selection.cell,
            targetCell,
            data: pending.cells,
        })
        : cellsFromString();
    if (items.length === 0) {
        return;
    }
    const changedCells = items.reduce((acc, next) => {
        acc[next.key] = next.value;
        return acc;
    }, {});
    grid.changeCells(changedCells, { source: 'CLIPBOARD/paste' });
    const shiftedAxisData = (axis, data) => {
        if (!pending || Object.keys(data).length === 0) {
            return {};
        }
        const source = firstAxisRange(axis, pending.selection);
        const target = firstAxisRange(axis, selection);
        if (!source || !target) {
            return {};
        }
        return shiftAxisData({ axis, source, target, data });
    };
    const columns = pending ? shiftedAxisData('COLUMN', pending.columns) : {};
    const rows = pending ? shiftedAxisData('ROW', pending.rows) : {};
    if (Object.keys(columns).length > 0) {
        grid.changeColumns(columns, { source: 'CLIPBOARD/paste' });
        redraw = true;
    }
    if (Object.keys(rows).length > 0) {
        grid.changeRows(rows, { source: 'CLIPBOARD/paste' });
        redraw = true;
    }
    const square = coord.range.square(items.map((item) => item.key));
    grid.select({ cell: square.left.key, ranges: [square.key] });
    if (grid.clipboard) {
        grid.clipboard = Object.assign(Object.assign({}, grid.clipboard), { pasted: grid.clipboard.pasted + 1 });
    }
    const cells = items.reduce((acc, next) => {
        acc[next.key] = next.value;
        return acc;
    }, {});
    args.fire({
        type: 'GRID/clipboard',
        payload: { action: 'PASTE', selection, text, cells, columns, rows },
    });
    if (redraw) {
        grid.redraw();
    }
}
function shiftCells(args) {
    const pos = {
        start: coord.cell.fromKey(args.sourceCell),
        end: coord.cell.fromKey(args.targetCell),
    };
    const diff = {
        row: pos.end.row - pos.start.row,
        column: pos.end.column - pos.start.column,
    };
    const data = Object.assign({}, args.data);
    return Object.keys(data).map((cell) => {
        const pos = coord.cell.fromKey(cell);
        const key = coord.cell.toKey(pos.column + diff.column, pos.row + diff.row);
        return { key, value: data[cell] };
    });
}
function shiftAxisData(args) {
    const { axis } = args;
    const pos = {
        start: args.source.left,
        end: args.target.left,
    };
    const diff = coord.cell.toAxisIndex(axis, pos.end) - coord.cell.toAxisIndex(axis, pos.start);
    const data = Object.assign({}, args.data);
    return Object.keys(data).reduce((acc, next) => {
        const index = coord.cell.toAxisIndex(axis, next) + diff;
        const key = coord.cell.toAxisKey(axis, index);
        if (key) {
            acc[key] = data[next];
        }
        return acc;
    }, {});
}
function firstAxisRange(axis, selection) {
    const key = selection.ranges.find((key) => coord.cell.isAxisRangeKey(key, axis));
    return key ? coord.range.fromKey(key) : undefined;
}
