import { isValid } from "@visactor/vutils";

import { FilterActionType } from "./types";

import { applyStyles } from "./styles";

export class ValueFilter {
    constructor(table, filterStateManager, pluginOptions) {
        this.selectedKeys = new Map, this.candidateKeys = new Map, this.formatFnCache = new Map, 
        this.toUnformattedCache = new Map, this.valueFilterOptionList = new Map, this.table = table, 
        this.filterStateManager = filterStateManager, this.pluginOptions = pluginOptions, 
        this.styles = pluginOptions.styles || {};
    }
    setSelectedField(fieldId) {
        this.selectedField = fieldId;
    }
    getFormatFnCache(fieldId) {
        let formatFn = this.formatFnCache.get(fieldId);
        if (null != formatFn) return formatFn;
        const headerAddress = this.table.internalProps.layoutMap.getHeaderCellAddressByField(String(fieldId)), bodyInfo = this.table.internalProps.layoutMap.getBody(null == headerAddress ? void 0 : headerAddress.col, null == headerAddress ? void 0 : headerAddress.row);
        return formatFn = bodyInfo && "fieldFormat" in bodyInfo && "function" == typeof bodyInfo.fieldFormat ? bodyInfo.fieldFormat : bodyInfo && "format" in bodyInfo && "function" == typeof bodyInfo.format ? bodyInfo.format : record => record[fieldId], 
        this.formatFnCache.set(fieldId, formatFn), formatFn;
    }
    getRecords(table, original) {
        if (!0 === original) return table.internalProps.records;
        const records = [], stack = [ ...table.internalProps.dataSource.records ];
        for (;stack.length > 0; ) {
            const item = stack.pop();
            if ((null == item ? void 0 : item.vtableMerge) && Array.isArray(null == item ? void 0 : item.children)) for (let i = item.children.length - 1; i >= 0; i--) stack.push(item.children[i]); else records.push(item);
        }
        return records.reverse();
    }
    collectCandidateKeysForUnfilteredColumn(fieldId) {
        var _a, _b;
        const syncFilterItemsState = null === (_b = null === (_a = this.pluginOptions) || void 0 === _a ? void 0 : _a.syncFilterItemsState) || void 0 === _b || _b, countMap = new Map;
        let records = [];
        records = syncFilterItemsState ? this.getRecords(this.table, !1) : this.table.internalProps.records;
        const formatFn = this.getFormatFnCache(fieldId), toUnformatted = new Map;
        records.forEach((record => {
            var _a;
            if (isValid(record)) {
                const originalValue = record[fieldId], formattedValue = null !== (_a = formatFn(record)) && void 0 !== _a ? _a : "（空白）";
                if (null != formattedValue) {
                    countMap.set(formattedValue, (countMap.get(formattedValue) || 0) + 1);
                    const unformattedSet = toUnformatted.get(formattedValue);
                    null != unformattedSet ? unformattedSet.add(originalValue) : toUnformatted.set(formattedValue, new Set([ originalValue ]));
                }
            }
        })), this.candidateKeys.set(fieldId, countMap), this.toUnformattedCache.set(fieldId, toUnformatted);
    }
    collectCandidateKeysForFilteredColumn(candidateField) {
        var _a, _b;
        const syncFilterItemsState = null === (_b = null === (_a = this.pluginOptions) || void 0 === _a ? void 0 : _a.syncFilterItemsState) || void 0 === _b || _b, filteredFields = this.filterStateManager.getActiveFilterFields().filter((field => field !== candidateField)), toUnformatted = new Map, formatFn = this.getFormatFnCache(candidateField), countMap = new Map;
        let records = [];
        if (syncFilterItemsState) {
            records = this.getRecords(this.table, !0).filter((record => filteredFields.every((field => {
                var _a;
                const filterType = null === (_a = this.filterStateManager.getFilterState(field)) || void 0 === _a ? void 0 : _a.type;
                "byValue" !== filterType && null != filterType && this.syncSingleStateFromTableData(field);
                return this.selectedKeys.get(field).has(record[field]);
            }))));
        } else records = this.table.internalProps.records;
        records.forEach((record => {
            var _a;
            if (isValid(record)) {
                const originalValue = record[candidateField], formattedValue = null !== (_a = formatFn(record)) && void 0 !== _a ? _a : "（空白）";
                if (countMap.set(formattedValue, (countMap.get(formattedValue) || 0) + 1), null != formattedValue) {
                    const unformattedSet = toUnformatted.get(formattedValue);
                    null != unformattedSet ? unformattedSet.add(originalValue) : toUnformatted.set(formattedValue, new Set([ originalValue ]));
                }
            }
        })), this.candidateKeys.set(candidateField, countMap), this.toUnformattedCache.set(candidateField, toUnformatted);
    }
    toggleSelectAll(fieldId, selected) {
        this.valueFilterOptionList.get(fieldId).forEach((option => {
            "none" !== option.itemContainer.style.display && (option.checkbox.checked = selected);
        }));
    }
    syncSelectAllCheckbox(fieldId) {
        const options = this.valueFilterOptionList.get(fieldId) || [], allChecked = options.every((o => o.checkbox.checked)), noneChecked = options.every((o => !o.checkbox.checked));
        this.selectAllCheckbox.checked = allChecked, this.selectAllCheckbox.indeterminate = !allChecked && !noneChecked;
    }
    onSearch(fieldId, value) {
        const items = this.valueFilterOptionList.get(fieldId), filterKeywords = value.toUpperCase().split(" ").filter((s => s));
        for (const item of items) {
            const txtValue = item.id.toUpperCase() || "", match = filterKeywords.some((keyword => txtValue.includes(keyword))), isVisible = 0 === filterKeywords.length || match;
            item.itemContainer.style.display = isVisible ? "flex" : "none";
        }
    }
    syncSingleStateFromTableData(fieldId) {
        const originalValues = new Set;
        this.table.internalProps.records.forEach((record => {
            isValid(record) && originalValues.add(record[fieldId]);
        }));
        const current = this.filterStateManager.getFilterState(fieldId);
        if ("byValue" === (null == current ? void 0 : current.type) && Array.isArray(current.values)) this.selectedKeys.set(fieldId, new Set(current.values)); else {
            if ("byCondition" === (null == current ? void 0 : current.type) && current.enable) {
                const visibleValues = new Set;
                return this.getRecords(this.table, !1).forEach((record => {
                    isValid(record) && visibleValues.add(record[fieldId]);
                })), void this.selectedKeys.set(fieldId, visibleValues);
            }
            this.selectedKeys.set(fieldId, originalValues);
        }
    }
    applyFilter(fieldId = this.selectedField) {
        var _a, _b;
        const options = this.valueFilterOptionList.get(fieldId);
        if (!options || 0 === options.length) return;
        const selections = options.map((option => option.checkbox.checked ? option.originalValue : null)).filter((key => null !== key)).flat();
        this.selectedKeys.set(fieldId, new Set(selections));
        const syncFilterItemsState = null === (_b = null === (_a = this.pluginOptions) || void 0 === _a ? void 0 : _a.syncFilterItemsState) || void 0 === _b || _b;
        selections.length >= 0 && selections.length < this.valueFilterOptionList.get(fieldId).length || !syncFilterItemsState ? this.filterStateManager.dispatch({
            type: FilterActionType.APPLY_FILTERS,
            payload: {
                field: fieldId,
                type: "byValue",
                values: selections,
                enable: !0,
                shouldKeepUnrelatedState: !syncFilterItemsState
            }
        }) : this.filterStateManager.dispatch({
            type: FilterActionType.REMOVE_FILTER,
            payload: {
                field: fieldId,
                type: "byValue"
            }
        });
    }
    clearFilter(fieldId) {
        this.filterStateManager.dispatch({
            type: FilterActionType.REMOVE_FILTER,
            payload: {
                field: fieldId
            }
        }), this.hide();
    }
    render(container) {
        var _a;
        const filterStyles = this.styles;
        this.filterByValuePanel = document.createElement("div"), applyStyles(this.filterByValuePanel, filterStyles.filterPanel), 
        this.searchContainer = document.createElement("div"), applyStyles(this.searchContainer, this.styles.searchContainer), 
        this.filterByValueSearchInput = document.createElement("input"), this.filterByValueSearchInput.type = "text", 
        this.filterByValueSearchInput.placeholder = (null === (_a = filterStyles.searchInput) || void 0 === _a ? void 0 : _a.placeholder) || "可使用空格分隔多个关键词", 
        applyStyles(this.filterByValueSearchInput, filterStyles.searchInput), this.searchContainer.appendChild(this.filterByValueSearchInput), 
        this.optionsContainer = document.createElement("div"), applyStyles(this.optionsContainer, this.styles.optionsContainer), 
        this.selectAllItemDiv = document.createElement("div"), applyStyles(this.selectAllItemDiv, this.styles.optionItem), 
        this.selectAllLabel = document.createElement("label"), applyStyles(this.selectAllLabel, this.styles.optionLabel), 
        this.selectAllCheckbox = document.createElement("input"), this.selectAllCheckbox.type = "checkbox", 
        this.selectAllCheckbox.checked = !0, applyStyles(this.selectAllCheckbox, filterStyles.checkbox), 
        this.totalCountSpan = document.createElement("span"), this.totalCountSpan.textContent = "", 
        applyStyles(this.totalCountSpan, filterStyles.countSpan), this.selectAllLabel.append(this.selectAllCheckbox, " 全选"), 
        this.selectAllItemDiv.appendChild(this.selectAllLabel), this.filterItemsContainer = document.createElement("div"), 
        this.optionsContainer.append(this.selectAllItemDiv, this.filterItemsContainer), 
        this.filterByValuePanel.append(this.searchContainer, this.optionsContainer), container.appendChild(this.filterByValuePanel), 
        this.bindEventForFilterByValue();
    }
    updateStyles(styles) {
        var _a;
        applyStyles(this.filterByValuePanel, styles.filterPanel), applyStyles(this.searchContainer, styles.searchContainer), 
        this.filterByValueSearchInput.placeholder = (null === (_a = styles.searchInput) || void 0 === _a ? void 0 : _a.placeholder) || "可使用空格分隔多个关键词", 
        applyStyles(this.filterByValueSearchInput, styles.searchInput), applyStyles(this.optionsContainer, styles.optionsContainer), 
        applyStyles(this.selectAllItemDiv, styles.optionItem), applyStyles(this.selectAllLabel, styles.optionLabel), 
        applyStyles(this.selectAllCheckbox, styles.checkbox);
    }
    renderFilterOptions(field) {
        var _a, _b, _c;
        const filterStyles = this.styles;
        this.filterItemsContainer.innerHTML = "", this.valueFilterOptionList.delete(field), 
        this.valueFilterOptionList.set(field, []);
        let totalCount = 0, allChecked = !0, noneChecked = !0;
        const selectedKeysSet = this.selectedKeys.get(field), itemDomList = [], isFiltered = null === (_a = this.filterStateManager.getFilterState(field)) || void 0 === _a ? void 0 : _a.enable, toUnformatted = this.toUnformattedCache.get(field), candidates = this.candidateKeys.get(field);
        if (!candidates || 0 === candidates.size) return;
        const candidatesArr = [ ...candidates.entries() ], sortedCandidatesArr = "number" == typeof candidatesArr[0][0] ? null === (_b = [ ...candidatesArr ]) || void 0 === _b ? void 0 : _b.sort((([a], [b]) => Number(a) - Number(b))) : null === (_c = [ ...candidatesArr ]) || void 0 === _c ? void 0 : _c.sort((([a], [b]) => String(a).localeCompare(String(b))));
        null == sortedCandidatesArr || sortedCandidatesArr.forEach((([val, count]) => {
            var _a, _b;
            totalCount += count;
            const unformattedArr = Array.from(toUnformatted.get(val) || new Set), itemDiv = document.createElement("div");
            applyStyles(itemDiv, filterStyles.optionItem), itemDiv.style.display = "flex";
            const label = document.createElement("label");
            applyStyles(label, filterStyles.optionLabel);
            const checkbox = document.createElement("input");
            checkbox.type = "checkbox", checkbox.value = String(val), checkbox.checked = !isFiltered || unformattedArr.some((v => null == selectedKeysSet ? void 0 : selectedKeysSet.has(v))), 
            applyStyles(checkbox, filterStyles.checkbox), checkbox.checked ? noneChecked = !1 : allChecked = !1;
            const countSpan = document.createElement("span");
            countSpan.textContent = String(count), applyStyles(countSpan, filterStyles.countSpan), 
            label.append(checkbox, ` ${(null === (_b = (_a = this.pluginOptions).checkboxItemFormat) || void 0 === _b ? void 0 : _b.call(_a, val, unformattedArr)) || val}`), 
            itemDiv.append(label, countSpan), this.filterItemsContainer.appendChild(itemDiv);
            const itemDom = {
                id: String(val),
                originalValue: unformattedArr,
                itemContainer: itemDiv,
                checkbox: checkbox,
                countSpan: countSpan
            };
            itemDomList.push(itemDom);
        })), this.valueFilterOptionList.set(field, itemDomList), this.selectAllCheckbox.checked = allChecked, 
        this.selectAllCheckbox.indeterminate = !allChecked && !noneChecked, this.totalCountSpan.textContent = String(totalCount);
    }
    bindEventForFilterByValue() {
        this._onInputKeyUpHandler = event => {
            const target = event.target;
            if (target instanceof HTMLInputElement && "text" === target.type) {
                const value = target.value;
                this.onSearch(this.selectedField, value);
            }
        }, this._onCheckboxChangeHandler = event => {
            const target = event.target;
            target instanceof HTMLInputElement && "checkbox" === target.type && (target === this.selectAllCheckbox ? this.toggleSelectAll(this.selectedField, this.selectAllCheckbox.checked) : this.syncSelectAllCheckbox(this.selectedField));
        }, this.filterByValuePanel.addEventListener("keyup", this._onInputKeyUpHandler), 
        this.filterByValuePanel.addEventListener("change", this._onCheckboxChangeHandler);
    }
    show() {
        var _a, _b;
        (null === (_a = this.filterStateManager.getFilterState(this.selectedField)) || void 0 === _a ? void 0 : _a.enable) ? this.collectCandidateKeysForFilteredColumn(this.selectedField) : this.collectCandidateKeysForUnfilteredColumn(this.selectedField);
        const filterType = null === (_b = this.filterStateManager.getFilterState(this.selectedField)) || void 0 === _b ? void 0 : _b.type;
        null != filterType && "byValue" !== filterType && this.syncSingleStateFromTableData(this.selectedField), 
        this.filterByValueSearchInput && (this.filterByValueSearchInput.value = ""), this.renderFilterOptions(this.selectedField), 
        this.filterByValuePanel.style.display = "block";
    }
    hide() {
        this.filterByValuePanel.style.display = "none";
    }
    clearSearchInputValue() {
        this.filterByValueSearchInput.value = "";
    }
    destroy() {
        if (this.filterByValuePanel && (this._onInputKeyUpHandler && this.filterByValuePanel.removeEventListener("keyup", this._onInputKeyUpHandler), 
        this._onCheckboxChangeHandler && this.filterByValuePanel.removeEventListener("change", this._onCheckboxChangeHandler)), 
        this._onInputKeyUpHandler = void 0, this._onCheckboxChangeHandler = void 0, this.filterByValuePanel) {
            const parent = this.filterByValuePanel.parentElement;
            parent ? parent.removeChild(this.filterByValuePanel) : this.filterByValuePanel.remove();
        }
        this.filterItemsContainer && (this.filterItemsContainer.innerHTML = ""), this.filterByValueSearchInput && (this.filterByValueSearchInput.value = ""), 
        this.selectedKeys && this.selectedKeys.clear(), this.candidateKeys && this.candidateKeys.clear(), 
        this.formatFnCache && this.formatFnCache.clear(), this.toUnformattedCache && this.toUnformattedCache.clear(), 
        this.valueFilterOptionList && this.valueFilterOptionList.clear(), this.filterByValuePanel = void 0, 
        this.filterByValueSearchInput = void 0, this.selectAllCheckbox = void 0, this.totalCountSpan = void 0, 
        this.filterItemsContainer = void 0;
    }
}
//# sourceMappingURL=value-filter.syncSingleStateFromTableData.backup.js.map