import { assertType, DisposableGroup, Slot } from '@blockeditor/global/utils';
import { groupBy } from '../../../_common/utils/iterable.js';
import { GroupElement } from '../../../surface-block/elements/index.js';
import { edgelessElementsBound } from '../utils/bound-utils.js';
export class EdgelessSelectionManager {
    get empty() {
        return (this.selections.length === 0 ||
            this.selections.every(sel => sel.elements.length === 0));
    }
    get activeGroup() {
        return this._activeGroup;
    }
    get editing() {
        return this.selections.some(sel => sel.editing);
    }
    /**
     * ids of the selected elements
     */
    get selectedIds() {
        return this._selectedIds;
    }
    /**
     * models of the selected elements
     */
    get elements() {
        const elements = [];
        this._selectedIds.forEach(id => {
            const el = this.container.surface.pickById(id);
            el && elements.push(el);
        });
        return elements;
    }
    get firstElement() {
        return this.elements[0];
    }
    get selectedBound() {
        return edgelessElementsBound(this.elements);
    }
    get _selection() {
        return this.container.host.selection;
    }
    constructor(container) {
        this.disposable = new DisposableGroup();
        this.slots = {
            updated: new Slot(),
            remoteUpdated: new Slot(),
            cursorUpdated: new Slot(),
            remoteCursorUpdated: new Slot(),
        };
        this.lastState = [];
        this.selections = [];
        this.cursor = null;
        this.remoteCursor = new Map();
        this.remoteSelection = new Map();
        this._activeGroup = null;
        this._selected = new Set();
        this._selectedIds = [];
        this._remoteSelected = new Set();
        this.container = container;
        this.mount();
    }
    _setState(selections) {
        this.lastState = this.selections;
        this.selections = selections;
        this._selected = new Set();
        this._selectedIds = [];
        selections.forEach(sel => sel.elements.forEach(id => {
            this._selected.add(id);
            this._selectedIds.push(id);
        }));
    }
    _setCursor(cursor) {
        this.cursor = cursor;
    }
    isEmpty(selections) {
        return (selections.length === 0 ||
            selections.every(sel => sel.elements.length === 0));
    }
    equals(selection) {
        let count = 0;
        let editing = false;
        const exist = selection.every(sel => {
            const exist = sel.elements.every(id => this._selected.has(id));
            if (exist) {
                count += sel.elements.length;
            }
            if (sel.editing)
                editing = true;
            return exist;
        });
        return exist && count === this._selected.size && editing === this.editing;
    }
    mount() {
        this.disposable.add(this._selection.slots.changed.on(selections => {
            const { cursor = [], surface = [] } = groupBy(selections, sel => {
                if (sel.is('surface')) {
                    return 'surface';
                }
                else if (sel.is('cursor')) {
                    return 'cursor';
                }
                return 'none';
            });
            assertType(cursor);
            assertType(surface);
            if (cursor[0] && !this.cursor?.equals(cursor[0])) {
                this._setCursor(cursor[0]);
                this.slots.cursorUpdated.emit(cursor[0]);
            }
            if ((surface.length === 0 && this.empty) || this.equals(surface)) {
                return;
            }
            this._setState(surface);
            this.slots.updated.emit(this.selections);
        }));
        this.disposable.add(this._selection.slots.remoteChanged.on(states => {
            const remoteSelection = new Map();
            const remoteCursors = new Map();
            const remoteSelectedElements = new Set();
            states.forEach((selections, id) => {
                let hasTextSelection = false;
                let hasBlockSelection = false;
                selections.forEach(selection => {
                    if (selection.is('text')) {
                        hasTextSelection = true;
                    }
                    if (selection.is('block')) {
                        hasBlockSelection = true;
                    }
                    if (selection.is('surface')) {
                        remoteSelection.set(id, selection);
                        selection.elements.forEach(id => remoteSelectedElements.add(id));
                    }
                    if (selection.is('cursor')) {
                        remoteCursors.set(id, selection);
                    }
                });
                if (hasBlockSelection || hasTextSelection) {
                    remoteSelection.delete(id);
                }
                if (hasTextSelection) {
                    remoteCursors.delete(id);
                }
            });
            this.remoteCursor = remoteCursors;
            this.remoteSelection = remoteSelection;
            this._remoteSelected = remoteSelectedElements;
            this.slots.remoteUpdated.emit();
            this.slots.remoteCursorUpdated.emit();
        }));
    }
    /**
     * check if element is selected by remote peers
     * @param element
     */
    hasRemote(element) {
        return this._remoteSelected.has(element);
    }
    /**
     * check if the element is selected in local
     * @param element
     */
    has(element) {
        return this._selected.has(element);
    }
    set(selection) {
        if (Array.isArray(selection)) {
            this._selection.setGroup('edgeless', this.cursor ? [...selection, this.cursor] : selection);
            return;
        }
        const { surface } = this.container;
        const { blocks = [], elements = [] } = groupBy(selection.elements, id => {
            return this.container.page.getBlockById(id) ? 'blocks' : 'elements';
        });
        let instances = [];
        if (elements.length > 0) {
            instances.push(this._selection.create('surface', this.container.surface.path, elements, selection.editing));
        }
        if (blocks.length > 0) {
            instances = instances.concat(blocks.map(blockId => this._selection.create('surface', [this.container.page.root.id, blockId], [blockId], selection.editing)));
        }
        if (this.cursor) {
            instances.push(this.cursor);
        }
        this._selection.setGroup('edgeless', instances);
        if (selection.elements.length === 1 &&
            this.firstElement instanceof GroupElement) {
            this._activeGroup = this.firstElement;
        }
        else {
            if (this.elements.some(ele => {
                surface.getGroupParent(ele) !== this._activeGroup;
            }) ||
                this.elements.length === 0) {
                this._activeGroup = null;
            }
        }
        surface.refresh();
    }
    setCursor(cursor) {
        const instance = this._selection.create('cursor', cursor.x, cursor.y);
        this._selection.setGroup('edgeless', [...this.selections, instance]);
    }
    clear() {
        this._selection.clear();
        this.set({
            elements: [],
            editing: false,
        });
    }
    dispose() {
        this.disposable.dispose();
    }
}
//# sourceMappingURL=selection-manager.js.map