import { ExpressionValueType } from "./ExpressionValueType";
import { DataUtilities } from "../Common/DataUtilities";
import { StateManager } from "../CommonUI/StateManagement/StateManager";
import { ValidationIssue } from "../Common/IDesignValidationProvider";
import { NumberDataDefinition } from "../Common/Data/Definitions/NumberDataDefinition";
import { XDataType } from "../Common/Data/XDataType";
import { GetExpressionFromData } from "./Utilities/RegisterExpression";
import { ConfigStorageUtility } from "../Common/ConfigStorageUtility";
import { ExpressionValueTestResult } from "./Testing/ExpressionTestResult";
import { EventLog, EventLogType } from "../Common/Event/EventLog";
import { ExpressionConstants } from "./ExpressionContants";
export class ExpressionValue {
    constructor() {
        this.canSelect = true;
        this._id = DataUtilities.generateUniqueId();
        this.expectedTypeValue = new NumberDataDefinition();
        this.valueTypeValue = ExpressionValueType.UNSET;
    }
    static createExpressionValue(owner, name, expectedType = new NumberDataDefinition()) {
        let v = new ExpressionValue();
        v.name = name;
        v.expectedTypeValue = expectedType;
        owner.registerExpressionValue(v);
        return v;
    }
    setEditContext(context, owner) {
        this.editContext = context;
        this.editContext.register(this, owner);
        if (this.subExpression) {
            this.subExpression.setEditContext(context, this);
        }
    }
    get variableName() {
        return this.variableNameValue;
    }
    set variableName(value) {
        this.variableNameValue = value;
        this.valueType = ExpressionValueType.VARIABLE;
        if (this.editContext)
            this.editContext.refresh();
    }
    get value() {
        return this.expressionValue;
    }
    set value(value) {
        this.expressionValue = value;
        this.valueType = ExpressionValueType.VALUE;
        if (this.editContext)
            this.editContext.refresh();
    }
    get subExpression() {
        return this.subExpressionValue;
    }
    set subExpression(value) {
        this.subExpressionValue = value;
        if (!value) {
            this.valueType = ExpressionValueType.UNSET;
        }
        else {
            if (value.__type === ExpressionConstants.IfThenExpressionType) {
                value.expressionValueType = this.expectedType;
            }
            this.valueType = ExpressionValueType.SUBEXPRESSION;
            // this.expectedTypeValue = value.expressionValueType;
            if (this.editContext) {
                value.setEditContext(this.editContext, this);
            }
        }
        if (this.editContext)
            this.editContext.refresh();
    }
    clear() {
        this.valueType = ExpressionValueType.UNSET;
    }
    get valueType() {
        return this.valueTypeValue;
    }
    set valueType(value) {
        this.valueTypeValue = value;
        StateManager.propertyChanged(this, "valueTypeValue");
        if (this.editContext)
            this.editContext.refresh();
    }
    get expectedType() {
        return this.expectedTypeValue;
    }
    set expectedType(value) {
        this.expectedTypeValue = value;
        if (this.editContext)
            this.editContext.refresh();
    }
    static isExpressionValue(item) {
        return item instanceof ExpressionValue;
    }
    getValidationIssues() {
        // check type
        // check to see if unset
        var _a, _b, _c, _d, _e, _f, _g;
        let issues = [];
        if (this.valueTypeValue === ExpressionValueType.UNSET)
            issues.push(new ValidationIssue("Expression Value is Required", undefined, this._id));
        if (this.valueTypeValue === ExpressionValueType.VARIABLE) {
            if (!this.variableName)
                issues.push(new ValidationIssue("Data Name is Required", undefined, this._id));
            else {
                try {
                    const field = ((_c = (_b = (_a = this.editContext) === null || _a === void 0 ? void 0 : _a.definition) === null || _b === void 0 ? void 0 : _b.requiredVariable) === null || _c === void 0 ? void 0 : _c.name) === this.variableName
                        ? (_e = (_d = this.editContext) === null || _d === void 0 ? void 0 : _d.definition) === null || _e === void 0 ? void 0 : _e.requiredVariable : DataUtilities.getFieldFromDataDefinitionByProperty(((_g = (_f = this.editContext) === null || _f === void 0 ? void 0 : _f.definition) === null || _g === void 0 ? void 0 : _g.getInputDefinition().fields) || [], this.variableName, "name");
                    if (!field) {
                        issues.push(new ValidationIssue("Invalid Data Name.", undefined, this._id));
                    }
                    else if (field) {
                        if (field.type !== this.expectedTypeValue.type || field.list !== this.expectedTypeValue.list) {
                            issues.push(new ValidationIssue(`${this.expectedTypeValue.type}${this.expectedTypeValue.list ? " list" : ""} field should be selected.`));
                        }
                    }
                }
                catch (e) {
                    issues.push(new ValidationIssue("Invalid Data Name.", undefined, this._id));
                }
            }
            return issues;
        }
        if (this.valueTypeValue === ExpressionValueType.VALUE) {
            if (this.value === null || this.value === undefined)
                issues.push(new ValidationIssue("Value is Required", undefined, this._id));
            else {
                let issue = this.expectedType.verifyValueType(this.value);
                if (issue) {
                    issues.push(issue);
                }
            }
        }
        if (this.valueTypeValue === ExpressionValueType.SUBEXPRESSION) {
            if (!this.subExpression)
                issues.push(new ValidationIssue("Expression is Required", undefined, this._id));
            else {
                if (this.subExpression.expressionValueType.type !== this.expectedTypeValue.type ||
                    this.subExpression.expressionValueType.list !== this.expectedTypeValue.list) {
                    issues = [
                        ...issues,
                        new ValidationIssue(`${this.expectedTypeValue.type} expression should be used.`),
                    ];
                }
            }
        }
        return issues;
    }
    evaluate(data, testContext, undefinedAllowed = false) {
        let logs = [];
        let val;
        switch (this.valueTypeValue) {
            case ExpressionValueType.VARIABLE:
                val = DataUtilities.get(data, this.variableName);
                logs.push(new EventLog(`Expression Variable: ${this.variableName} = ${val}`));
                break;
            case ExpressionValueType.VALUE:
                val = this.value;
                logs.push(new EventLog(`Expression Constant Value: ${val}`));
                break;
            case ExpressionValueType.SUBEXPRESSION:
                try {
                    logs.push(new EventLog(`Expression Evaluate ${this.subExpression.__type}`));
                    val = this.subExpression.evaluate(data, testContext);
                    if (val === undefined) {
                        logs.push(new EventLog(`Expression ${this.subExpression.__type} Error:`, EventLogType.ERROR));
                    }
                    else {
                        logs.push(new EventLog(`Expression ${this.subExpression.__type} Result: ${val}`));
                    }
                }
                catch (e) {
                    if (testContext) {
                        logs.push(new EventLog(`Expression ${this.subExpression.__type} Error: ${e.message}`, EventLogType.ERROR));
                        testContext.addLogItems(logs);
                        let testresult = new ExpressionValueTestResult(this._id, e.message);
                        testresult.isError = true;
                        testContext.addTestResultItem(testresult);
                    }
                    throw e;
                }
                break;
            default:
                val = undefined;
        }
        // check if undefined is allowed
        if (val === undefined && !undefinedAllowed) {
            if (testContext) {
                if (this.valueTypeValue === ExpressionValueType.SUBEXPRESSION ||
                    this.valueTypeValue === ExpressionValueType.VARIABLE) {
                    let testresult = new ExpressionValueTestResult(this._id, this.valueTypeValue === ExpressionValueType.SUBEXPRESSION
                        ? `Expression Value: "${this.name}" is not defined`
                        : `Expression Value: "${this.variableName}" is missing`);
                    testresult.isError = true;
                    testContext.addTestResultItem(testresult);
                    logs.push(new EventLog(testresult.result, EventLogType.ERROR));
                    testContext.addLogItems(logs);
                    throw new Error(testresult.result);
                }
                throw new Error("Expression Value: Contant Value is not defined");
            }
            else {
                throw new Error(`Expression Value: ${this.name} is not defined`);
            }
        }
        // do conversion
        val = this.expectedType.convert(val);
        // check type of return
        let valIssue = DataUtilities.compareValueToDataDefinition(val, this.expectedType);
        if (valIssue) {
            if (testContext) {
                let testresult = new ExpressionValueTestResult(this._id, `Expression Value: ${this.variableName || this.name} value returned is not of expected type. ${valIssue.issue}`);
                testresult.isError = true;
                testContext.addTestResultItem(testresult);
                logs.push(new EventLog(`Expression Value: ${this.name} value returned is not of expected type. ${valIssue.issue}`, EventLogType.ERROR));
                testContext.addLogItems(logs);
                if (this.valueTypeValue === ExpressionValueType.VARIABLE) {
                    throw new Error(`Expression Value: "${this.variableName}" value returned is not of expected type. ${valIssue.issue}`);
                }
                throw new Error(`Expression Value: ${this.variableName || this.name} value returned is not of expected type. ${valIssue.issue}`);
            }
            else {
                throw new Error(`Expression Value: ${this.variableName || this.name} value returned is not of expected type. ${valIssue.issue}`);
            }
        }
        if (testContext) {
            // publish out value as result.
            testContext.addTestResultItem(new ExpressionValueTestResult(this._id, val.toString()));
            testContext.addLogItems(logs);
        }
        return val;
    }
    getAllVariables() {
        var _a;
        let output = [];
        if (this.valueTypeValue === ExpressionValueType.VARIABLE && this.variableName) {
            output.push(this.variableName);
        }
        else if (this.valueTypeValue === ExpressionValueType.SUBEXPRESSION && this.subExpression) {
            output.push(...(_a = this.subExpression) === null || _a === void 0 ? void 0 : _a.getAllVariables());
        }
        return output;
    }
    getStorageData() {
        var _a;
        let data = {};
        Reflect.set(data, "_id", this._id);
        Reflect.set(data, "name", this.name);
        Reflect.set(data, "expectedTypeValue", this.expectedTypeValue.getStorageData());
        Reflect.set(data, "valueType", this.valueTypeValue);
        let exp = (_a = this.subExpressionValue) === null || _a === void 0 ? void 0 : _a.getStorageData();
        if (exp) {
            ConfigStorageUtility.verifyTypeField(exp);
            Reflect.set(data, "subExpressionValue", exp);
        }
        Reflect.set(data, "expressionValue", this.expressionValue);
        Reflect.set(data, "variableName", this.variableNameValue);
        return data;
    }
    setStorageData(data) {
        this._id = Reflect.get(data, "_id");
        this.name = Reflect.get(data, "name");
        let dd = ConfigStorageUtility.getDataDefinitionFromData(Reflect.get(data, "expectedTypeValue"));
        if (dd) {
            this.expectedTypeValue = dd;
        }
        else {
            throw new Error("no data definition defined");
        }
        // todo: create the right type
        this.valueTypeValue = Reflect.get(data, "valueType");
        this.subExpressionValue = GetExpressionFromData(Reflect.get(data, "subExpressionValue"));
        // todo: create the right type
        this.expressionValue = Reflect.get(data, "expressionValue");
        // do date conversion
        if (!dd.list && dd.type === XDataType.DATE) {
            if (!isNaN(new Date(this.expressionValue).getTime())) {
                // is valid date
                this.expressionValue = new Date(this.expressionValue);
            }
        }
        this.variableNameValue = Reflect.get(data, "variableName");
    }
    getVariableNames() {
        var _a;
        if (this.valueTypeValue === ExpressionValueType.VARIABLE) {
            return [this.variableNameValue];
        }
        else if (this.valueTypeValue === ExpressionValueType.SUBEXPRESSION) {
            return (_a = this.subExpression) === null || _a === void 0 ? void 0 : _a.getVarialbleNames();
        }
        return [];
    }
}
//# sourceMappingURL=ExpressionValue.js.map