"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var tslib_1 = require("tslib");
var ig_utilities_1 = require("@intelligentgraphics/ig.utilities");
var ig_gfx_core_1 = require("@intelligentgraphics/ig.gfx.core");
var lodash_1 = tslib_1.__importDefault(require("lodash"));
var Logger_1 = tslib_1.__importDefault(require("./Logger"));
var closest = function (n) { return function (x) { return Math.round(x / n) * n; }; };
var centimeters = closest(0.001);
var NUMBER_LIKE = /^(\d+(\.\d*)?)|(\.\d+)$/;
/** Evaluation of rules. */
var Rule = /** @class */ (function () {
    function Rule() {
    }
    /** Evaluate a parametric attachment point against a configuration state.
     * @param point Parametric attachment point.
     * @param configuration A configuration state.
     * @returns Real attachment point, or null.
     */
    Rule.evalAttachPoint = function (point, configuration) {
        try {
            if (Rule.evalBooleanRule(point.Enabled, configuration) === false) {
                return null;
            }
            //FIXME: null checks for rule eval results
            var tPoint = {
                Enabled: true,
                Key: Rule.evalStringRule(point.Key, configuration),
                Foreign: Rule.evalStringRule(point.Foreign, configuration),
                Type: point.Type,
                Point: {
                    X: Rule.evalNumberRule(point.PointX, configuration),
                    Y: Rule.evalNumberRule(point.PointY, configuration),
                    Z: Rule.evalNumberRule(point.PointZ, configuration),
                },
                Rotation: {
                    X: 0,
                    Y: Rule.evalNumberRule(point.RotationY, configuration),
                    Z: 0,
                },
                Representation: point.Representation,
            };
            return tPoint;
        }
        catch (e) {
            console.error("Error while evaluating the point " + JSON.stringify(point) + " with configuration " + JSON.stringify(configuration) + ".");
            return null;
        }
    };
    Rule.evalBooleanRule = function (rule, properties) {
        if (typeof rule === "boolean") {
            return rule;
        }
        if (rule === "true") {
            return true;
        }
        if (rule === "false") {
            return false;
        }
        var tResult = Rule._evalRule(rule, properties);
        if (ig_utilities_1.isNullOrUndefined(tResult)) {
            Logger_1.default.warn("Rule \"" + rule + "\" evaluated to \"undefined\" with the following properties:", properties);
            return undefined;
        }
        return Boolean(tResult);
    };
    Rule.evalNumberRule = function (rule, properties) {
        if (ig_utilities_1.isNullOrUndefined(rule)) {
            return undefined;
        }
        if (typeof rule === "number") {
            return rule;
        }
        var tResult = Rule._evalRule(rule, properties);
        if (typeof tResult === "number") {
            return tResult;
        }
        if (ig_utilities_1.isNullOrUndefined(tResult)) {
            Logger_1.default.warn("Rule \"" + rule + "\" evaluated to \"undefined\" with the following properties:", properties);
            return undefined;
        }
        if (tResult.match(NUMBER_LIKE)) {
            return parseFloat(tResult);
        }
        return undefined;
    };
    Rule.evalStringRule = function (rule, properties) {
        var tResult = Rule._evalRule(rule, properties);
        if (ig_utilities_1.isNullOrUndefined(tResult)) {
            Logger_1.default.warn("Rule \"" + rule + "\" evaluated to \"undefined\" with the following properties:", properties);
            return undefined;
        }
        return tResult.toString();
    };
    Rule._evalRule = function (rule, properties) {
        var ruleParams = tslib_1.__assign(tslib_1.__assign({}, Rule.defaultRuleParameters), properties);
        if (rule[0] !== "{" || rule[rule.length - 1] !== "}") {
            return rule;
        }
        rule = rule.substring(1, rule.length - 1);
        // replace additional rule parameters for using eval later
        for (var key in Rule.defaultRuleParameters) {
            rule = rule.replace(new RegExp(key, "g"), "ruleParams." + key);
        }
        var $bool = function (name, onDefault) {
            try {
                var tValue = eval(properties[name]);
                if (typeof tValue === "boolean") {
                    return tValue;
                }
            }
            catch (e) { }
            return onDefault;
        };
        var $num = function (name, onDefault) {
            try {
                var tValue = eval(properties[name]);
                if (typeof tValue === "number") {
                    return tValue;
                }
            }
            catch (e) { }
            return onDefault;
        };
        var $str = function (name, onDefault) {
            try {
                var tValue = properties[name];
                if (typeof tValue === "string") {
                    return tValue;
                }
            }
            catch (e) { }
            return onDefault;
        };
        var tResult = undefined;
        try {
            tResult = eval(rule);
        }
        catch (e) {
            console.error("Failed to evaluate " + rule + " with properties", properties, e);
        }
        if (Rule.Debug) {
            console.log("MultiArticle Rule Evaluation: " + rule + " -> " + tResult);
        }
        return tResult;
    };
    Rule.evalActionRule = function (rule, properties) {
        try {
            var isActive = Rule.evalBooleanRule(rule.Condition || "true", properties);
            var value = Rule._evalRule(rule.Value, properties);
            var ghostProperty = lodash_1.default.includes(["Width", "Height", "Depth"], rule.GhostParameter)
                ? rule.GhostParameter
                : undefined;
            return {
                isActive: isActive === true,
                property: rule.Parameter,
                value: value,
                ghostProperty: ghostProperty,
            };
        }
        catch (e) {
            console.error("Error while evaluating the rule with condition " + rule.Condition + ", parameter " + rule.Parameter + " and value " + rule.Value + ".");
            return {
                isActive: false,
                property: undefined,
                value: undefined,
                ghostProperty: undefined,
            };
        }
    };
    Rule.createBoundingBoxForRule = function (context) {
        var BBOX = {};
        var rootId = context;
        while (ig_gfx_core_1.State.API.GetParent(rootId)) {
            rootId = ig_gfx_core_1.State.API.GetParent(rootId);
        }
        var rootBounds = ig_gfx_core_1.State.API.GetBounds(rootId, "Local", true);
        var worldBounds = ig_gfx_core_1.State.API.GetBounds(context, "World");
        var contextBounds = ig_gfx_core_1.State.API.GetBounds(context, "Local", true);
        var addBounds = function (id, bounds) {
            BBOX[id] = bounds || {};
            BBOX[id].width = bounds
                ? centimeters(bounds.max.x - bounds.min.x)
                : 0;
            BBOX[id].height = bounds
                ? centimeters(bounds.max.y - bounds.min.y)
                : 0;
            BBOX[id].depth = bounds
                ? centimeters(bounds.max.z - bounds.min.z)
                : 0;
        };
        addBounds("root", rootBounds);
        addBounds("world", worldBounds);
        addBounds("local", contextBounds);
        return BBOX;
    };
    Rule.additionalRuleParameters = function (context, hasLeftNeighbor, hasRightNeighbor, relativeSnapPosition) {
        console.warn("Rule.additionalRuleParameters is deprecated. Please use Rule.createAdditionalRuleParameters instead");
        return Rule.createAdditionalRuleParameters({
            context: context,
            hasBottomNeighbor: false,
            hasLeftNeighbor: hasLeftNeighbor,
            hasRightNeighbor: hasRightNeighbor,
            hasTopNeighbor: false,
            relativeSnapPosition: relativeSnapPosition,
        });
    };
    Rule.createAdditionalRuleParameters = function (_a) {
        var context = _a.context, hasBottomNeighbor = _a.hasBottomNeighbor, hasLeftNeighbor = _a.hasLeftNeighbor, hasRightNeighbor = _a.hasRightNeighbor, hasTopNeighbor = _a.hasTopNeighbor, relativeSnapPosition = _a.relativeSnapPosition;
        var bounds = Rule.createBoundingBoxForRule(context);
        return {
            BBOX: bounds,
            neighbor: {
                left: hasLeftNeighbor,
                right: hasRightNeighbor,
                bottom: hasBottomNeighbor,
                top: hasTopNeighbor,
            },
            snapPosition: {
                x: centimeters(relativeSnapPosition.x),
                y: centimeters(relativeSnapPosition.y),
                z: centimeters(relativeSnapPosition.z),
            },
        };
    };
    Rule.defaultRuleParameters = {
        BBOX: {
            global: ig_gfx_core_1.createBounds(ig_gfx_core_1.Vector.CreateVec3(), ig_gfx_core_1.Vector.CreateVec3()),
            local: ig_gfx_core_1.createBounds(ig_gfx_core_1.Vector.CreateVec3(), ig_gfx_core_1.Vector.CreateVec3()),
            world: ig_gfx_core_1.createBounds(ig_gfx_core_1.Vector.CreateVec3(), ig_gfx_core_1.Vector.CreateVec3()),
        },
        neighbor: {
            left: false,
            right: false,
            top: false,
            bottom: false,
        },
        snapPosition: ig_gfx_core_1.Vector.CreateVec3(),
    };
    Rule.Debug = true;
    Rule.executeAction = function (action, componentId, context) {
        return Rule.executeActions([action], componentId, context);
    };
    Rule.executeActions = function (actions) {
        if (actions.length) {
            Logger_1.default.warn("Missing implementation for executeActions of MultiArticleRule!");
        }
        return Promise.resolve(undefined);
    };
    return Rule;
}());
exports.Rule = Rule;
