(function (factory) {
    if (typeof module === "object" && typeof module.exports === "object") {
        var v = factory(require, exports);
        if (v !== undefined) module.exports = v;
    }
    else if (typeof define === "function" && define.amd) {
        define(["require", "exports", "./AtomBinder", "./ExpressionParser"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.AtomWatcher = exports.ObjectProperty = void 0;
    var AtomBinder_1 = require("./AtomBinder");
    var ExpressionParser_1 = require("./ExpressionParser");
    var ObjectProperty = /** @class */ (function () {
        function ObjectProperty(name) {
            this.name = name;
        }
        ObjectProperty.prototype.toString = function () {
            return this.name;
        };
        return ObjectProperty;
    }());
    exports.ObjectProperty = ObjectProperty;
    /**
     *
     *
     * @export
     * @class AtomWatcher
     * @implements {IDisposable}
     * @template T
     */
    var AtomWatcher = /** @class */ (function () {
        /**
         * Creates an instance of AtomWatcher.
         *
         *      let w = new AtomWatcher(this, x => x.data.fullName = `${x.data.firstName} ${x.data.lastName}`);
         *
         * You must dispose `w` in order to avoid memory leaks.
         * Above method will set fullName whenever, data or its firstName,lastName property is modified.
         *
         * AtomWatcher will assign null if any expression results in null in single property path.
         *
         * In order to avoid null, you can rewrite above expression as,
         *
         *      let w = new AtomWatcher(this,
         *                  x => {
         *                      if(x.data.firstName && x.data.lastName){
         *                        x.data.fullName = `${x.data.firstName} ${x.data.lastName}`
         *                      }
         *                  });
         *
         * @param {T} target - Target on which watch will be set to observe given set of properties
         * @param {(PathList[] | ((x:T) => any))} path - Path is either lambda expression or array of
         *                      property path to watch, if path was lambda, it will be executed when any of
         *                      members will modify
         * @param {Function} onChanged - This function will be executed when any member in path is updated
         * @memberof AtomWatcher
         */
        function AtomWatcher(target, path, onChanged, source) {
            var _this = this;
            this.source = source;
            this.isExecuting = false;
            this.target = target;
            this.forValidation = true;
            if (path instanceof Function) {
                var f = path;
                path = ExpressionParser_1.parsePath(path);
                this.func = onChanged || f;
                this.funcText = f.toString();
            }
            else {
                this.func = onChanged;
            }
            this.runEvaluate = function () {
                _this.evaluate();
            };
            this.runEvaluate.watcher = this;
            this.path = path.map(function (x) { return x.map(function (y) { return new ObjectProperty(y); }); });
            if (!this.path.length) {
                // tslint:disable-next-line:no-debugger
                debugger;
                // tslint:disable-next-line:no-console
                console.warn("There is nothing to watch, do not use one way binding without any binding expression");
            }
        }
        AtomWatcher.prototype.toString = function () {
            return this.func.toString();
        };
        /**
         * This will dispose and unregister all watchers
         *
         * @memberof AtomWatcher
         */
        AtomWatcher.prototype.dispose = function () {
            if (!this.path) {
                return;
            }
            for (var _i = 0, _a = this.path; _i < _a.length; _i++) {
                var p = _a[_i];
                for (var _b = 0, p_1 = p; _b < p_1.length; _b++) {
                    var op = p_1[_b];
                    if (op.watcher) {
                        op.watcher.dispose();
                        op.watcher = null;
                        op.target = null;
                    }
                }
            }
            // tslint:disable-next-line:no-string-literal
            // this["disposedPath"] = this.path;
            this.func = null;
            // this.path.length = 0;
            this.path = null;
            this.source = null;
        };
        /**
         * Initialize the path targets
         * @param evaluate if true, evaluate entire watch expression and run onChange method
         */
        AtomWatcher.prototype.init = function (evaluate) {
            if (evaluate) {
                this.evaluate(true);
            }
            else {
                for (var _i = 0, _a = this.path; _i < _a.length; _i++) {
                    var iterator = _a[_i];
                    this.evaluatePath(this.target, iterator);
                }
            }
        };
        AtomWatcher.prototype.evaluatePath = function (target, path) {
            // console.log(`\tevaluatePath: ${path.map(op=>op.name).join(", ")}`);
            var newTarget = null;
            for (var _i = 0, path_1 = path; _i < path_1.length; _i++) {
                var p = path_1[_i];
                if (this.source && p.name === "this") {
                    target = this.source;
                    continue;
                }
                newTarget = target[p.name];
                if (!p.target) {
                    if (p.watcher) {
                        p.watcher.dispose();
                    }
                    p.watcher = AtomBinder_1.AtomBinder.watch(target, p.name, this.runEvaluate);
                }
                else if (p.target !== target) {
                    if (p.watcher) {
                        p.watcher.dispose();
                    }
                    p.watcher = AtomBinder_1.AtomBinder.watch(target, p.name, this.runEvaluate);
                }
                p.target = target;
                target = newTarget;
                if (newTarget === undefined || newTarget === null) {
                    break;
                }
            }
            return newTarget;
        };
        /**
         *
         *
         * @param {boolean} [force]
         * @returns {*}
         * @memberof AtomWatcher
         */
        AtomWatcher.prototype.evaluate = function (force) {
            if (!this.path) {
                // this watcher may have been disposed...
                // tslint:disable-next-line:no-console
                console.warn("Watcher is not disposed properly, please watch for any memory leak");
                return;
            }
            if (this.isExecuting) {
                return;
            }
            var disposeWatchers = [];
            this.isExecuting = true;
            try {
                var values = [];
                var logs = [];
                for (var _i = 0, _a = this.path; _i < _a.length; _i++) {
                    var p = _a[_i];
                    values.push(this.evaluatePath(this.target, p));
                }
                // if (force === true) {
                //     this.forValidation = false;
                // }
                // if (this.forValidation) {
                //     const x: boolean = true;
                //     if (values.find( (x1) => x1 ? true : false)) {
                //         this.forValidation = false;
                //     } else {
                //         return;
                //     }
                // }
                try {
                    this.func.apply(this.target, values);
                }
                catch (e) {
                    // tslint:disable-next-line:no-console
                    console.warn(e);
                }
            }
            finally {
                this.isExecuting = false;
                for (var _b = 0, disposeWatchers_1 = disposeWatchers; _b < disposeWatchers_1.length; _b++) {
                    var d = disposeWatchers_1[_b];
                    d.dispose();
                }
            }
        };
        return AtomWatcher;
    }());
    exports.AtomWatcher = AtomWatcher;
});
//# sourceMappingURL=AtomWatcher.js.map