"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports._agGridApi_getValue = exports.AgGridAdapter = void 0;
const tslib_1 = require("tslib");
const core_1 = require("ag-grid-community");
const GeneralConstants_1 = require("../Utilities/Constants/GeneralConstants");
const Uuid_1 = require("../PredefinedConfig/Uuid");
const ArrayExtensions_1 = tslib_1.__importDefault(require("../Utilities/Extensions/ArrayExtensions"));
const ModuleConstants = tslib_1.__importStar(require("../Utilities/Constants/ModuleConstants"));
class AgGridAdapter {
    constructor(adaptableInstance) {
        this.adaptableInstance = adaptableInstance;
    }
    get adaptableOptions() {
        return this.adaptableInstance.adaptableOptions;
    }
    get adaptableApi() {
        return this.adaptableInstance.api;
    }
    get logger() {
        return this.adaptableInstance.logger;
    }
    destroy() {
        this.adaptableInstance = null;
    }
    setAgGridApi(gridApi) {
        this.DANGER_USE_GETTER_gridApi = gridApi;
    }
    getAgGridApi(skipLogging) {
        if (this.DANGER_USE_GETTER_gridApi) {
            return this.DANGER_USE_GETTER_gridApi;
        }
        if (this.DANGER_gridApi_from_args) {
            return this.DANGER_gridApi_from_args;
        }
        if (!skipLogging) {
            console.error('AgGridApi is not available yet');
        }
    }
    /**
     * When AG Grid is rendered the first time, the AG GridApi is not yet set in the Adaptable context (as it's set only AFTER the grid is fully initialised)
     * yet we need it when evaluating custom GridOptions properties on the first render.
     * to handle this edge case, we try to extract the AG GridApi from the invocation arguments
     */
    grabAgGridApiOnTheFly(args) {
        var _a;
        if (this.DANGER_USE_GETTER_gridApi || this.DANGER_gridApi_from_args) {
            return;
        }
        if (Array.isArray(args) &&
            args[0] &&
            typeof args[0].api === 'object' &&
            // can't ise instanceof operator because gridApi is exported as interface
            typeof ((_a = args[0].api) === null || _a === void 0 ? void 0 : _a.getGridId) === 'function') {
            this.DANGER_gridApi_from_args = args[0].api;
        }
    }
    updateGridOptions(options) {
        var _a;
        (_a = this.getAgGridApi()) === null || _a === void 0 ? void 0 : _a.updateGridOptions(options);
    }
    getGridOption(key) {
        var _a;
        return (_a = this.getAgGridApi()) === null || _a === void 0 ? void 0 : _a.getGridOption(key);
    }
    setGridOption(key, value) {
        var _a;
        (_a = this.getAgGridApi()) === null || _a === void 0 ? void 0 : _a.setGridOption(key, value);
    }
    getUserGridOptionsProperty(propertyName) {
        return this.adaptableInstance.agGridOptionsService.getUserGridOptionsProperty(propertyName);
    }
    updateColumnFilterActiveState() {
        var _a;
        const columnFilters = this.adaptableApi.columnFilterApi.getActiveColumnFilters();
        const activeFilters = (_a = columnFilters === null || columnFilters === void 0 ? void 0 : columnFilters.filter) === null || _a === void 0 ? void 0 : _a.call(columnFilters, (columnFilter) => this.adaptableApi.columnFilterApi.isColumnFilterActive(columnFilter));
        const isFilterActive = ArrayExtensions_1.default.IsNotNullOrEmpty(activeFilters);
        const columnsWithActiveFilters = {};
        if (isFilterActive) {
            // used in particular at init time to show the filter icon correctly
            for (const colFilter of activeFilters) {
                // AG Grid exposes the Column interface, but we need the AG Column class
                const agGridCol = this.getAgGridApi().getColumn(colFilter.ColumnId);
                if (agGridCol) {
                    columnsWithActiveFilters[agGridCol.getColId()] = true;
                    if (!agGridCol.isFilterActive()) {
                        // TODO AFL MIG: added source, not sure it's OK
                        agGridCol.setFilterActive(true, 'filterChanged');
                    }
                }
            }
        }
        const agFilterModel = this.getAgGridApi().getFilterModel();
        // AG Grid exposes the Column interface, but we need the AG Column class
        (this.getAgGridApi().getColumns() || []).forEach((col) => {
            /**
             * When AG Grid filters are used active state should not be removed for all columns.
             * Need to check if the column has a filter model.
             */
            const isColumnInFilterModel = agFilterModel && agFilterModel[col.getColId()];
            if (!isColumnInFilterModel &&
                !columnsWithActiveFilters[col.getColId()] &&
                col.isFilterActive()) {
                // TODO AFL MIG: added source, not sure it's OK
                col.setFilterActive(false, 'filterChanged');
            }
        });
    }
    deriveSelectedCellInfoFromAgGrid() {
        const selected = this.getAgGridApi().getCellRanges();
        const columns = [];
        const gridCells = [];
        if (!(selected === null || selected === void 0 ? void 0 : selected.length) || this.adaptableApi.internalApi.isGridInPivotMode()) {
            return { columns: [], gridCells: [] };
        }
        // we iterate for each ranges
        selected.forEach((rangeSelection) => {
            let shouldIncludeRange = true;
            if (rangeSelection.startRow && rangeSelection.endRow) {
                let isStartRowPin = rangeSelection.startRow.rowPinned != null;
                let isEndRowPin = rangeSelection.endRow.rowPinned != null;
                // Warn user if trying to select pinned rows
                // If only selecting pinned rows then stop
                if (isStartRowPin) {
                    if (isEndRowPin) {
                        shouldIncludeRange = false;
                    }
                    this.adaptableInstance.logger.consoleWarn('Cannot select pinned rows in AG Grid.');
                }
                if (shouldIncludeRange) {
                    const y1 = Math.min(rangeSelection.startRow.rowIndex, rangeSelection.endRow.rowIndex);
                    const y2 = Math.max(rangeSelection.startRow.rowIndex, rangeSelection.endRow.rowIndex);
                    for (const column of rangeSelection.columns) {
                        if (column != null) {
                            const colId = column.getColId();
                            const selectedColumn = this.adaptableApi.columnApi.getColumnWithColumnId(colId);
                            if (selectedColumn &&
                                columns.find((c) => c.columnId == selectedColumn.columnId) == null) {
                                columns.push(selectedColumn);
                            }
                            for (let rowIndex = y1; rowIndex <= y2; rowIndex++) {
                                const rowNode = this.getAgGridApi().getDisplayedRowAtIndex(rowIndex);
                                // we used NOT to return grouped rows but I think that was wrong - if someone wants to return them then that is up to them...
                                // we definitely dont return pinned rows as they cannot be selected
                                if (rowNode && !this.isPinnedRowNode(rowNode)) {
                                    const selectedCell = this.adaptableInstance.getGridCellFromRowNode(rowNode, colId);
                                    gridCells.push(selectedCell);
                                }
                            }
                        }
                    }
                }
            }
        });
        return {
            columns,
            gridCells,
        };
    }
    deriveSelectedRowInfoFromAgGrid() {
        const nodes = this.getAgGridApi().getSelectedNodes();
        const selectedRows = [];
        if (this.getAgGridApi().isPivotMode()) {
            //  dont perform row selection in pivot mode
            return undefined;
        }
        if (ArrayExtensions_1.default.IsNotNullOrEmpty(nodes)) {
            nodes.forEach((node) => {
                const rowInfo = {
                    isMaster: !!(node.master != null && node.master == true),
                    isExpanded: !!(node.expanded != null && node.expanded == true),
                    isGroup: !!(node.group != null && node.group == true),
                    isSelected: true,
                    isDisplayed: node.displayed == true,
                    rowGroupLevel: node.level,
                };
                const gridRow = {
                    primaryKeyValue: this.adaptableInstance.getPrimaryKeyValueFromRowNode(node),
                    rowData: node.data,
                    rowNode: node,
                    rowInfo,
                };
                selectedRows.push(gridRow);
            });
        }
        return { gridRows: selectedRows };
    }
    isModulePresent(moduleName) {
        const standardAgGridRegistrationCheck = core_1.ModuleRegistry.__isRegistered(moduleName, this.getAgGridApi().getGridId());
        if (standardAgGridRegistrationCheck) {
            return true;
        }
        return this.getRegisteredModuleNames().some((name) => name === moduleName);
    }
    getRegisteredModuleNames() {
        return this.getRegisteredModules().map((module) => module.moduleName);
    }
    getRegisteredModules() {
        const gridId = this.getAgGridApi().getGridId();
        return core_1.ModuleRegistry.__getRegisteredModules(gridId);
    }
    isPinnedRowNode(rowNode) {
        if (!rowNode) {
            return false;
        }
        if (rowNode.isRowPinned()) {
            return true;
        }
        return false;
    }
    // TODO AFL MIG rename to getFirstGroupedColumnId()
    getFirstGroupedColumn() {
        var _a, _b, _c, _d;
        return (_d = (_c = (_b = (_a = this.getAgGridApi) === null || _a === void 0 ? void 0 : _a.call(this)) === null || _b === void 0 ? void 0 : _b.getRowGroupColumns()) === null || _c === void 0 ? void 0 : _c[0]) === null || _d === void 0 ? void 0 : _d.getColId();
    }
    createAdaptableColumnFromAgGridColumn(agGridColumn, colsToGroups) {
        const colId = agGridColumn.getColId();
        const colDef = agGridColumn.getColDef();
        const ColumnId = colId;
        const pkColumn = this.adaptableInstance.adaptableOptions.primaryKey;
        const ColumnGroup = colsToGroups === null || colsToGroups === void 0 ? void 0 : colsToGroups[ColumnId];
        const isRealColumnGroup = ColumnGroup
            ? ColumnGroup.columnGroupId !== ColumnGroup.friendlyName
            : false;
        const isActionRowButtonColumn = this.adaptableInstance.api.columnApi.isActionRowButtonColumn(colId);
        const isFdc3MainActionColumn = this.adaptableInstance.api.fdc3Api.internalApi.isFdc3MainActionColumn(colId);
        let friendlyName;
        const colExists = this.adaptableInstance.api.columnApi.doesColumnExist(ColumnId);
        if (colExists) {
            friendlyName = this.adaptableInstance.api.columnApi.getFriendlyNameForColumnId(ColumnId);
        }
        else {
            const displayName = this.getAgGridApi().getDisplayNameForColumn(agGridColumn, 'header');
            const columnFriendlyName = this.adaptableInstance.adaptableOptions.columnOptions.columnFriendlyName;
            const customFriendlyName = typeof columnFriendlyName === 'function'
                ? columnFriendlyName({
                    colId: colId,
                    agColumn: agGridColumn,
                    columnGroup: isRealColumnGroup ? ColumnGroup : undefined,
                    displayName: displayName,
                })
                : null;
            friendlyName =
                customFriendlyName !== null && customFriendlyName !== void 0 ? customFriendlyName : (isActionRowButtonColumn
                    ? GeneralConstants_1.ADAPTABLE_ROW_ACTION_BUTTONS_FRIENDLY_NAME
                    : isFdc3MainActionColumn
                        ? GeneralConstants_1.ADAPTABLE_FDC3_ACTION_COLUMN_FRIENDLY_NAME
                        : displayName);
            // Add Column Group;s friendlyname to the Column Friendly Name if its in a legitimate Column Group
            if (this.adaptableInstance.adaptableOptions.columnOptions.addColumnGroupToColumnFriendlyName &&
                colDef.columnGroupShow &&
                ColumnGroup &&
                ColumnGroup.columnGroupId !== ColumnGroup.friendlyName) {
                friendlyName += ' [' + ColumnGroup.friendlyName + ']';
            }
        }
        const dataType = this.getColumnDataType(agGridColumn, false);
        const abColumn = {
            Uuid: (0, Uuid_1.createUuid)(),
            columnId: ColumnId,
            field: colDef.field,
            friendlyName: friendlyName,
            isPrimaryKey: ColumnId === pkColumn,
            dataType: dataType,
            visible: agGridColumn.isVisible(),
            readOnly: this.isColumnReadonly(colDef),
            columnGroup: ColumnGroup,
            fieldOnly: this.isColumnFieldonly(colDef),
            sortable: this.isColumnSortable(colDef),
            filterable: this.isColumnFilterable(colDef),
            groupable: this.isColumnRowGroupable(colDef),
            pivotable: this.isColumnPivotable(colDef),
            aggregatable: this.isColumnAggregetable(colDef),
            availableAggregationFunctions: null,
            aggregationFunction: null,
            moveable: this.isColumnMoveable(colDef),
            hideable: this.isColumnHideable(colDef),
            queryable: this.isColumnQueryable(colDef, ColumnId, friendlyName, dataType),
            exportable: this.isColumnExportable(colDef, ColumnId, friendlyName, dataType),
            isGrouped: this.isColumnRowGrouped(colDef),
            isFixed: this.isColumnFixed(colDef),
            pinned: this.getColumnPinnedPosition(colDef),
            columnTypes: this.getColumnTypes(colDef),
            isExcludedFromQuickSearch: false,
            isSparkline: this.isColumnSparkline(colDef),
        };
        if (abColumn.aggregatable) {
            abColumn.availableAggregationFunctions = this.getColumnAggregationFunctions(colDef);
            if (typeof colDef.aggFunc === 'string') {
                abColumn.aggregationFunction = colDef.aggFunc;
            }
        }
        // lets set this here one as the function cannot change the result so dont need to run it each time
        let excludeColumnFromQuickSearch = this.adaptableInstance.adaptableOptions.quickSearchOptions.excludeColumnFromQuickSearch;
        if (excludeColumnFromQuickSearch) {
            if (excludeColumnFromQuickSearch(abColumn)) {
                abColumn.isExcludedFromQuickSearch = true;
            }
        }
        return abColumn;
    }
    getColumnDataType(column, logWarning = true) {
        // Some columns can have no ID or Title. we return string as a consequence but it needs testing
        if (!column) {
            this.adaptableInstance.logger.warn('column is undefined returning String for Type');
            return 'String';
        }
        let dataType = 'Unknown';
        // get the column type if already in store (and not unknown)
        const existingColumn = this.adaptableInstance.api.columnApi.getColumnWithColumnId(column.getId(), logWarning);
        if (existingColumn && existingColumn.dataType !== 'Unknown') {
            return existingColumn.dataType;
        }
        // check for column type
        const colType = column.getColDef().type;
        if (colType) {
            if (Array.isArray(colType)) {
                colType.forEach((c) => {
                    if (dataType == 'Unknown') {
                        dataType = this.getAbColDefValue(c);
                    }
                });
            }
            else {
                dataType = this.getAbColDefValue(colType);
            }
            if (dataType != 'Unknown') {
                return dataType;
            }
        }
        let row = this.getAgGridApi().getDisplayedRowAtIndex(0);
        if (row == null) {
            // possible that there will be no data.
            this.adaptableInstance.logger.consoleError(`No data in grid, returning type "Unknown" for Column: "${column.getColId()}". This will impact several Adaptable features, such as Filters and ColumnFormats.`);
            return 'Unknown';
        }
        // if it's a group we need the content of the group
        if (row.group) {
            const childNodes = row.childrenAfterGroup;
            if (ArrayExtensions_1.default.IsNullOrEmpty(childNodes)) {
                this.adaptableInstance.logger.consoleError(`No data in grid, returning type "Unknown" for Column: "${column.getColId()}". This will impact several Adaptable features, such as Filters and ColumnFormats.`);
                return 'Unknown';
            }
            row = childNodes[0];
        }
        const value = this._agGridApi_getValue(column, row);
        if (value instanceof Date) {
            dataType = 'Date';
        }
        else if (Array.isArray(value) && value.length && typeof value[0] === 'number') {
            dataType = 'Unknown';
        }
        else {
            switch (typeof value) {
                case 'string':
                    dataType = 'String';
                    break;
                case 'number':
                    dataType = 'Number';
                    break;
                case 'boolean':
                    dataType = 'Boolean';
                    break;
                case 'object':
                    dataType = 'Object';
                    break;
                default:
                    break;
            }
        }
        this.adaptableInstance.logger.consoleWarn(`No defined type for column '${column.getColId()}'. Defaulting to type of first row value: ${dataType}`);
        return dataType;
    }
    getAbColDefValue(colType) {
        if (colType == 'numericColumn') {
            return 'Number';
        }
        if (colType.startsWith('abColDef')) {
            const abColType = colType;
            switch (abColType) {
                case 'abColDefNumber':
                    return 'Number';
                case 'abColDefString':
                    return 'String';
                case 'abColDefBoolean':
                    return 'Boolean';
                case 'abColDefDate':
                    return 'Date';
                case 'abColDefObject':
                    return 'Object';
                case 'abColDefStringArray':
                    return 'StringArray';
                case 'abColDefNumberArray':
                    return 'NumberArray';
                case 'abColDefTupleNumberArray':
                    return 'TupleNumberArray';
                case 'abColDefObjectNumberArray':
                    return 'ObjectNumberArray';
                default:
                    return 'Unknown';
            }
        }
        return 'Unknown';
    }
    isColumnReadonly(colDef) {
        if (!colDef) {
            return true;
        }
        // if the column has conditional/dynamic editability, we assume some rows may be editable
        if (typeof colDef.editable === 'function') {
            return false;
        }
        // otherwise we evaluate the colDef.editable property (columns are NOT editable by default)
        return !colDef.editable;
    }
    isColumnFieldonly(colDef) {
        if (colDef.hide == true && colDef.initialHide == true && colDef.lockVisible == true) {
            return true;
        }
        return false;
    }
    isColumnSortable(colDef) {
        if (colDef && colDef.sortable != null) {
            return colDef.sortable;
        }
        return false;
    }
    isColumnRowGroupable(colDef) {
        if (colDef && colDef.enableRowGroup != null) {
            return colDef.enableRowGroup;
        }
        return false;
    }
    isColumnPivotable(colDef) {
        if (colDef && colDef.enablePivot != null) {
            return colDef.enablePivot;
        }
        return false;
    }
    isColumnAggregetable(colDef) {
        if (colDef && colDef.enableValue != null) {
            return colDef.enableValue;
        }
        return false;
    }
    getColumnAggregationFunctions(colDef) {
        return colDef.allowedAggFuncs || ['sum', 'min', 'max', 'count', 'avg', 'first', 'last']; // those are the default fns aggrid supports out-of-the-box
    }
    isColumnMoveable(colDef) {
        if (!colDef) {
            return false;
        }
        if (colDef.suppressMovable != null && colDef.suppressMovable == true) {
            return false;
        }
        if (this.isColumnFixed(colDef)) {
            return false;
        }
        return true;
    }
    isColumnQueryable(colDef, columnId, friendlyName, datatype) {
        if (!colDef) {
            return false;
        }
        if (colDef.colId === GeneralConstants_1.ADAPTABLE_ROW_ACTION_BUTTONS) {
            return false;
        }
        const abColumnBase = {
            columnId: columnId,
            friendlyName: friendlyName,
            dataType: datatype,
        };
        return this.adaptableInstance.api.expressionApi.isColumnQueryable(abColumnBase);
    }
    isColumnExportable(colDef, columnId, friendlyName, datatype) {
        if (!colDef) {
            return false;
        }
        if (colDef.colId === GeneralConstants_1.ADAPTABLE_ROW_ACTION_BUTTONS) {
            return false;
        }
        const abColumnBase = {
            columnId,
            friendlyName,
            dataType: datatype,
        };
        return this.adaptableInstance.api.exportApi.isColumnExportable(abColumnBase);
    }
    isColumnHideable(colDef) {
        if (!colDef) {
            return false;
        }
        if (colDef.lockVisible != null && colDef.lockVisible == true) {
            return false;
        }
        return true;
    }
    isColumnFilterable(colDef) {
        // follow agGrid logic which is that ONLY filterable if explicitly set
        if (this.adaptableApi.entitlementApi.getEntitlementAccessLevelForModule(ModuleConstants.ColumnFilterModuleId) == 'Hidden') {
            return false;
        }
        return colDef != null && colDef.filter != null && colDef.filter != false;
    }
    getColumnTypes(colDef) {
        if (!colDef.type) {
            return [];
        }
        const allTypes = typeof colDef.type === 'string' ? [colDef.type] : colDef.type;
        return allTypes;
    }
    getColumnPinnedPosition(colDef) {
        return colDef.pinned
            ? colDef.pinned === 'left' || colDef.pinned === true
                ? 'left'
                : 'right'
            : false;
    }
    // used for AG Grid when the column is FixedPinned, meaning it should never be unpinned
    isColumnFixed(colDef) {
        if (!colDef) {
            return false;
        }
        if (colDef.lockPosition != null && colDef.lockPosition == true) {
            return true;
        }
        if (colDef.lockPinned != null && colDef.lockPinned == true) {
            return true;
        }
        return false;
    }
    isColumnRowGrouped(colDef) {
        if (!colDef) {
            return false;
        }
        if (colDef.rowGroup != null && colDef.rowGroup == true) {
            return true;
        }
        if (colDef.rowGroupIndex != null) {
            return true;
        }
        return false;
    }
    isColumnSparkline(colDef) {
        // see https://www.ag-grid.com/javascript-data-grid/sparklines-overview/#enabling-sparklines
        return (colDef === null || colDef === void 0 ? void 0 : colDef.cellRenderer) === 'agSparklineCellRenderer';
    }
    isVisibleNode(rowNode) {
        var _a;
        const foundNode = (_a = this.getAgGridApi()) === null || _a === void 0 ? void 0 : _a.getRenderedNodes().find((n) => n.id == rowNode.id);
        return foundNode != null;
    }
    getFlattenedColDefs(colDefs = []) {
        const flattenedColDefs = [];
        colDefs.forEach((colDef) => {
            if (colDef.children) {
                flattenedColDefs.push(...this.getFlattenedColDefs(colDef.children));
            }
            else {
                flattenedColDefs.push(colDef);
            }
        });
        this.assignColumnIdsToColDefs(flattenedColDefs);
        return flattenedColDefs;
    }
    /**
     * Mutates the colDefs to ensure that each column has a colId
     */
    assignColumnIdsToColDefs(colDefs = []) {
        const assignColId = (colDef) => {
            if (!colDef) {
                return;
            }
            if (colDef.field && !colDef.colId) {
                colDef.colId = colDef.field;
            }
            if (!colDef.colId) {
                this.logger.warn('A column is missing the colId - please check ', colDef, 'Either pass a "field" property or a "colId" property.');
            }
        };
        this.patchColDefs(colDefs, assignColId);
    }
    patchColDefs(colDefs = [], patchFn) {
        const applyPatch = (colDef) => {
            if (!colDef) {
                return;
            }
            if (!colDef.children) {
                patchFn(colDef);
            }
            if (colDef.children) {
                colDef.children.forEach((childColDef) => applyPatch(childColDef));
            }
        };
        colDefs.forEach((colDef) => applyPatch(colDef));
    }
    traverseColDefs(colDefs, modifyFn) {
        const applyModification = (colDef) => {
            if ('children' in colDef) {
                const updatedChildren = colDef.children.map(applyModification).filter(Boolean);
                return Object.assign(Object.assign({}, colDef), { children: updatedChildren });
            }
            else {
                return modifyFn(colDef);
            }
        };
        return colDefs.map(applyModification).filter(Boolean);
    }
    getDefaultColumnDefinition() {
        var _a, _b;
        // for early init phase, gridApi might not be ready yet
        return (_b = (_a = this.getAgGridApi(true)) === null || _a === void 0 ? void 0 : _a.getGridOption('defaultColDef')) !== null && _b !== void 0 ? _b : {};
    }
    _agGridApi_getValue(colKey, rowNode, gridApi) {
        return (0, exports._agGridApi_getValue)(colKey, rowNode, gridApi || this.getAgGridApi());
    }
}
exports.AgGridAdapter = AgGridAdapter;
// FIXME remove this once we support only AG Grid >=30.3.x
const _agGridApi_getValue = (colKey, rowNode, gridApi) => {
    return typeof gridApi.getCellValue === 'function'
        ? gridApi.getCellValue({ colKey, rowNode })
        : gridApi.getValue(colKey, rowNode);
};
exports._agGridApi_getValue = _agGridApi_getValue;
