/*!
 * Built by Revolist OU ❤️
 */
import { GROUP_DEPTH, GROUP_EXPANDED, PSEUDO_GROUP_COLUMN, PSEUDO_GROUP_ITEM, PSEUDO_GROUP_ITEM_ID, PSEUDO_GROUP_ITEM_VALUE, GROUP_ORIGINAL_INDEX, GROUP_COLUMN_PROP, } from "./grouping.const";
function getGroupValueDefault(item, prop) {
    return item[prop] || null;
}
function isDataRow(item) {
    return item != null;
}
// get source based on proxy item collection to preserve rgRow order
export function getSource(source, items, withoutGrouping = false) {
    let index = 0;
    const result = {
        source: [],
        prevExpanded: {},
        oldNewIndexes: {},
    };
    // order important here, expected parent is first, then others
    items.forEach(i => {
        const model = source[i];
        if (!withoutGrouping) {
            result.source.push(model);
            return;
        }
        // grouping filter
        if (isGrouping(model)) {
            if (getExpanded(model)) {
                result.prevExpanded[model[PSEUDO_GROUP_ITEM_VALUE]] = true;
            }
        }
        else if (!isDataRow(model)) {
            return;
        }
        else {
            result.source.push(model);
            result.oldNewIndexes[i] = index;
            index++;
        }
    });
    return result;
}
export function getExpanded(model = {}) {
    return model[GROUP_EXPANDED];
}
function flattenGroupMaps({ groupedValues, parentIds, isExpanded, itemIndex, expandedAll, prevExpanded, columnProps, }) {
    const depth = parentIds.length;
    const sourceWithGroups = [];
    // collapse all groups in the beginning
    let trimmed = {};
    // index mapping
    let oldNewIndexMap = {};
    groupedValues.forEach((innerGroupedValues, groupId) => {
        const levelIds = [...parentIds, groupId];
        const mergedIds = levelIds.join(',');
        const isGroupExpanded = isExpanded && (!!expandedAll || !!prevExpanded[mergedIds]);
        sourceWithGroups.push({
            [PSEUDO_GROUP_ITEM]: groupId,
            [GROUP_DEPTH]: depth,
            [PSEUDO_GROUP_ITEM_ID]: JSON.stringify(levelIds),
            [PSEUDO_GROUP_ITEM_VALUE]: mergedIds,
            [GROUP_EXPANDED]: isGroupExpanded,
            [GROUP_COLUMN_PROP]: columnProps[depth],
            [columnProps[depth]]: groupId,
        });
        itemIndex += 1;
        // If parent group is collapsed, mark all items as hidden
        if (!isExpanded && depth) {
            trimmed[itemIndex] = true;
        }
        if (Array.isArray(innerGroupedValues)) {
            // This branch handles leaf nodes (actual data items)
            innerGroupedValues.forEach(value => {
                itemIndex += 1;
                if (!isGroupExpanded) {
                    trimmed[itemIndex] = true; // Mark items as hidden if group is collapsed
                }
                oldNewIndexMap[value[GROUP_ORIGINAL_INDEX]] = itemIndex; // Keep track of new positions
            });
            sourceWithGroups.push(...innerGroupedValues);
        }
        else {
            // This branch handles nested groups (further subgroups)
            const children = flattenGroupMaps({
                groupedValues: innerGroupedValues,
                parentIds: levelIds,
                isExpanded: isGroupExpanded,
                itemIndex,
                expandedAll,
                prevExpanded,
                columnProps,
            }); // Recursively process subgroups
            sourceWithGroups.push(...children.source);
            trimmed = Object.assign(Object.assign({}, children.trimmed), trimmed);
            oldNewIndexMap = Object.assign(Object.assign({}, children.oldNewIndexMap), oldNewIndexMap);
            itemIndex = children.itemIndex;
        }
    });
    return {
        source: sourceWithGroups,
        oldNewIndexMap,
        trimmed,
        itemIndex,
    };
}
/**
 * Gather data for grouping
 * @param array - flat data array
 * @param columnProps - ids of groups
 * @param expanded - potentially expanded items if present
 */
export function gatherGrouping(array, columnProps, { prevExpanded = {}, expandedAll = false, getGroupValue = getGroupValueDefault, }) {
    const groupedItems = new Map();
    array.forEach((item, originalIndex) => {
        if (!isDataRow(item)) {
            return;
        }
        const groupLevelValues = columnProps.map(groupId => getGroupValue(item, groupId));
        const lastLevelValue = groupLevelValues.pop();
        let currentGroupLevel = groupedItems;
        groupLevelValues.forEach(value => {
            if (!currentGroupLevel.has(value)) {
                currentGroupLevel.set(value, new Map());
            }
            currentGroupLevel = currentGroupLevel.get(value);
        });
        if (!currentGroupLevel.has(lastLevelValue)) {
            const groupItems = [];
            currentGroupLevel.set(lastLevelValue, groupItems);
        }
        const lastLevelItems = currentGroupLevel.get(lastLevelValue);
        lastLevelItems.push(Object.assign(Object.assign({}, item), { [GROUP_ORIGINAL_INDEX]: originalIndex }));
    });
    const groupingDepth = columnProps.length;
    const { source: sourceWithGroups, trimmed, oldNewIndexMap } = flattenGroupMaps({
        groupedValues: groupedItems,
        parentIds: [],
        isExpanded: true,
        itemIndex: -1,
        expandedAll,
        prevExpanded,
        columnProps
    });
    return {
        sourceWithGroups, // updates source mirror
        depth: groupingDepth, // largest depth for grouping
        trimmed, // used for expand/collapse grouping values
        oldNewIndexMap, // used for mapping old values to new
    };
}
export function getGroupingName(rgRow) {
    return rgRow === null || rgRow === void 0 ? void 0 : rgRow[PSEUDO_GROUP_ITEM];
}
export function isGrouping(rgRow) {
    return typeof (rgRow === null || rgRow === void 0 ? void 0 : rgRow[PSEUDO_GROUP_ITEM]) !== 'undefined';
}
export function isGroupingColumn(column) {
    return typeof (column === null || column === void 0 ? void 0 : column[PSEUDO_GROUP_COLUMN]) !== 'undefined';
}
export function measureEqualDepth(groupA, groupB) {
    const ln = groupA.length;
    let i = 0;
    for (; i < ln; i++) {
        if (groupA[i] !== groupB[i]) {
            return i;
        }
    }
    return i;
}
export function getParsedGroup(id) {
    const parseGroup = JSON.parse(id);
    // extra precaution and type safeguard
    if (!Array.isArray(parseGroup)) {
        return null;
    }
    return parseGroup;
}
// check if items is child of current clicked group
export function isSameGroup(currentGroup, currentModel, nextModel) {
    const nextGroup = getParsedGroup(nextModel[PSEUDO_GROUP_ITEM_ID]);
    if (!nextGroup) {
        return false;
    }
    const depth = measureEqualDepth(currentGroup, nextGroup);
    return currentModel[GROUP_DEPTH] < depth;
}
