"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.QueryPredicateBuilder = void 0;
const tslib_1 = require("tslib");
const react_1 = tslib_1.__importDefault(require("react"));
const dnd_1 = require("../../dnd");
const rebass_1 = require("rebass");
const predicate_1 = require("../../../parser/src/predicate");
const adaptableQlUtils_1 = require("../../../Utilities/adaptableQlUtils");
const booleanExpressionFunctions_1 = require("../../../Utilities/ExpressionFunctions/booleanExpressionFunctions");
const AdaptableContext_1 = require("../../../View/AdaptableContext");
const DropdownButton_1 = tslib_1.__importDefault(require("../../DropdownButton"));
const ErrorBox_1 = tslib_1.__importDefault(require("../../ErrorBox"));
const icons_1 = require("../../icons");
const SimpleButton_1 = tslib_1.__importDefault(require("../../SimpleButton"));
const QueryBuilderInputs_1 = require("./QueryBuilderInputs");
const utils_1 = require("./utils");
const ITEM_HEIGHT = 40;
const BASE_CLASS_NAME = 'ab-QueryBuilder-predicate-editor';
const Handle = (props) => (react_1.default.createElement(rebass_1.Flex, { className: `${BASE_CLASS_NAME}__handle`, height: ITEM_HEIGHT, alignItems: "center", mr: 1, ...props },
    react_1.default.createElement(icons_1.Icon, { name: "drag" })));
const QueryPredicateButtons = (props) => {
    return (react_1.default.createElement(react_1.default.Fragment, null,
        !props.hideAdd && (react_1.default.createElement(DropdownButton_1.default, { listMinWidth: 150, columns: ['label'], items: [
                { label: 'Condition', onClick: () => props.onNewPredicate('filter') },
                { label: 'AND / OR Group', onClick: () => props.onNewPredicate('group') },
            ], variant: "text" },
            react_1.default.createElement(icons_1.Icon, { name: "plus" }))),
        !props.hideDelete && (react_1.default.createElement(SimpleButton_1.default, { icon: "delete", variant: "text", onClick: () => {
                props.onChange(null);
            } }))));
};
const LogicalFunctionEditor = (props) => {
    const level = props.id.split('/').length - 1;
    const className = `
    ${BASE_CLASS_NAME}
    ${BASE_CLASS_NAME}-level-${level}
    ${BASE_CLASS_NAME}-combinator
    ${props.lastChild ? `${BASE_CLASS_NAME}--last-child` : ''}
    ${props.isRoot ? `${BASE_CLASS_NAME}--root` : `${BASE_CLASS_NAME}--child`}
  `;
    const getCombinatorEl = (handleProps, className) => (react_1.default.createElement(dnd_1.Droppable, { droppableId: props.id, type: props.id }, (provided, snapshot) => {
        return (react_1.default.createElement("div", { ...provided.droppableProps, ref: provided.innerRef, className: className },
            react_1.default.createElement(rebass_1.Flex, null,
                props.isRoot ? null : react_1.default.createElement(Handle, { ...handleProps }),
                react_1.default.createElement(rebass_1.Flex, { flex: 1, alignItems: "center", height: ITEM_HEIGHT },
                    react_1.default.createElement(QueryBuilderInputs_1.CombinatorSelector, { value: props.predicate.operator, onChange: (combinator) => {
                            props.onChange({
                                ...props.predicate,
                                operator: combinator,
                            });
                        } }),
                    react_1.default.createElement(rebass_1.Box, { flex: 1 }),
                    react_1.default.createElement(QueryPredicateButtons, { hideDelete: props.isRoot, hideAdd: true, ...props }))),
            react_1.default.createElement(rebass_1.Box, { className: `${BASE_CLASS_NAME}__children-wrapper` },
                props.predicate.args.map((arg, index) => {
                    const id = `${props.id}/${index}`;
                    return (react_1.default.createElement(exports.QueryPredicateBuilder, { key: id, lastChild: index === props.predicate.args.length - 1, index: index, id: id, predicate: arg, onNewPredicate: (type) => {
                            const newPredicate = {
                                operator: type === 'filter' ? undefined : 'AND',
                                args: [],
                            };
                            if (typeof arg === 'object' &&
                                'operator' in arg &&
                                (0, predicate_1.isQlLogicalOperator)(arg.operator)) {
                                // add as a child
                                const newArg = {
                                    ...arg,
                                    args: [...arg.args, newPredicate],
                                };
                                const args = [...props.predicate.args];
                                args[index] = newArg;
                                props.onChange({
                                    ...props.predicate,
                                    args,
                                });
                            }
                            else {
                                // add as a sibling
                                const prevArgs = [...props.predicate.args];
                                prevArgs.splice(index + 1, 0, newPredicate);
                                props.onChange({
                                    ...props.predicate,
                                    args: prevArgs,
                                });
                            }
                        }, onChange: (predicate) => {
                            const args = [...props.predicate.args];
                            if (predicate) {
                                args[index] = predicate;
                            }
                            else {
                                args.splice(index, 1);
                            }
                            props.onChange({
                                ...props.predicate,
                                args,
                            });
                        } }));
                }),
                provided.placeholder,
                react_1.default.createElement("div", { className: `${BASE_CLASS_NAME}__root-actions` },
                    react_1.default.createElement(QueryPredicateButtons, { ...props, hideDelete: true })))));
    }));
    if (props.isRoot) {
        return getCombinatorEl({ className: className });
    }
    else {
        return (react_1.default.createElement(dnd_1.Draggable, { key: props.id, draggableId: props.id, index: props.index }, (provided, snapshot) => {
            return (react_1.default.createElement("div", { ...provided.draggableProps, ref: provided.innerRef, className: className }, getCombinatorEl(provided.dragHandleProps)));
        }));
    }
};
const PrimitiveFunctionEditor = (props) => {
    // [handle] [column] [operator-dropdown] [...args] [delete-button] [plus-button]
    const adaptable = (0, AdaptableContext_1.useAdaptable)();
    const [columnOrFieldExpression, ...restOfArgs] = props.predicate.args;
    const columnOrField = columnOrFieldExpression;
    let columnOrFieldId = null;
    let columnOrFieldDataType = null;
    let columnInputDataType = null;
    let functionInputInputDataTypes = null;
    // Thsese are the type of inputs ommiting the column
    // [[column-data-type], number, number]
    let restOfFunctionInputDataTypes = [];
    if (columnOrField) {
        if (!(0, predicate_1.isArgumentColumnOrField)(columnOrField)) {
            return react_1.default.createElement(ErrorBox_1.default, null, "Expression must start with a column or a filed!");
        }
        if (columnOrField.includes('FIELD')) {
            // we let the full expression so we can difirienciate between column and field
            columnOrFieldId = columnOrField;
            const fieldValue = (0, utils_1.mapExpressionToFieldValue)(columnOrField);
            columnOrFieldDataType = adaptable.api.expressionApi.internalApi.getFieldType(fieldValue);
        }
        else if (columnOrField.includes('[')) {
            columnOrFieldId = columnOrField;
            const columnId = columnOrField.replace(/[\[\]]/g, '');
            columnOrFieldDataType = adaptable.api.columnApi.getColumnDataTypeForColumnId(columnId);
        }
        columnInputDataType = (0, adaptableQlUtils_1.mapColumnDataTypeToExpressionFunctionType)(columnOrFieldDataType);
        functionInputInputDataTypes = booleanExpressionFunctions_1.booleanExpressionFunctions[props.predicate.operator]?.inputs;
        restOfFunctionInputDataTypes = functionInputInputDataTypes
            ? (0, utils_1.getOperatorMatchingInputs)(columnInputDataType, functionInputInputDataTypes)
            : [];
    }
    const level = props.id.split('/').length - 1;
    return (react_1.default.createElement(dnd_1.Draggable, { key: props.id, draggableId: props.id, index: props.index }, (provided) => {
        return (react_1.default.createElement(rebass_1.Flex, { className: `
              ${BASE_CLASS_NAME}
              ${BASE_CLASS_NAME}-level-${level}
              ${BASE_CLASS_NAME}-primitive
              ${props.lastChild ? `${BASE_CLASS_NAME}--last-child` : ''}
            `, pb: 2, ref: provided.innerRef, ...provided.draggableProps, style: { ...provided.draggableProps.style, minHeight: ITEM_HEIGHT } },
            react_1.default.createElement(Handle, { ...provided.dragHandleProps }),
            react_1.default.createElement(rebass_1.Flex, { alignItems: "center", height: ITEM_HEIGHT },
                react_1.default.createElement(rebass_1.Box, { mr: 2 },
                    react_1.default.createElement(QueryBuilderInputs_1.PrimitiveColumnOrFieldSelector, { onChange: (colOrField) => {
                            props.onChange({
                                ...props.predicate,
                                args: [colOrField],
                                operator: null,
                            });
                        }, fieldOrColumn: columnOrFieldId })),
                columnOrFieldId && columnOrFieldDataType && (react_1.default.createElement(QueryBuilderInputs_1.ExpressionSelector, { dataType: columnOrFieldDataType, onExpressionChange: (operator) => {
                        let args = [props.predicate.args[0]];
                        if (columnOrFieldDataType === 'boolean' && operator !== 'NOT') {
                            args = [props.predicate.args[0], 'TRUE'];
                        }
                        props.onChange({
                            ...props.predicate,
                            operator,
                            // discard arguments
                            args,
                        });
                    }, value: props.predicate.operator })),
                react_1.default.createElement(rebass_1.Flex, { flex: 1, ml: 2 }, restOfFunctionInputDataTypes.map((type, index) => {
                    const key = type + index;
                    const commonProps = {
                        lefthandColumnIdParam: columnOrFieldId,
                        inputType: type,
                    };
                    if (type.includes('[]')) {
                        return (react_1.default.createElement(QueryBuilderInputs_1.PrimitiveMultiValueInput, { ...commonProps, key: key, value: restOfArgs, onChange: (values) => {
                                const args = [...props.predicate.args.slice(0, 1), ...values];
                                props.onChange({
                                    ...props.predicate,
                                    args,
                                });
                            } }));
                    }
                    return (react_1.default.createElement(QueryBuilderInputs_1.PrimitiveValueInput, { ...commonProps, key: key, value: restOfArgs[index] ?? null, onChange: (value) => {
                            const args = [...props.predicate.args];
                            // +1 because col is the first argument
                            args[index + 1] = value;
                            props.onChange({
                                ...props.predicate,
                                args,
                            });
                        } }));
                }))),
            react_1.default.createElement(rebass_1.Box, { flex: 1 }),
            react_1.default.createElement(QueryPredicateButtons, { ...props })));
    }));
};
/**
 * Two types:
 * - combinatory operator
 * - can contain both expressons and combinators
 * [handle] [combinator-dropdown]
 *   [children]
 *
 * - boolean function: ars do not contain other combinators
 * [handle] [column] [operator-dropdown] [...args] [delete-button] [plus-button]
 */
const QueryPredicateBuilder = (props) => {
    if ((0, predicate_1.isQlLogicalOperator)(props.predicate.operator)) {
        return react_1.default.createElement(LogicalFunctionEditor, { ...props });
    }
    else {
        return react_1.default.createElement(PrimitiveFunctionEditor, { ...props });
    }
};
exports.QueryPredicateBuilder = QueryPredicateBuilder;
