/*!
 * Built by Revolist OU ❤️
 */
/**
 * Plugin module for revo-grid grid system
 * Add support for automatic column resize
 */
import each from "lodash/each";
import reduce from "lodash/reduce";
import { BasePlugin } from "./base.plugin";
import { getSourceItem, columnTypes } from "../store/index";
import { getColumnType } from "../utils/column.utils";
const LETTER_BLOCK_SIZE = 7;
export var ColumnAutoSizeMode;
(function (ColumnAutoSizeMode) {
    // increases column width on header click according the largest text value
    ColumnAutoSizeMode["headerClickAutosize"] = "headerClickAutoSize";
    // increases column width on data set and text edit, decreases performance
    ColumnAutoSizeMode["autoSizeOnTextOverlap"] = "autoSizeOnTextOverlap";
    // increases and decreases column width based on all items sizes, worst for performance
    ColumnAutoSizeMode["autoSizeAll"] = "autoSizeAll";
})(ColumnAutoSizeMode || (ColumnAutoSizeMode = {}));
export class AutoSizeColumnPlugin extends BasePlugin {
    constructor(revogrid, providers, config) {
        super(revogrid, providers);
        this.providers = providers;
        this.config = config;
        this.autoSizeColumns = null;
        /** for edge case when no columns defined before data */
        this.dataResolve = null;
        this.dataReject = null;
        this.letterBlockSize = (config === null || config === void 0 ? void 0 : config.letterBlockSize) || LETTER_BLOCK_SIZE;
        // create test container to check text width
        if (config === null || config === void 0 ? void 0 : config.preciseSize) {
            this.precsizeCalculationArea = this.initiatePresizeElement();
            revogrid.appendChild(this.precsizeCalculationArea);
        }
        const aftersourceset = ({ detail: { source }, }) => {
            this.setSource(source);
        };
        const beforecolumnsset = ({ detail: { columns }, }) => {
            this.columnSet(columns);
        };
        this.addEventListener('beforecolumnsset', beforecolumnsset);
        switch (config === null || config === void 0 ? void 0 : config.mode) {
            case ColumnAutoSizeMode.autoSizeOnTextOverlap:
                this.addEventListener('aftersourceset', aftersourceset);
                this.addEventListener('afteredit', ({ detail }) => {
                    this.afteredit(detail);
                });
                break;
            case ColumnAutoSizeMode.autoSizeAll:
                this.addEventListener('aftersourceset', aftersourceset);
                this.addEventListener('afteredit', ({ detail }) => {
                    this.afterEditAll(detail);
                });
                break;
            default:
                this.addEventListener('headerdblclick', ({ detail }) => {
                    const type = getColumnType(detail.column);
                    const size = this.getColumnSize(detail.index, type);
                    if (size) {
                        this.providers.dimension.setCustomSizes(type, {
                            [detail.index]: size,
                        }, true);
                    }
                });
                break;
        }
    }
    async setSource(source) {
        let autoSize = this.autoSizeColumns;
        if (this.dataReject) {
            this.dataReject();
            this.clearPromise();
        }
        /** If data set first and no column provided await until get one */
        if (!autoSize) {
            const request = new Promise((resolve, reject) => {
                this.dataResolve = resolve;
                this.dataReject = reject;
            });
            try {
                autoSize = await request;
            }
            catch (e) {
                return;
            }
        }
        // calculate sizes
        each(autoSize, (_v, type) => {
            const sizes = {};
            each(autoSize[type], rgCol => {
                // calculate size
                rgCol.size = sizes[rgCol.index] = source.reduce((prev, rgRow) => Math.max(prev, this.getLength(rgRow[rgCol.prop])), this.getLength(rgCol.name || ''));
            });
            this.providers.dimension.setCustomSizes(type, sizes, true);
        });
    }
    getLength(len) {
        var _a;
        const padding = 15;
        if (!len) {
            return 0;
        }
        try {
            const str = len.toString();
            /**if exact calculation required proxy with html element, slow operation */
            if ((_a = this.config) === null || _a === void 0 ? void 0 : _a.preciseSize) {
                this.precsizeCalculationArea.innerText = str;
                return this.precsizeCalculationArea.scrollWidth + padding * 2;
            }
            return str.length * this.letterBlockSize + padding * 2;
        }
        catch (e) {
            return 0;
        }
    }
    afteredit(e) {
        let data;
        if (this.isRangeEdit(e)) {
            data = e.data;
        }
        else {
            data = { 0: { [e.prop]: e.val } };
        }
        each(this.autoSizeColumns, (columns, type) => {
            const sizes = {};
            each(columns, rgCol => {
                var _a;
                // calculate size
                const size = reduce(data, (prev, rgRow) => {
                    if (typeof rgRow[rgCol.prop] === 'undefined') {
                        return prev;
                    }
                    return Math.max(prev || 0, this.getLength(rgRow[rgCol.prop]));
                }, undefined);
                if (size && ((_a = rgCol.size) !== null && _a !== void 0 ? _a : 0) < size) {
                    rgCol.size = sizes[rgCol.index] = size;
                }
            });
            this.providers.dimension.setCustomSizes(type, sizes, true);
        });
    }
    afterEditAll(e) {
        const props = {};
        if (this.isRangeEdit(e)) {
            each(e.data, r => each(r, (_v, p) => (props[p] = true)));
        }
        else {
            props[e.prop] = true;
        }
        each(this.autoSizeColumns, (columns, type) => {
            const sizes = {};
            each(columns, rgCol => {
                if (props[rgCol.prop]) {
                    const size = this.getColumnSize(rgCol.index, type);
                    if (size) {
                        sizes[rgCol.index] = size;
                    }
                }
            });
            this.providers.dimension.setCustomSizes(type, sizes, true);
        });
    }
    getColumnSize(index, type) {
        var _a, _b;
        const rgCol = (_b = (_a = this.autoSizeColumns) === null || _a === void 0 ? void 0 : _a[type]) === null || _b === void 0 ? void 0 : _b[index];
        if (!rgCol) {
            return 0;
        }
        return reduce(this.providers.data.stores, (r, s) => {
            const perStore = reduce(s.store.get('items'), (prev, _row, i) => {
                const item = getSourceItem(s.store, i);
                return Math.max(prev || 0, this.getLength(item === null || item === void 0 ? void 0 : item[rgCol.prop]));
            }, 0);
            return Math.max(r, perStore);
        }, rgCol.size || 0);
    }
    columnSet(columns) {
        var _a;
        for (let t of columnTypes) {
            const type = t;
            const cols = columns[type];
            for (let i in cols) {
                if (cols[i].autoSize || ((_a = this.config) === null || _a === void 0 ? void 0 : _a.allColumns)) {
                    if (!this.autoSizeColumns) {
                        this.autoSizeColumns = {};
                    }
                    if (!this.autoSizeColumns[type]) {
                        this.autoSizeColumns[type] = {};
                    }
                    this.autoSizeColumns[type][i] = Object.assign(Object.assign({}, cols[i]), { index: parseInt(i, 10) });
                }
            }
        }
        if (this.dataResolve) {
            this.dataResolve(this.autoSizeColumns || {});
            this.clearPromise();
        }
    }
    clearPromise() {
        this.dataResolve = null;
        this.dataReject = null;
    }
    isRangeEdit(e) {
        return !!e.data;
    }
    initiatePresizeElement() {
        var _a;
        const styleForFontTest = {
            position: 'absolute',
            fontSize: '14px',
            height: '0',
            width: '0',
            whiteSpace: 'nowrap',
            top: '0',
            overflowX: 'scroll',
            display: 'block',
        };
        const el = document.createElement('div');
        for (let s in styleForFontTest) {
            el.style[s] = (_a = styleForFontTest[s]) !== null && _a !== void 0 ? _a : '';
        }
        el.classList.add('revo-test-container');
        return el;
    }
    destroy() {
        var _a;
        super.destroy();
        (_a = this.precsizeCalculationArea) === null || _a === void 0 ? void 0 : _a.remove();
    }
}
