/*!
 * Built by Revolist OU ❤️
 */
import findIndex from "lodash/findIndex";
import range from "lodash/range";
import { createStore } from "@stencil/store";
import { gatherTrimmedItems, trimmedPlugin } from "./trimmed.plugin";
import { setStore } from "../../utils";
import { proxyPlugin } from "./data.proxy";
/**
 * Data store
 * Manage the state of a data source and provide methods for updating, adding, and refreshing the data.
 */
export class DataStore {
    get store() {
        return this.dataStore;
    }
    constructor(type, storeData) {
        const store = (this.dataStore = createStore(Object.assign({ items: [], proxyItems: [], source: [], groupingDepth: 0, groups: {}, type, trimmed: {}, groupingCustomRenderer: undefined }, storeData)));
        store.use(proxyPlugin(store));
        store.use(trimmedPlugin(store));
    }
    /**
     * full data source update
     * @param source - data column/rgRow source
     * @param grouping - grouping information if present
     * @param silent - if true, store will be updated without resetting trimmed state
     * @param preserveTrimmed - if true, current trimmed indexes will be re-applied to the new source, use with caution because physical indexes may change across full data refreshes
     */
    updateData(source, grouping, 
    // if true, store will be updated without resetting trimmed state
    silent = false, 
    // if true, current trimmed indexes will be re-applied to the new source
    preserveTrimmed = false) {
        const trimmed = this.store.get('trimmed');
        const trimmedItems = silent && preserveTrimmed ? gatherTrimmedItems(trimmed) : null;
        // during full update we do drop trim
        if (!silent) {
            this.store.set('trimmed', {});
        }
        // clear items
        this.store.set('items', []);
        const items = range(0, (source === null || source === void 0 ? void 0 : source.length) || 0);
        // set proxy first
        setStore(this.store, {
            source,
            proxyItems: [...items],
        });
        // Explicit trim preservation is opt-in because physical indexes may change
        // across full data refreshes.
        this.store.set('items', trimmedItems ? items.filter(i => !trimmedItems[i]) : items);
        // apply grouping if present
        if (grouping) {
            setStore(this.store, {
                groupingDepth: grouping.depth,
                // if groups are not provided, we will consider that there is only one group with all items
                groups: grouping.groups,
                groupingCustomRenderer: grouping.customRenderer,
            });
        }
    }
    addTrimmed(some) {
        let trimmed = this.store.get('trimmed');
        trimmed = Object.assign(Object.assign({}, trimmed), some);
        setStore(this.store, { trimmed });
    }
    setSourceData(items, mutate = true) {
        setSourceByVirtualIndex(this.store, items, mutate);
    }
    // local data update
    setData(input) {
        const data = Object.assign({}, input);
        setStore(this.store, data);
    }
    refresh() {
        const source = this.store.get('source');
        this.store.set('source', [...source]);
    }
}
/**
 * get physical index by virtual
 * @param store - store to process
 */
export function getPhysical(store, virtualIndex) {
    const items = store.get('items');
    return items[virtualIndex];
}
/**
 * get all visible items
 * @param store - store to process
 */
export function getVisibleSourceItem(store) {
    const source = store.get('source');
    return store.get('items').map(v => source[v]);
}
/**
 * get mapped item from source
 * @param store - store to process
 * @param virtualIndex - virtual index to process
 */
export const getSourceItem = (store, virtualIndex) => {
    const source = store.get('source');
    return source[getSourcePhysicalIndex(store, virtualIndex)];
};
/**
 * Get physical index from virtual index
 */
export const getSourcePhysicalIndex = (store, virtualIndex) => {
    const items = store.get('items');
    return items[virtualIndex];
};
/**
 * Apply silently item/model/row value to data source
 * @param store  - data source with changes
 * @param modelByIndex - collection of rows/values with virtual indexes to setup/replace in store/data source
 * @param mutate - if true, store will be mutated and whole viewport will be re-rendered
 */
export function setSourceByVirtualIndex(store, modelByIndex, mutate = true) {
    const items = store.get('items');
    const source = store.get('source');
    for (let virtualIndex in modelByIndex) {
        const realIndex = items[virtualIndex];
        const item = modelByIndex[virtualIndex];
        source[realIndex] = item;
    }
    if (mutate) {
        store.set('source', [...source]);
    }
}
/**
 * set item to source
 * @param store  - store to process
 * @param modelByIndex - collection of rows with physical indexes to setup
 * @param mutate - if true, store will be mutated and whole viewport will be re-rendered
 */
export function setSourceByPhysicalIndex(store, modelByIndex, mutate = true) {
    const source = store.get('source');
    for (let index in modelByIndex) {
        source[index] = modelByIndex[index];
    }
    if (mutate) {
        store.set('source', [...source]);
    }
}
export function setItems(store, items) {
    store.set('items', items);
}
export function getSourceItemVirtualIndexByProp(store, prop) {
    const items = store.get('items');
    const source = store.get('source');
    const physicalIndex = findIndex(source, { prop });
    return items.indexOf(physicalIndex);
}
