"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ExpressionEditor = exports.baseClassName = void 0;
const tslib_1 = require("tslib");
const React = tslib_1.__importStar(require("react"));
const react_1 = require("react");
const rebass_1 = require("rebass");
const DocumentationLinkConstants_1 = require("../../Utilities/Constants/DocumentationLinkConstants");
const ModuleConstants_1 = require("../../Utilities/Constants/ModuleConstants");
const ArrayExtensions_1 = tslib_1.__importDefault(require("../../Utilities/Extensions/ArrayExtensions"));
const StringExtensions_1 = tslib_1.__importDefault(require("../../Utilities/Extensions/StringExtensions"));
const AdaptableInput_1 = tslib_1.__importDefault(require("../../View/Components/AdaptableInput"));
const ButtonInfo_1 = require("../../View/Components/Buttons/ButtonInfo");
const CheckBox_1 = require("../CheckBox");
const CodeBlock_1 = require("../CodeBlock");
const HelpBlock_1 = tslib_1.__importDefault(require("../HelpBlock"));
const Panel_1 = tslib_1.__importDefault(require("../Panel"));
const Tabs_1 = require("../Tabs");
const DataTableEditor_1 = require("./DataTableEditor");
const EditorContext_1 = require("./EditorContext");
const EditorInput_1 = tslib_1.__importDefault(require("./EditorInput"));
const EditorInputWithWhereClause_1 = tslib_1.__importDefault(require("./EditorInputWithWhereClause"));
const ExpressionFunctionDocumentation_1 = require("./ExpressionFunctionDocumentation");
const NamedQueryContext_1 = require("./NamedQueryContext");
const NamedQueryEditor_1 = require("./NamedQueryEditor");
const QueryBuilder_1 = require("./QueryBuilder");
exports.baseClassName = `ab-ExpressionEditor`;
function ExpressionEditor(props) {
    const { type, module } = props;
    const [selectedFunction, setSelectedFunction] = (0, react_1.useState)(null);
    const [expressionText, setExpressionText] = (0, react_1.useState)(props.value);
    const { namedQuery, setNamedQuery } = (0, NamedQueryContext_1.useNamedQueryContext)();
    const textAreaRef = (0, react_1.useRef)(null);
    const allowSaveNamedQuery = props.allowSaveNamedQuery ?? type === 'boolean';
    const editorInput = type === 'observable' || type === 'aggregatedBoolean' ? (React.createElement(EditorInputWithWhereClause_1.default, { type: type, module: module, value: props.value, onChange: (value) => {
            setExpressionText(value);
            props.onChange(value);
        }, testData: props.initialData, api: props.api })) : (
    // 'boolean','scalar','aggregatedScalar'/'cumulativeAggregatedScalar'/'quantileAggregatedScalar'
    (React.createElement(
        EditorInput_1.default,
        { type: type, module: module, value: props.value, onChange: (value) => {
                setExpressionText(value);
                props.onChange(value);
            }, testData: props.initialData, isFullExpression: props.isFullExpression, api: props.api }
    )));
    const adaptableFields = props.api.expressionApi.internalApi.getAvailableFields();
    // currently only boolean and scalar expressions support nested calculated columns (calc cols which reference other calc cols)
    const queryableColumns = type === 'scalar' || type === 'boolean' || type === 'aggregatedScalar'
        ? props.columns
        : props.columns.filter((c) => !c.isCalculatedColumn);
    const showDocumentationLinks = props.api.internalApi.isDocumentationLinksDisplayed();
    // @Bogdan i did this to be sure but i think we can get rid of this as the button does the same (and better IMO)
    const showNamedQueryStuff = false;
    const queryDocumentationLink = queryDocumentationLinks[type];
    const saveAsNamedQueryElement = allowSaveNamedQuery &&
        props.api.internalApi
            .getModuleService()
            .getModuleById(ModuleConstants_1.NamedQueryModuleId)
            .isModuleEditable() && (React.createElement(rebass_1.Flex, { flexDirection: "row", mb: 2, fontSize: 3 },
        React.createElement(CheckBox_1.CheckBox, { checked: !!namedQuery, onChange: (checked) => {
                if (checked) {
                    setNamedQuery({
                        Name: '',
                        BooleanExpression: expressionText,
                    });
                }
                else {
                    setNamedQuery(false);
                }
            }, mr: 2, style: { alignItems: 'center' } }, "Save as new Named Query"),
        namedQuery ? (React.createElement(AdaptableInput_1.default, { style: { flex: 1 }, onChange: (event) => {
                const { target: { value }, } = event;
                setNamedQuery({
                    Name: value,
                    BooleanExpression: expressionText,
                });
            } })) : null));
    const editorElement = (React.createElement(rebass_1.Flex, { className: exports.baseClassName, flex: 1, "data-name": "expression-editor-wrapper", pl: 2, style: props.style },
        React.createElement(rebass_1.Flex, { flex: 1, style: { minHeight: 0 }, flexDirection: "column", onFocus: (event) => {
                if (event.target.tagName === 'TEXTAREA') {
                    textAreaRef.current = event.target;
                }
            } },
            React.createElement(rebass_1.Flex, { flex: 1, flexDirection: "row", "data-name": "expression-editor", fontSize: 2, style: { minHeight: 0 } },
                React.createElement(rebass_1.Flex, { flex: 1, paddingRight: 2, style: { overflow: 'auto' }, "data-name": "expression-builder", flexDirection: "column" },
                    editorInput,
                    StringExtensions_1.default.IsNotNullOrEmpty(expressionText?.trim()) && (React.createElement(ExpressionFunctionDocumentation_1.ExpressionFunctionDocumentation, { expressionFunction: selectedFunction })),
                    /* displayed for advanced queries (observable&Aggregation) to give the users a starting point */
                    StringExtensions_1.default.IsNullOrEmpty(expressionText?.trim()) && renderQueryHints(type),
                    showDocumentationLinks && (React.createElement(HelpBlock_1.default, { "data-name": "query-documentation", mt: 2, mb: 2, fontSize: 3 },
                        React.createElement(ButtonInfo_1.ButtonInfo, { mr: 2, onClick: () => window.open(queryDocumentationLink, '_blank') }),
                        "See documentation for more details and examples")),
                    React.createElement(rebass_1.Box, { flex: 1 }),
                    showNamedQueryStuff && saveAsNamedQueryElement),
                React.createElement(rebass_1.Box, { className: `${exports.baseClassName}__sidebar`, "data-name": "expression-sidebar", pb: 2, paddingLeft: 2, pr: 2 },
                    React.createElement(Panel_1.default, { bodyProps: { style: { height: '100%' } }, style: { height: '100%' } },
                        React.createElement(Tabs_1.Tabs, { style: { height: '100%' }, variant: "" },
                            React.createElement(Tabs_1.Tabs.Tab, { value: "column" }, "Columns"),
                            React.createElement(Tabs_1.Tabs.Tab, { value: "field" }, "Fields"),
                            React.createElement(Tabs_1.Tabs.Tab, { value: "named-query" }, "Named Queries"),
                            React.createElement(Tabs_1.Tabs.Content, { flex: 1, style: { height: '100%' } },
                                React.createElement(DataTableEditor_1.DataTableEditor, { type: "column", dataFormatter: (value) => `[${value}]`, fields: queryableColumns.map((column) => ({
                                        label: column.friendlyName,
                                        value: column.columnId,
                                        dataType: column.dataType,
                                        readOnly: column.readOnly,
                                    })), data: props.initialData })),
                            React.createElement(Tabs_1.Tabs.Content, null, adaptableFields && adaptableFields.length > 0 ? (React.createElement(DataTableEditor_1.DataTableEditor, { type: "field", labels: {
                                    showIds: 'Show Field path',
                                    filterPlaceholder: 'Filter fields...',
                                }, dataFormatter: (value) => `FIELD("${value}")`, data: props.initialData, fields: adaptableFields?.map((field) => ({
                                    label: field.label,
                                    value: field.name,
                                    dataType: field.dataType,
                                    readOnly: true,
                                })) })) : (React.createElement(rebass_1.Text, { p: 2 }, "No Data Fields provided"))),
                            React.createElement(Tabs_1.Tabs.Content, null, ArrayExtensions_1.default.IsNullOrEmpty(props.namedQueries) ? (React.createElement("div", null,
                                React.createElement(rebass_1.Text, { p: 2 }, "No Named Queries defined"),
                                React.createElement(rebass_1.Text, { fontSize: 2, padding: 2, p: 2 },
                                    "Named Queries are saved Expressions which can be referenced in other Expressions using the ",
                                    React.createElement("i", null, "QUERY"),
                                    " keyword",
                                    ' '))) : (React.createElement(NamedQueryEditor_1.NamedQueryEditor, { namedQueries: props.namedQueries }))))))))));
    const queryBuilderElement = (React.createElement(QueryBuilder_1.QueryBuilder, { module: module, query: expressionText, onChange: (query) => {
            setExpressionText(query);
            props.onChange(query);
        }, getFields: (type) => {
            return props.fields
                .filter((field) => {
                if (!type) {
                    return (field.dataType === 'text' ||
                        field.dataType === 'number' ||
                        field.dataType === 'boolean' ||
                        field.dataType === 'date');
                }
                else {
                    return field.dataType === type;
                }
            })
                .map((field) => ({
                value: field.name,
                label: field.label,
                type: field.dataType,
            }));
        }, getColumns: (type) => {
            return props.columns
                .filter((column) => {
                if (!type) {
                    return (column.dataType === 'text' ||
                        column.dataType === 'number' ||
                        column.dataType === 'boolean' ||
                        column.dataType === 'date');
                }
                else {
                    return column.dataType === type;
                }
            })
                .map((col) => ({
                value: col.columnId,
                label: col.friendlyName ?? col.columnId,
                type: col.dataType,
            }));
        } }));
    const showQueryBuilder = props.showQueryBuilder ?? false;
    const showExpressionEditor = props.showExpressionEditor ?? true;
    return (React.createElement(EditorContext_1.ExpressionEditorContext.Provider, { value: {
            selectedFunction,
            setSelectedFunction,
            textAreaRef,
        } }, (() => {
        switch (true) {
            case showQueryBuilder && showExpressionEditor:
                return (React.createElement(Tabs_1.Tabs, { "data-name": "editor-selector-tabs", mb: 2, p: 2, minHeight: 0, flex: 1 },
                    React.createElement(Tabs_1.Tabs.Tab, { value: "editor" }, "Expression Editor"),
                    React.createElement(Tabs_1.Tabs.Tab, { value: "ui" }, "Query Builder"),
                    React.createElement(Tabs_1.Tabs.Content, null, editorElement),
                    React.createElement(Tabs_1.Tabs.Content, null,
                        React.createElement(rebass_1.Flex, { flexDirection: "column", height: "100%" },
                            queryBuilderElement,
                            React.createElement(rebass_1.Box, { flex: 1 }),
                            showNamedQueryStuff && saveAsNamedQueryElement))));
            case showQueryBuilder:
                return (React.createElement(rebass_1.Flex, { flexDirection: "column", p: 2, height: "100%" }, queryBuilderElement));
            case showExpressionEditor:
                return (React.createElement(rebass_1.Flex, { flexDirection: "column", p: 2, height: "100%" }, editorElement));
            default:
                return null;
        }
    })()));
}
exports.ExpressionEditor = ExpressionEditor;
const renderQueryHints = (type) => {
    const examples = [];
    if (type === 'observable') {
        examples.push({
            code: "ROW_CHANGE( COUNT([ItemCount],3), TIMEFRAME('5m'))",
            description: 'The Item Count value in a specific Row changes 3 times within a 5 minute timeframe',
        });
        examples.push({
            code: "GRID_CHANGE( MAX([OrderCost]), TIMEFRAME('1h')) WHERE [CustomerReference] = 'TRADH'",
            description: "An Order Cost cell contains its highest value in the whole Grid within the last hour - for rows where Cust Ref is 'TRADH'",
        });
    }
    if (type === 'aggregatedBoolean') {
        examples.push({
            code: 'SUM([PnL]) > 50000000',
            description: "The sum of the 'PnL' column values in all rows is greater than 5 Million",
        });
        examples.push({
            code: "SUM([PnL]) > '5B' WHERE QUERY('CurrencyDollar')",
            description: "The sum of the 'PnL' column values in all rows where named query 'CurrencyDollar' is evaluated to TRUE is greater than 5 Billions",
        });
    }
    if (type === 'aggregatedScalar') {
        examples.push({
            code: 'AVG([stargazers_count], GROUP_BY([language]))',
            description: 'Average popularity (number of stars) of all the Github repositories, grouped by programming language',
        }, {
            code: 'MAX([PnL]), GROUP_BY([currency], [country])',
            description: "The maximum 'PnL' value, grouped by currency and country",
        });
    }
    if (type === 'cumulativeAggregatedScalar') {
        examples.push({
            code: 'CUMUL(AVG([stargazers_count], GROUP_BY([language])), OVER([TradeDate]))',
            description: "The cumulative average popularity (number of stars) of all the Github repositories, grouped by programming language in the order given by the 'TradeDate' column",
        }, {
            code: 'CUMUL( SUM([PnL]), OVER([TradeDate]))',
            description: "The cumulative sum of all 'PnL' columns in the order given by the 'TradeDate' column",
        });
    }
    return examples.length ? (React.createElement(rebass_1.Box, { "data-name": "expression-hints", my: 2, p: 2, style: {
            background: 'var(--ab-color-primary)',
            borderRadius: 'var(--ab__border-radius)',
        } },
        React.createElement(rebass_1.Box, null,
            React.createElement(rebass_1.Text, { marginBottom: 2 },
                React.createElement("b", null, "Examples"),
                " (click on each to see its explanation):"),
            examples.map((example, index) => (React.createElement("details", { key: index, style: { marginBottom: 'var(--ab-space-2)' } },
                React.createElement(rebass_1.Flex, { style: {
                        borderRadius: 'var(--ab__border-radius)',
                    }, marginRight: 2, marginBottom: 1, color: 'text-on-secondary', fontSize: 'var( --ab-font-size-2)', alignItems: "center", as: "summary" },
                    React.createElement(CodeBlock_1.CodeBlock, null,
                        " ",
                        example.code)),
                React.createElement(rebass_1.Text, { fontSize: 2, marginLeft: 3, style: { fontStyle: 'italic' } }, example.description))))))) : null;
};
const queryDocumentationLinks = {
    boolean: DocumentationLinkConstants_1.BooleanQueryDocsLink,
    scalar: DocumentationLinkConstants_1.ScalarQueryDocsLink,
    observable: DocumentationLinkConstants_1.ObservableQueryDocsLink,
    aggregatedBoolean: DocumentationLinkConstants_1.AggregatedBooleanQueryDocsLink,
    aggregatedScalar: DocumentationLinkConstants_1.AggregatedScalarQueryDocsLink,
    cumulativeAggregatedScalar: DocumentationLinkConstants_1.CumulativeAggregatedScalarQueryDocsLink,
    quantileAggregatedScalar: DocumentationLinkConstants_1.QuantileAggregatedScalarQueryDocsLink,
};
