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

import { FilterActionType } from "./types";

import { applyStyles, filterStyles } from "./styles";

export class ValueFilter {
    constructor(table, filterStateManager) {
        this.uniqueKeys = new Map, this.displayToRawValueMap = new Map, this.valueFilterOptionList = new Map, 
        this.table = table, this.filterStateManager = filterStateManager, this.filterStateManager.subscribe((state => {
            const filterState = state.filters.get(this.selectedField);
            filterState && "byValue" === filterState.type && this.updateUI(filterState);
        }));
    }
    updateUI(filterState) {
        this.syncCheckboxesWithFilterState(filterState), this.syncSelectAllWithFilterState(filterState);
    }
    setSelectedField(fieldId) {
        this.selectedField = fieldId, this.collectUniqueColumnValues(fieldId);
    }
    collectUniqueColumnValues(fieldId) {
        var _a;
        const isEnable = null === (_a = this.filterStateManager.getFilterState(fieldId)) || void 0 === _a ? void 0 : _a.enable, displayValueMap = new Map, rawToDisplayMap = new Map, displayToRawMap = new Map;
        let targetCol = -1, targetRow = -1;
        for (let col = 0; col < this.table.colCount; col++) {
            for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
                const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
                if (bodyInfo && bodyInfo.field === fieldId) {
                    targetCol = col, targetRow = row;
                    break;
                }
            }
            if (-1 !== targetCol) break;
        }
        if (isEnable) {
            const records = this.table.internalProps.records, recordsLength = records.length;
            for (let i = 0; i < recordsLength; i++) {
                let rawValue, displayValue;
                if (-1 !== targetCol) {
                    const row = this.table.columnHeaderLevelCount + i;
                    rawValue = records[i][fieldId];
                    const bodyInfo = this.table.internalProps.layoutMap.getBody(targetCol, row);
                    displayValue = bodyInfo && "fieldFormat" in bodyInfo && bodyInfo.fieldFormat && "function" == typeof bodyInfo.fieldFormat ? bodyInfo.fieldFormat({
                        [fieldId]: rawValue
                    }) : rawValue;
                }
                null != rawValue && (displayValueMap.set(displayValue, (displayValueMap.get(displayValue) || 0) + 1), 
                rawToDisplayMap.set(rawValue, displayValue), displayToRawMap.set(displayValue, rawValue));
            }
        } else {
            const currentLength = this.table.internalProps.dataSource.length;
            for (let i = 0; i < currentLength; i++) {
                let rawValue, displayValue;
                if (-1 !== targetCol) {
                    const row = this.table.columnHeaderLevelCount + i;
                    row < this.table.rowCount && (rawValue = this.table.getCellOriginValue(targetCol, row), 
                    displayValue = this.table.getCellValue(targetCol, row));
                } else rawValue = this.table.getFieldData(String(fieldId), -1 !== targetCol ? targetCol : 0, this.table.columnHeaderLevelCount + i), 
                displayValue = rawValue;
                null != rawValue && (displayValueMap.set(displayValue, (displayValueMap.get(displayValue) || 0) + 1), 
                rawToDisplayMap.set(rawValue, displayValue), displayToRawMap.set(displayValue, rawValue));
            }
        }
        this.displayToRawValueMap.set(fieldId, displayToRawMap);
        const uniqueValues = Array.from(displayValueMap.entries()).map((([displayValue, count]) => ({
            value: displayValue,
            count: count,
            rawValue: displayToRawMap.get(displayValue)
        })));
        this.uniqueKeys.set(fieldId, uniqueValues);
    }
    onValueSelect(fieldId, displayValue, selected) {
        const displayToRawMap = this.displayToRawValueMap.get(fieldId), rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue, filter = this.filterStateManager.getFilterState(fieldId);
        if (filter) {
            const updatedValues = selected ? [ ...filter.values || [], rawValue ] : (filter.values || []).filter((v => v !== rawValue));
            this.filterStateManager.dispatch({
                type: FilterActionType.UPDATE_FILTER,
                payload: {
                    field: fieldId,
                    values: updatedValues
                }
            });
        } else this.filterStateManager.dispatch({
            type: FilterActionType.ADD_FILTER,
            payload: {
                field: fieldId,
                type: "byValue",
                values: [ rawValue ]
            }
        });
    }
    toggleSelectAll(fieldId, selected) {
        var _a;
        const filter = this.filterStateManager.getFilterState(fieldId), rawValuesToUpdate = selected && (null === (_a = this.uniqueKeys.get(fieldId)) || void 0 === _a ? void 0 : _a.map((item => item.rawValue))) || [];
        if (filter) {
            const updatedValues = selected ? rawValuesToUpdate : [];
            this.filterStateManager.dispatch({
                type: FilterActionType.UPDATE_FILTER,
                payload: {
                    field: fieldId,
                    values: updatedValues
                }
            });
        } else this.filterStateManager.dispatch({
            type: FilterActionType.ADD_FILTER,
            payload: {
                field: fieldId,
                type: "byValue",
                values: rawValuesToUpdate,
                enable: !0
            }
        });
    }
    onSearch(fieldId, value) {
        const filterKeywords = value.toUpperCase().split(" ").filter((s => s)), items = this.valueFilterOptionList.get(fieldId);
        for (const item of items) {
            const txtValue = item.id.toUpperCase() || "", match = filterKeywords.some((keyword => txtValue.includes(keyword)));
            item.itemContainer.style.display = 0 === filterKeywords.length || match ? "flex" : "none";
        }
    }
    initFilterStateFromTableData(fieldId) {
        const filter = this.filterStateManager.getFilterState(fieldId), selectedRawValues = new Set, displayToRawMap = this.displayToRawValueMap.get(fieldId);
        let targetCol = -1;
        for (let col = 0; col < this.table.colCount; col++) {
            for (let row = this.table.columnHeaderLevelCount; row < this.table.rowCount; row++) if (!this.table.internalProps.layoutMap.isHeader(col, row)) {
                const bodyInfo = this.table.internalProps.layoutMap.getBody(col, row);
                if (bodyInfo && bodyInfo.field === fieldId) {
                    targetCol = col;
                    break;
                }
            }
            if (-1 !== targetCol) break;
        }
        const currentLength = this.table.internalProps.dataSource.length;
        for (let i = 0; i < currentLength; i++) {
            let displayValue, rawValue;
            if (-1 !== targetCol) {
                const row = this.table.columnHeaderLevelCount + i;
                row < this.table.rowCount && (displayValue = this.table.getCellValue(targetCol, row), 
                rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue);
            } else displayValue = this.table.getFieldData(String(fieldId), -1 !== targetCol ? targetCol : 0, this.table.columnHeaderLevelCount + i), 
            rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
            null != rawValue && selectedRawValues.add(rawValue);
        }
        (!filter || !arrayEqual(filter.values, Array.from(selectedRawValues))) && (filter ? this.filterStateManager.dispatch({
            type: FilterActionType.UPDATE_FILTER,
            payload: {
                field: fieldId,
                type: "byValue",
                values: Array.from(selectedRawValues)
            }
        }) : this.filterStateManager.dispatch({
            type: FilterActionType.ADD_FILTER,
            payload: {
                field: fieldId,
                type: "byValue",
                values: Array.from(selectedRawValues)
            }
        }));
    }
    syncCheckboxesWithFilterState(filter) {
        if (!filter) return;
        const selectedRawValues = filter.values || [], displayToRawMap = this.displayToRawValueMap.get(filter.field), optionDomList = this.valueFilterOptionList.get(filter.field);
        null == optionDomList || optionDomList.forEach((optionDom => {
            var _a, _b;
            const displayValue = optionDom.originalValue, rawValue = displayToRawMap ? displayToRawMap.get(displayValue) : displayValue;
            optionDom.checkbox.checked = selectedRawValues.some((v => v === rawValue));
            const count = (null === (_b = null === (_a = this.uniqueKeys.get(filter.field)) || void 0 === _a ? void 0 : _a.find((key => String(key.value) === optionDom.id))) || void 0 === _b ? void 0 : _b.count) || 0;
            optionDom.countSpan.textContent = String(count), optionDom.itemContainer.style.display = 0 === count ? "none" : "flex";
        }));
    }
    syncSelectAllWithFilterState(filter) {
        var _a;
        if (!filter || !filter.values) return this.selectAllCheckbox.checked = !1, void (this.selectAllCheckbox.indeterminate = !1);
        const uniqueValuesCount = (null === (_a = this.uniqueKeys.get(filter.field)) || void 0 === _a ? void 0 : _a.length) || 0;
        0 === uniqueValuesCount || 0 === filter.values.length ? (this.selectAllCheckbox.checked = !1, 
        this.selectAllCheckbox.indeterminate = !1) : filter.values.length === uniqueValuesCount ? (this.selectAllCheckbox.checked = !0, 
        this.selectAllCheckbox.indeterminate = !1) : (this.selectAllCheckbox.checked = !1, 
        this.selectAllCheckbox.indeterminate = !0);
    }
    applyFilter(fieldId = this.selectedField) {
        var _a, _b;
        const selectedKeys = (null === (_a = this.filterStateManager.getFilterState(fieldId)) || void 0 === _a ? void 0 : _a.values) || [];
        selectedKeys.length > 0 && selectedKeys.length < (null === (_b = this.uniqueKeys.get(fieldId)) || void 0 === _b ? void 0 : _b.length) ? this.filterStateManager.dispatch({
            type: FilterActionType.APPLY_FILTERS,
            payload: {
                field: fieldId,
                type: "byValue",
                values: selectedKeys,
                enable: !0
            }
        }) : this.filterStateManager.dispatch({
            type: FilterActionType.REMOVE_FILTER,
            payload: {
                field: fieldId
            }
        });
    }
    clearFilter(fieldId) {
        this.filterStateManager.dispatch({
            type: FilterActionType.REMOVE_FILTER,
            payload: {
                field: fieldId
            }
        }), this.hide();
    }
    render(container) {
        this.filterByValuePanel = document.createElement("div"), applyStyles(this.filterByValuePanel, filterStyles.filterPanel);
        const searchContainer = document.createElement("div");
        applyStyles(searchContainer, filterStyles.searchContainer), this.filterByValueSearchInput = document.createElement("input"), 
        this.filterByValueSearchInput.type = "text", this.filterByValueSearchInput.placeholder = "可使用空格分隔多个关键词", 
        applyStyles(this.filterByValueSearchInput, filterStyles.searchInput), searchContainer.appendChild(this.filterByValueSearchInput);
        const optionsContainer = document.createElement("div");
        applyStyles(optionsContainer, filterStyles.optionsContainer);
        const selectAllItemDiv = document.createElement("div");
        applyStyles(selectAllItemDiv, filterStyles.optionItem);
        const selectAllLabel = document.createElement("label");
        applyStyles(selectAllLabel, filterStyles.optionLabel), this.selectAllCheckbox = document.createElement("input"), 
        this.selectAllCheckbox.type = "checkbox", this.selectAllCheckbox.checked = !0, applyStyles(this.selectAllCheckbox, filterStyles.checkbox), 
        selectAllLabel.append(this.selectAllCheckbox, " 全选"), selectAllItemDiv.appendChild(selectAllLabel), 
        this.filterItemsContainer = document.createElement("div"), optionsContainer.append(selectAllItemDiv, this.filterItemsContainer), 
        this.filterByValuePanel.append(searchContainer, optionsContainer), container.appendChild(this.filterByValuePanel), 
        this.bindEventForFilterByValue();
    }
    renderFilterOptions(field) {
        var _a, _b;
        this.filterItemsContainer.innerHTML = "", this.valueFilterOptionList.delete(field), 
        this.valueFilterOptionList.set(field, []);
        const selectedRawValues = (null === (_a = this.filterStateManager.getFilterState(field)) || void 0 === _a ? void 0 : _a.values) || [], selectedRawValueSet = new Set(selectedRawValues), itemDomList = [];
        null === (_b = this.uniqueKeys.get(field)) || void 0 === _b || _b.forEach((({value: value, count: count, rawValue: rawValue}) => {
            const itemDiv = document.createElement("div");
            applyStyles(itemDiv, filterStyles.optionItem);
            const label = document.createElement("label");
            applyStyles(label, filterStyles.optionLabel);
            const checkbox = document.createElement("input");
            checkbox.type = "checkbox", checkbox.value = String(value), checkbox.checked = selectedRawValueSet.has(rawValue), 
            applyStyles(checkbox, filterStyles.checkbox);
            const countSpan = document.createElement("span");
            countSpan.textContent = String(count), applyStyles(countSpan, filterStyles.countSpan), 
            label.append(checkbox, ` ${value}`), itemDiv.append(label, countSpan), this.filterItemsContainer.appendChild(itemDiv);
            const itemDom = {
                id: String(value),
                originalValue: value,
                itemContainer: itemDiv,
                checkbox: checkbox,
                countSpan: countSpan
            };
            itemDomList.push(itemDom);
        })), this.valueFilterOptionList.set(field, itemDomList);
    }
    bindEventForFilterByValue() {
        this.filterByValuePanel.addEventListener("keyup", (event => {
            const target = event.target;
            if (target instanceof HTMLInputElement && "text" === target.type) {
                const value = target.value;
                this.onSearch(this.selectedField, value);
            }
        })), this.filterByValuePanel.addEventListener("change", (event => {
            var _a, _b;
            const target = event.target;
            if (target instanceof HTMLInputElement && "checkbox" === target.type) if (target === this.selectAllCheckbox) this.toggleSelectAll(this.selectedField, this.selectAllCheckbox.checked); else {
                const checkbox = target, checked = checkbox.checked, value = null === (_b = null === (_a = this.valueFilterOptionList.get(this.selectedField)) || void 0 === _a ? void 0 : _a.find((item => item.id === checkbox.value))) || void 0 === _b ? void 0 : _b.originalValue;
                this.onValueSelect(this.selectedField, value, checked);
            }
        }));
    }
    show() {
        this.initFilterStateFromTableData(this.selectedField), this.renderFilterOptions(this.selectedField), 
        this.filterByValuePanel.style.display = "block";
    }
    hide() {
        this.filterByValuePanel.style.display = "none";
    }
}
//# sourceMappingURL=value-filter.js.map