"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.PredicateApiImpl = exports.getTreeSelectionStateForPredicateInputs = exports.clearPredicateDefMapMemo = void 0;
const tslib_1 = require("tslib");
const ApiBase_1 = require("./ApiBase");
const ArrayExtensions_1 = tslib_1.__importDefault(require("../../Utilities/Extensions/ArrayExtensions"));
const StringExtensions_1 = tslib_1.__importDefault(require("../../Utilities/Extensions/StringExtensions"));
const PredicateInternalApi_1 = require("../Internal/PredicateInternalApi");
const AdaptablePredicate_1 = require("../../AdaptableState/Common/AdaptablePredicate");
const GeneralConstants_1 = require("../../Utilities/Constants/GeneralConstants");
const infinite_react_1 = require("@infinite-table/infinite-react");
const predicateDefMap_MEMO = new Map();
const clearPredicateDefMapMemo = () => {
    predicateDefMap_MEMO.clear();
};
exports.clearPredicateDefMapMemo = clearPredicateDefMapMemo;
const treeSelectionStatesForPredicates = new WeakMap();
const getTreeSelectionStateForPredicateInputs = (inputs, cache = treeSelectionStatesForPredicates) => {
    const treeSelectionState = cache.get(inputs);
    if (treeSelectionState) {
        return treeSelectionState;
    }
    const newTreeSelectionState = new infinite_react_1.TreeSelectionState({
        defaultSelection: false,
        selectedPaths: inputs,
    }, {
        treePaths: inputs,
        strictCheckPaths: false,
    });
    cache.set(inputs, newTreeSelectionState);
    return newTreeSelectionState;
};
exports.getTreeSelectionStateForPredicateInputs = getTreeSelectionStateForPredicateInputs;
class PredicateApiImpl extends ApiBase_1.ApiBase {
    internalApi;
    constructor(_adaptable) {
        super(_adaptable);
        this.internalApi = new PredicateInternalApi_1.PredicateInternalApi(_adaptable);
    }
    getPredicateDefs() {
        return this.internalApi.mergeSystemAndCustomPredicates(this.getSystemPredicateDefs(), this.getCustomPredicateDefs());
    }
    getSystemPredicateDefs() {
        return AdaptablePredicate_1.SystemPredicateDefs;
    }
    getSystemPredicateDefsByModuleScope(moduleScope) {
        return AdaptablePredicate_1.SystemPredicateDefs.filter((p) => p.moduleScope.includes(moduleScope));
    }
    getCustomPredicateDefs() {
        const predicateDefsMap = new Map(this.getSystemPredicateDefs().map((p) => [p.id, p]));
        const customPredicateDefs = this.getPredicateOptions().customPredicateDefs || [];
        return customPredicateDefs
            .map((p) => {
            if ('extends' in p) {
                const foundPredicateDef = predicateDefsMap.get(p.extends);
                if (!foundPredicateDef) {
                    this.logWarn(`Custom predicate with ID '${p.id}' references a missing 'extends' target '${p.extends}'. No such system or custom predicate found.`);
                    return null;
                }
                else {
                    const { extends: _, ...rest } = p;
                    const result = {
                        ...foundPredicateDef,
                        ...rest,
                        id: rest.id,
                    };
                    predicateDefsMap.set(result.id, result);
                    return result;
                }
            }
            predicateDefsMap.set(p.id, p);
            return p;
        })
            .filter((p) => p !== null);
    }
    getPredicateDefsByModuleScope(moduleScope) {
        return this.getPredicateDefs().filter((p) => p.moduleScope.includes(moduleScope));
    }
    getPredicateDefById(predicateId) {
        return this.getPredicateDefs().find((predicateDef) => predicateDef.id === predicateId);
    }
    getSystemPredicateDefById(predicateId) {
        return this.getSystemPredicateDefs().find((predicateDef) => predicateDef.id === predicateId);
    }
    getCustomPredicateDefById(predicateId) {
        return this.getCustomPredicateDefs().find((predicateDef) => predicateDef.id === predicateId);
    }
    predicateToString(predicate) {
        const predicateDef = this.getPredicateDefById(predicate.PredicateId);
        if (!predicateDef) {
            this.logWarn('Cannot find Predicate with Id:' + predicate.PredicateId);
            return '[Predicate Not found]';
        }
        let strPredicate = predicateDef.hasOwnProperty('toString')
            ? predicateDef.toString({ inputs: predicate.Inputs })
            : predicateDef.label;
        if ('ColumnId' in predicate) {
            strPredicate = `[${predicate.ColumnId}] ${strPredicate}`;
        }
        return strPredicate;
    }
    predicatesToString(predicates, logicalOperator = 'AND') {
        return predicates
            .map((predicate) => this.predicateToString(predicate))
            .join(` ${logicalOperator} `);
    }
    isValidPredicate(predicate) {
        if (!predicate) {
            return false;
        }
        const adaptablePredicateDef = this.getPredicateDefById(predicate.PredicateId);
        if (!adaptablePredicateDef) {
            return false;
        }
        // not perfect but if there are no inputs - and there should be some - we treat the filter as invalid
        if (adaptablePredicateDef.inputs) {
            if (ArrayExtensions_1.default.IsNullOrEmpty(predicate.Inputs) ||
                StringExtensions_1.default.IsNullOrEmpty(predicate.Inputs[0])) {
                return false;
            }
        }
        return !(['In', 'NotIn'].includes(predicate.PredicateId) &&
            ArrayExtensions_1.default.IsNullOrEmpty(predicate.Inputs));
    }
    isEveryPredicateValid(predicates) {
        return predicates.every((predicate) => this.isValidPredicate(predicate));
    }
    handleColumnPredicate(predicate, context, defaultReturn) {
        if (!predicate.ColumnId) {
            return this.handlePredicate(predicate, context, defaultReturn);
        }
        const paramsForColumn = {
            ...context,
            column: this.getColumnApi().getColumnWithColumnId(predicate.ColumnId),
            // value: params?.node?.data?.[predicate.ColumnId],
            value: this.getGridApi().getRawValueFromRowNode(context.node, predicate.ColumnId),
        };
        if ('oldValue' in paramsForColumn) {
            delete paramsForColumn.oldValue;
        }
        return this.handlePredicate(predicate, paramsForColumn, defaultReturn);
    }
    handleColumnPredicates(predicates, context, defaultReturn) {
        return predicates.every((predicate) => this.handleColumnPredicate(predicate, context, defaultReturn));
    }
    handlePredicate(predicate, context, defaultReturn) {
        if (!predicate) {
            return defaultReturn;
        }
        // memoization to avoid repeated lookups
        // during filtering of large datasets
        // see #column_filter_predicates_evaluation
        let predicateDef = predicateDefMap_MEMO.get(predicate.PredicateId);
        if (predicateDef === undefined) {
            predicateDef = this.getPredicateDefById(predicate.PredicateId);
            predicateDefMap_MEMO.set(predicate.PredicateId, predicateDef);
        }
        if (predicateDef === undefined) {
            return defaultReturn;
        }
        if (predicateDef.inputs?.some((_, i) => predicate.Inputs?.[i] === undefined || predicate.Inputs?.[i] === '')) {
            return defaultReturn;
        }
        let treeSelectionState;
        if (context.column.columnId === GeneralConstants_1.AG_GRID_GROUPED_COLUMN && predicate.PredicateId === 'In') {
            treeSelectionState = (0, exports.getTreeSelectionStateForPredicateInputs)(predicate.Inputs);
        }
        try {
            return predicateDef.handler({
                adaptableApi: this.getAdaptableApi(),
                inputs: predicate.Inputs,
                treeSelectionState,
                ...context,
            });
        }
        catch (error) {
            this.logError(`Error in predicate ${predicateDef.label}`, error);
            return false;
        }
    }
    handlePredicates(predicates, params, defaultReturn) {
        if (predicates === undefined || predicates === null || predicates?.length === 0) {
            return this.handlePredicate(undefined, params, defaultReturn);
        }
        if (predicates.length === 1) {
            return this.handlePredicate(predicates[0], params, defaultReturn);
        }
        if (params.predicatesOperator && params.predicatesOperator === 'OR') {
            return predicates?.some((p) => this.handlePredicate(p, params, defaultReturn));
        }
        return predicates?.every((p) => this.handlePredicate(p, params, defaultReturn));
    }
    useCaseSensitivity(columnId) {
        return this.internalApi.shouldUseCaseSensitivePredicates(columnId);
    }
}
exports.PredicateApiImpl = PredicateApiImpl;
