import '../../styles';
import * as React from 'react';
import { Subject } from 'rxjs';
import { debounceTime, filter, takeUntil } from 'rxjs/operators';
import { FactoryManager } from '../../factory';
import * as render from '../../render';
import { containsFocus, events, constants, css, defaultValue } from '../common';
import { Handsontable as TableLib } from '../../common/libs.Handsontable';
import { getSettings } from '../settings';
import { DataGridOverlay } from './DataGrid.Overlay';
const { CSS } = constants;
export class DataGrid extends React.PureComponent {
    constructor() {
        super(...arguments);
        this.state = {};
        this.unmounted$ = new Subject();
        this.state$ = new Subject();
        this.elRef = (ref) => (this.el = ref);
    }
    componentDidMount() {
        const grid = (this.grid = this.props.grid);
        this.unmounted$.subscribe(() => grid.dispose());
        this.state$.pipe(takeUntil(this.unmounted$)).subscribe((e) => this.setState(e));
        const settings = getSettings({ grid });
        const Table = this.props.Handsontable || TableLib;
        const table = (this.table = new Table(this.el, settings));
        grid.initialize({ table });
        const factory = (this.factory = new FactoryManager({ grid, factory: this.props.factory }));
        render.registerAll(Table, grid, factory);
        const refs = {
            grid,
            editorEvents$: new Subject(),
            factory: this.factory,
        };
        table.__gridRefs = refs;
        const { events$, keyboard$ } = grid;
        const editor$ = refs.editorEvents$.pipe(takeUntil(this.unmounted$));
        const bubble$ = this.props.events$;
        if (bubble$) {
            events$.subscribe((e) => bubble$.next(e));
        }
        editor$.subscribe((e) => this.grid.fire(e));
        events$
            .pipe(filter((e) => e.type === 'GRID/redraw'), debounceTime(0))
            .subscribe(() => this.redraw());
        keyboard$
            .pipe(filter((e) => e.metaKey && e.key === 'a'), filter((e) => this.props.canSelectAll !== true), filter((e) => !grid.isEditing))
            .subscribe((e) => e.cancel());
        this.updateSize();
        events.resize$.pipe(takeUntil(this.unmounted$)).subscribe(() => this.redraw());
        const hot = module.hot;
        if (hot) {
            hot.dispose(() => this.dispose());
        }
        events.focus$
            .pipe(takeUntil(this.unmounted$), debounceTime(0), filter((e) => e.to !== document.body), filter((e) => !containsFocus(this)))
            .subscribe((e) => this.grid.deselect());
        this.init();
    }
    async init() {
        if (this.isDisposed) {
            return;
        }
        const { initial = {} } = this.props;
        const grid = this.grid;
        if (initial.selection) {
            const selection = typeof initial.selection === 'string' ? { cell: initial.selection } : initial.selection;
            const { cell, ranges } = selection;
            grid.select({ cell, ranges });
        }
        await grid.calc.update();
        const cells = grid.data.cells;
        grid.changeCells(cells, { init: true, silent: true });
        grid.mergeCells({ cells, init: true });
        grid.fire({ type: 'GRID/ready', payload: { grid } });
        this.forceUpdate();
    }
    componentWillUnmount() {
        this.dispose();
    }
    dispose() {
        this.unmounted$.next();
        this.unmounted$.complete();
    }
    get isDisposed() {
        return this.unmounted$.isStopped || this.grid.isDisposed;
    }
    get isReady() {
        return this.grid ? this.grid.isReady : false;
    }
    get events$() {
        return this.grid.events$;
    }
    focus(isFocused) {
        if (defaultValue(isFocused, true)) {
            this.grid.focus();
        }
        else {
            this.grid.blur();
        }
        return this;
    }
    redraw() {
        this.updateSize();
        if (this.table) {
            this.table.render();
        }
        return this;
    }
    updateSize() {
        const el = this.el;
        if (!el || this.isDisposed) {
            return;
        }
        const { offsetWidth: width, offsetHeight: height } = el;
        const size = { width, height };
        this.state$.next({ size });
        return this;
    }
    render() {
        const { factory } = this.props;
        const grid = this.grid;
        const styles = {
            base: css({
                position: 'relative',
                overflow: 'hidden',
            }),
            grid: css({
                Absolute: 0,
                userSelect: 'none',
                visibility: this.isReady ? 'visible' : 'hidden',
            }),
        };
        return (React.createElement("div", Object.assign({}, css(styles.base, this.props.style)),
            React.createElement("div", Object.assign({ ref: this.elRef, className: CSS.CLASS.GRID.BASE }, styles.grid)),
            grid && React.createElement(DataGridOverlay, { grid: grid, factory: factory })));
    }
}
