(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", "./types"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.AtomBinder = void 0;
    var types_1 = require("./types");
    var AtomBinder = /** @class */ (function () {
        function AtomBinder() {
        }
        AtomBinder.refreshValue = function (target, key) {
            var handlers = AtomBinder.get_WatchHandler(target, key);
            if (handlers === undefined || handlers == null) {
                return;
            }
            for (var _i = 0, handlers_1 = handlers; _i < handlers_1.length; _i++) {
                var item = handlers_1[_i];
                item(target, key);
            }
            if (target.onPropertyChanged) {
                target.onPropertyChanged(key);
            }
        };
        AtomBinder.add_WatchHandler = function (target, key, handler) {
            if (target == null) {
                return;
            }
            var handlers = AtomBinder.get_WatchHandler(target, key);
            handlers.push(handler);
            if (Array.isArray(target)) {
                return;
            }
            // get existing property definition if it ha any
            var pv = AtomBinder.getPropertyDescriptor(target, key);
            // return if it has a getter
            // in case of getter/setter, it is responsibility of setter to refresh
            // object
            if (pv && pv.get) {
                return;
            }
            var tw = target;
            if (!tw._$_bindable) {
                tw._$_bindable = {};
            }
            if (!tw._$_bindable[key]) {
                tw._$_bindable[key] = 1;
                var o = target[key];
                var nk_1 = "_$_" + key;
                target[nk_1] = o;
                var set = function (v) {
                    var ov = this[nk_1];
                    // tslint:disable-next-line:triple-equals
                    if (ov === undefined ? ov === v : ov == v) {
                        return;
                    }
                    this[nk_1] = v;
                    AtomBinder.refreshValue(this, key);
                };
                var get = function () {
                    return this[nk_1];
                };
                if (pv) {
                    delete target[key];
                    Object.defineProperty(target, key, {
                        get: get,
                        set: set,
                        configurable: true,
                        enumerable: true
                    });
                }
                else {
                    Object.defineProperty(target, key, {
                        get: get, set: set,
                        enumerable: true, configurable: true
                    });
                }
            }
        };
        AtomBinder.getPropertyDescriptor = function (target, key) {
            var pv = Object.getOwnPropertyDescriptor(target, key);
            if (!pv) {
                var pt = Object.getPrototypeOf(target);
                if (pt) {
                    return AtomBinder.getPropertyDescriptor(pt, key);
                }
            }
            return pv;
        };
        AtomBinder.get_WatchHandler = function (target, key) {
            if (target == null) {
                return null;
            }
            var handlers = target._$_handlers;
            if (!handlers) {
                handlers = {};
                target._$_handlers = handlers;
            }
            var handlersForKey = handlers[key];
            if (handlersForKey === undefined || handlersForKey == null) {
                handlersForKey = [];
                handlers[key] = handlersForKey;
            }
            return handlersForKey;
        };
        AtomBinder.remove_WatchHandler = function (target, key, handler) {
            if (target == null) {
                return;
            }
            if (!target._$_handlers) {
                return;
            }
            var handlersForKey = target._$_handlers[key];
            if (handlersForKey === undefined || handlersForKey == null) {
                return;
            }
            // handlersForKey = handlersForKey.filter( (f) => f !== handler);
            types_1.ArrayHelper.remove(handlersForKey, function (f) { return f === handler; });
            if (!handlersForKey.length) {
                target._$_handlers[key] = null;
                delete target._$_handlers[key];
            }
        };
        AtomBinder.invokeItemsEvent = function (target, mode, index, item) {
            var key = "_items";
            var handlers = AtomBinder.get_WatchHandler(target, key);
            if (!handlers) {
                return;
            }
            for (var _i = 0, handlers_2 = handlers; _i < handlers_2.length; _i++) {
                var obj = handlers_2[_i];
                obj(target, mode, index, item);
            }
            AtomBinder.refreshValue(target, "length");
        };
        AtomBinder.refreshItems = function (ary) {
            AtomBinder.invokeItemsEvent(ary, "refresh", -1, null);
        };
        AtomBinder.add_CollectionChanged = function (target, handler) {
            if (target == null) {
                throw new Error("Target Array to watch cannot be null");
            }
            if (handler == null) {
                throw new Error("Target handle to watch an Array cannot be null");
            }
            var handlers = AtomBinder.get_WatchHandler(target, "_items");
            handlers.push(handler);
            return { dispose: function () {
                    AtomBinder.remove_CollectionChanged(target, handler);
                }
            };
        };
        AtomBinder.remove_CollectionChanged = function (t, handler) {
            if (t == null) {
                return;
            }
            var target = t;
            if (!target._$_handlers) {
                return;
            }
            var key = "_items";
            var handlersForKey = target._$_handlers[key];
            if (handlersForKey === undefined || handlersForKey == null) {
                return;
            }
            types_1.ArrayHelper.remove(handlersForKey, function (f) { return f === handler; });
            if (!handlersForKey.length) {
                target._$_handlers[key] = null;
                delete target._$_handlers[key];
            }
        };
        AtomBinder.watch = function (item, property, f) {
            AtomBinder.add_WatchHandler(item, property, f);
            return {
                dispose: function () {
                    AtomBinder.remove_WatchHandler(item, property, f);
                }
            };
        };
        AtomBinder.clear = function (a) {
            a.length = 0;
            this.invokeItemsEvent(a, "refresh", -1, null);
            AtomBinder.refreshValue(a, "length");
        };
        AtomBinder.addItem = function (a, item) {
            var index = a.length;
            a.push(item);
            this.invokeItemsEvent(a, "add", index, item);
            AtomBinder.refreshValue(a, "length");
        };
        AtomBinder.removeItem = function (a, item) {
            var i = a.findIndex(function (x) { return x === item; });
            if (i === -1) {
                return false;
            }
            a.splice(i, 1);
            AtomBinder.invokeItemsEvent(a, "remove", i, item);
            AtomBinder.refreshValue(a, "length");
            return true;
        };
        return AtomBinder;
    }());
    exports.AtomBinder = AtomBinder;
});
//# sourceMappingURL=AtomBinder.js.map