/*!
 * Built by Revolist OU ❤️
 */
import { columnTypes, getSourceItem, getVisibleSourceItem, rowTypes } from "../../store/index";
import { CONTENT_SLOT, FOOTER_SLOT, HEADER_SLOT, viewportDataPartition } from "./viewport.helpers";
/** Collect Column data */
function gatherColumnData(data) {
    const colDimension = data.dimensions[data.colType].store;
    const realWidth = colDimension.get('realSize');
    const prop = {
        contentWidth: realWidth,
        class: data.colType,
        contentHeight: data.contentHeight,
        key: data.colType,
        colType: data.colType,
        noHorizontalScrollTransfer: data.noHorizontalScrollTransfer,
        onResizeviewport: data.onResizeviewport,
        // set viewport size to real size
        style: data.fixWidth ? { minWidth: `${realWidth}px` } : undefined,
    };
    const headerProp = {
        colData: getVisibleSourceItem(data.colStore),
        dimensionCol: colDimension,
        type: data.colType,
        groups: data.colStore.get('groups'),
        groupingDepth: data.colStore.get('groupingDepth'),
        resizeHandler: data.colType === 'colPinEnd' ? ['l'] : undefined,
        onHeaderresize: data.onHeaderresize,
    };
    return {
        prop,
        type: data.colType,
        position: data.position,
        headerProp,
        viewportCol: data.viewports[data.colType].store,
    };
}
export default class ViewportService {
    constructor(config, contentHeight) {
        // ----------- Handle columns ----------- //
        var _a;
        this.config = config;
        // Transform data from stores and apply it to different components
        const columns = [];
        let x = 0; // we increase x only if column present
        columnTypes.forEach(val => {
            const colStore = config.columnProvider.stores[val].store;
            // only columns that have data show
            if (!colStore.get('items').length) {
                return;
            }
            const column = {
                colType: val,
                position: { x, y: 1 },
                contentHeight,
                // only central column has dynamic width
                fixWidth: val !== 'rgCol',
                viewports: config.viewportProvider.stores,
                dimensions: config.dimensionProvider.stores,
                rowStores: config.dataProvider.stores,
                noHorizontalScrollTransfer: config.noHorizontalScrollTransfer,
                colStore,
                onHeaderresize: e => this.onColumnResize(val, e, colStore),
            };
            if (val === 'rgCol') {
                column.onResizeviewport = (e) => {
                    var _a;
                    const vpState = {
                        clientSize: e.detail.size,
                    };
                    // virtual size will be handled by dimension provider if disabled
                    if ((e.detail.dimension === 'rgRow' && !config.disableVirtualY)
                        || (e.detail.dimension === 'rgCol' && !config.disableVirtualX)) {
                        vpState.virtualSize = e.detail.size;
                    }
                    (_a = config.viewportProvider) === null || _a === void 0 ? void 0 : _a.setViewport(e.detail.dimension, vpState);
                };
            }
            const colData = gatherColumnData(column);
            const columnSelectionStore = this.registerCol(colData.position.x, val);
            // render per each column data collections vertically
            const dataPorts = this.dataViewPort(column).reduce((r, rgRow) => {
                // register selection store for Segment
                const segmentSelection = this.registerSegment(rgRow.position, rgRow.lastCell);
                // register selection store for Row
                const rowSelectionStore = this.registerRow(rgRow.position.y, rgRow.type);
                const rowDef = Object.assign(Object.assign({ colType: val }, rgRow), { rowSelectionStore, selectionStore: segmentSelection.store, onSetrange: e => {
                        segmentSelection.setRangeArea(e.detail);
                    }, onSettemprange: e => segmentSelection.setTempArea(e.detail), onFocuscell: e => {
                        // todo: multi focus
                        segmentSelection.clearFocus();
                        config.selectionStoreConnector.focus(segmentSelection, e.detail);
                    } });
                r.push(rowDef);
                return r;
            }, []);
            columns.push(Object.assign(Object.assign({}, colData), { columnSelectionStore,
                dataPorts }));
            x++;
        });
        this.columns = columns;
        // ----------- Handle columns end ----------- //
        (_a = this.config.scrollingService) === null || _a === void 0 ? void 0 : _a.unregister();
    }
    onColumnResize(type, { detail }, store) {
        var _a;
        // apply to dimension provider
        (_a = this.config.dimensionProvider) === null || _a === void 0 ? void 0 : _a.setCustomSizes(type, detail, true);
        // set resize event
        const changedItems = {};
        for (const [i, size] of Object.entries(detail || {})) {
            const virtualIndex = parseInt(i, 10);
            const item = getSourceItem(store, virtualIndex);
            if (item) {
                changedItems[virtualIndex] = Object.assign(Object.assign({}, item), { size });
            }
        }
        this.config.resize(changedItems);
    }
    /** register selection store for Segment */
    registerSegment(position, lastCell) {
        const store = this.config.selectionStoreConnector.register(position);
        store.setLastCell(lastCell);
        return store;
    }
    /** register selection store for Row */
    registerRow(y, type) {
        return this.config.selectionStoreConnector.registerRow(y, type).store;
    }
    /** register selection store for Column */
    registerCol(x, type) {
        return this.config.selectionStoreConnector.registerColumn(x, type).store;
    }
    /** Collect Row data */
    dataViewPort(data) {
        const slots = {
            rowPinStart: HEADER_SLOT,
            rgRow: CONTENT_SLOT,
            rowPinEnd: FOOTER_SLOT,
        };
        // y position for selection
        let y = 0;
        return rowTypes.reduce((result, type) => {
            const rgCol = Object.assign(Object.assign({}, data), { position: Object.assign(Object.assign({}, data.position), { y }) });
            const partition = viewportDataPartition(rgCol, type, slots[type], type !== 'rgRow');
            result.push(partition);
            y++;
            return result;
        }, []);
    }
    scrollToCell(cell) {
        for (let key in cell) {
            const coordinate = cell[key];
            if (typeof coordinate === 'number') {
                this.config.scrollingService.proxyScroll({
                    dimension: key === 'x' ? 'rgCol' : 'rgRow',
                    coordinate,
                });
            }
        }
    }
    /**
     * Clear current grid focus
     */
    clearFocused() {
        this.config.selectionStoreConnector.clearAll();
    }
    clearEdit() {
        this.config.selectionStoreConnector.setEdit(false);
    }
    /**
     * Collect focused element data
     */
    getFocused() {
        const focused = this.config.selectionStoreConnector.focusedStore;
        if (!focused) {
            return null;
        }
        // get column data
        const colType = this.config.selectionStoreConnector.storesXToType[focused.position.x];
        const column = this.config.columnProvider.getColumn(focused.cell.x, colType);
        // get row data
        const rowType = this.config.selectionStoreConnector.storesYToType[focused.position.y];
        const model = this.config.dataProvider.getModel(focused.cell.y, rowType);
        return {
            column,
            model,
            cell: focused.cell,
            colType,
            rowType,
        };
    }
    getStoreCoordinateByType(colType, rowType) {
        const stores = this.config.selectionStoreConnector.storesByType;
        if (typeof stores[colType] === 'undefined' || typeof stores[rowType] === 'undefined') {
            return;
        }
        return {
            x: stores[colType],
            y: stores[rowType],
        };
    }
    setFocus(colType, rowType, start, end) {
        var _a;
        const coordinate = this.getStoreCoordinateByType(colType, rowType);
        if (coordinate) {
            (_a = this.config.selectionStoreConnector) === null || _a === void 0 ? void 0 : _a.focusByCell(coordinate, start, end);
        }
    }
    getSelectedRange() {
        const focused = this.config.selectionStoreConnector.focusedStore;
        if (!focused) {
            return null;
        }
        // get column data
        const colType = this.config.selectionStoreConnector.storesXToType[focused.position.x];
        // get row data
        const rowType = this.config.selectionStoreConnector.storesYToType[focused.position.y];
        const range = focused.entity.store.get('range');
        if (!range) {
            return null;
        }
        return Object.assign(Object.assign({}, range), { colType,
            rowType });
    }
    setEdit(rowIndex, colIndex, colType, rowType) {
        var _a;
        const coordinate = this.getStoreCoordinateByType(colType, rowType);
        if (coordinate) {
            (_a = this.config.selectionStoreConnector) === null || _a === void 0 ? void 0 : _a.setEditByCell(coordinate, { x: colIndex, y: rowIndex });
        }
    }
}
