import { XExpressionDefinition } from "../../Expressions/XExpressionDefinition";
import { BooleanDataDefinition } from "../../Common/Data/Definitions/BooleanDataDefinition";
import { ValidationIssue } from "../../Common/IDesignValidationProvider";
import { ExpressionValueType } from "../../Expressions/ExpressionValueType";
import { IntersectionRuleTestResult, IntersectionXRuleTestResult, IntersectionYRuleTestResult, } from "./IntersectionRuleTestResult";
import { DataUtilities } from "../../Common/DataUtilities";
import { EventLog } from "../../Common/Event/EventLog";
import { RuleExecutable } from "../../Common/Execution/RuleExecutable";
export class IntersectionResult {
    constructor() {
        this.x = "";
        this.y = "";
        this.resultExpression = new XExpressionDefinition();
    }
    setValue(newValue) {
        if (!(newValue instanceof XExpressionDefinition)) {
            this.resultExpression.valueType = ExpressionValueType.VALUE;
            this.resultExpression.value = newValue;
        }
    }
    getStorageData() {
        let obj = {};
        Reflect.set(obj, "x", this.x);
        Reflect.set(obj, "y", this.y);
        Reflect.set(obj, "resultExpression", this.resultExpression.getStorageData());
        return obj;
    }
    setStorageData(data) {
        this.x = Reflect.get(data, "x");
        this.y = Reflect.get(data, "y");
        let resultExpression = Reflect.get(data, "resultExpression");
        this.resultExpression.setStorageData(resultExpression);
    }
    getDesignValidationIssues() {
        let issues = [];
        if (this.x === "") {
            issues.push(new ValidationIssue("invalid x"));
        }
        if (this.y === "") {
            issues.push(new ValidationIssue("invalid y"));
        }
        issues.push(...this.resultExpression.getDesignValidationIssues());
        return issues;
    }
}
export class IntersectionRuleDefinition extends RuleExecutable {
    constructor() {
        super();
        this._xaxisExpressions = [];
        this._yaxisExpressions = [];
        this._results = [];
        this._resultType = new BooleanDataDefinition();
        this._id = DataUtilities.generateUniqueId();
        this.addXAxisExpression();
        this.addYAxisExpression();
    }
    get results() {
        return this._results;
    }
    get resultType() {
        return this._resultType;
    }
    get xaxisExpressions() {
        return this._xaxisExpressions;
    }
    get yaxisExpressions() {
        return this._yaxisExpressions;
    }
    removeExpression(exp, direction = "x") {
        if (direction === "x")
            this.removeXAxisExpression(exp);
        if (direction === "y")
            this.removeYAxisExpression(exp);
    }
    removeXAxisExpression(exp) {
        if (this._xaxisExpressions.length === 1)
            return;
        this._xaxisExpressions = this._xaxisExpressions.filter((e) => e._id !== exp._id);
        this._results = this._results.filter((result) => result.x !== exp._id);
    }
    removeYAxisExpression(exp) {
        if (this._yaxisExpressions.length === 1)
            return;
        this._yaxisExpressions = this._yaxisExpressions.filter((e) => e._id !== exp._id);
        this._results = this._results.filter((result) => result.y !== exp._id);
    }
    addYAxisExpression(exp) {
        let expression = exp;
        if (!expression) {
            expression = new XExpressionDefinition();
            expression.valueType = ExpressionValueType.SUBEXPRESSION;
        }
        expression.inputData = () => {
            return this.getInputDefinition();
        };
        this._yaxisExpressions.push(expression);
        for (let x of this.xaxisExpressions) {
            this.results.push(this.createResultCell(x, expression));
        }
    }
    addXAxisExpression(exp) {
        let expression = exp;
        if (!expression) {
            expression = new XExpressionDefinition();
            expression.valueType = ExpressionValueType.SUBEXPRESSION;
        }
        expression.inputData = () => this.getInputDefinition();
        expression.executionResult = () => {
            return new BooleanDataDefinition();
        };
        this._xaxisExpressions.push(expression);
        for (let y of this.yaxisExpressions) {
            this.results.push(this.createResultCell(expression, y));
        }
    }
    createResultCell(x, y) {
        let resultCol = new IntersectionResult();
        resultCol.x = x._id;
        resultCol.y = y._id;
        resultCol.resultExpression.executionResult = () => {
            return this.resultType;
        };
        resultCol.resultExpression.inputData = () => {
            return this.getInputDefinition();
        };
        resultCol.resultExpression.expressionRoot.expectedType = this.resultType;
        return resultCol;
    }
    validateResultCells() {
        // makes sure that result cells have all required
    }
    purgeResultCells() {
        // remove unused result cells
    }
    run(data, testContext) {
        let subContext;
        let ysubContext;
        let xsubContext;
        if (testContext) {
            subContext = new IntersectionRuleTestResult(this._id);
            subContext.definition = this;
            subContext.addLogItem(new EventLog("IntersectionRule: start evaluating"));
            testContext.addTestResultItem(subContext);
            ysubContext = new IntersectionYRuleTestResult(`${this._id}_y`);
            subContext.addTestResultItem(ysubContext);
            xsubContext = new IntersectionXRuleTestResult(`${this._id}_x`);
            subContext.addTestResultItem(xsubContext);
        }
        let evalutatedYID;
        let evalutatedXID;
        for (let yIdx in this._yaxisExpressions) {
            let y = this._yaxisExpressions[yIdx];
            try {
                if (ysubContext) {
                    ysubContext.addLogItem(new EventLog(`IntersectionRule: start evalutating ${parseInt(yIdx, 10) + 1}th y expression`));
                }
                let res = y.run(data, ysubContext);
                if (ysubContext) {
                    ysubContext.addLogItem(new EventLog(`IntersectionRule: end evalutating ${parseInt(yIdx, 10) + 1}th y expression with result [${res}]`));
                }
                if (res) {
                    evalutatedYID = y._id;
                    break;
                }
            }
            catch (e) {
                if (ysubContext) {
                    ysubContext.isError = true;
                    ysubContext.result = e.message;
                    ysubContext.addLogItem(new EventLog(`IntersectionRule: end evaluating with error [${e.message}]`));
                    if (subContext) {
                        subContext.isError = true;
                        subContext.result = e.message;
                        subContext.addLogItem(new EventLog(`IntersectionRule: end evaluating with error [${e.message}]`));
                    }
                }
                throw e;
            }
        }
        for (let xIdx in this._xaxisExpressions) {
            let x = this._xaxisExpressions[xIdx];
            try {
                let res = x.run(data, xsubContext);
                if (res) {
                    evalutatedXID = x._id;
                    break;
                }
            }
            catch (e) {
                if (xsubContext) {
                    xsubContext.isError = true;
                    xsubContext.result = e.message;
                    xsubContext.addLogItem(new EventLog(`IntersectionRule: end evaluating with error [${e.message}]`));
                    if (subContext) {
                        subContext.isError = true;
                        subContext.result = e.message;
                        subContext.addLogItem(new EventLog(`IntersectionRule: end evaluating with error [${e.message}]`));
                    }
                }
                throw e;
            }
        }
        if (evalutatedXID && evalutatedYID) {
            let resultCell = this.findResultCellByXY(evalutatedXID, evalutatedYID);
            if (!resultCell) {
                throw new Error("Can not find a result cell");
            }
            if (subContext) {
                subContext.addLogItem(new EventLog("IntersectionRule: start evalutating result expression"));
            }
            try {
                let res = resultCell.resultExpression.run(data, subContext);
                if (subContext) {
                    subContext.addLogItem(new EventLog(`IntersectionRule: end evalutating result expression with result [${res}]`));
                    subContext.result = res.toString();
                }
                return res;
            }
            catch (e) {
                if (subContext) {
                    subContext.isError = true;
                    subContext.result = e.message;
                    subContext.addLogItem(new EventLog(`IntersectionRule: end evaluating with error [${e.message}]`));
                }
                throw e;
            }
        }
    }
    findResultCellByXY(x, y) {
        return this.results.find((res) => res.x === x && res.y === y);
    }
    setTypeResults(newType) {
        if (!this.canChangeOutputs)
            return;
        this._resultType = newType;
        for (let resCol of this.results) {
            resCol.resultExpression.executionResult = () => this.resultType;
        }
    }
    getDesignValidationIssues() {
        let issues = [];
        for (let x in this.xaxisExpressions) {
            let xexp = this.xaxisExpressions[x];
            issues.push(...xexp.getDesignValidationIssues().map((issue) => {
                issue.issue = `Cell Expression[${parseInt(x, 10) + 1}]:: ${issue.issue}`;
                return issue;
            }));
        }
        for (let y in this.yaxisExpressions) {
            let yexp = this.yaxisExpressions[y];
            issues.push(...yexp.getDesignValidationIssues().map((issue) => {
                issue.issue = `Row Expression[${parseInt(y, 10) + 1}]:: ${issue.issue}`;
                return issue;
            }));
        }
        for (let y in this.yaxisExpressions) {
            let yexp = this.yaxisExpressions[y];
            for (let x in this.xaxisExpressions) {
                let xexp = this.xaxisExpressions[x];
                const result = this.findResultCellByXY(xexp._id, yexp._id);
                if (!result) {
                    // issues.push(
                    //     new ValidationIssue(`Result Cell[${parseInt(y, 10) + 1}, ${parseInt(x, 10) + 1}] is required.`)
                    // );
                }
                else {
                    issues.push(...result.getDesignValidationIssues().map((issue) => {
                        issue.issue = `Result Cell[${parseInt(y, 10) + 1}, ${parseInt(x, 10) + 1}]:: ${issue.issue}`;
                        return issue;
                    }));
                }
            }
        }
        return issues;
    }
    getStorageData() {
        let obj = {};
        let xaxisExpressions = [];
        for (let x of this.xaxisExpressions) {
            xaxisExpressions.push(x.getStorageData());
        }
        Reflect.set(obj, "xaxisExpressions", xaxisExpressions);
        let yaxisExpressions = [];
        for (let y of this.yaxisExpressions) {
            yaxisExpressions.push(y.getStorageData());
        }
        Reflect.set(obj, "yaxisExpressions", yaxisExpressions);
        let results = [];
        for (let rescol of this.results) {
            results.push(rescol.getStorageData());
        }
        Reflect.set(obj, "results", results);
        return obj;
    }
    setStorageData(data) {
        this._xaxisExpressions = (Reflect.get(data, "xaxisExpressions") || []).map((datum) => {
            let exp = new XExpressionDefinition();
            exp.setStorageData(datum);
            return exp;
        });
        this._yaxisExpressions = (Reflect.get(data, "yaxisExpressions") || []).map((datum) => {
            let exp = new XExpressionDefinition();
            exp.setStorageData(datum);
            return exp;
        });
        this._results = (Reflect.get(data, "results") || []).map((datum) => {
            let res = new IntersectionResult();
            res.setStorageData(datum);
            return res;
        });
    }
}
//# sourceMappingURL=IntersectionRuleDefinition.js.map