var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
var __param = (this && this.__param) || function (paramIndex, decorator) {
    return function (target, key) { decorator(target, key, paramIndex); }
};
(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", "../App", "../core/AtomBridge", "../core/PropertyBinding", "../core/PropertyMap", "../core/types", "../di/Inject"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    var App_1 = require("../App");
    var AtomBridge_1 = require("../core/AtomBridge");
    var PropertyBinding_1 = require("../core/PropertyBinding");
    var PropertyMap_1 = require("../core/PropertyMap");
    // tslint:disable-next-line:import-spacing
    var types_1 = require("../core/types");
    var Inject_1 = require("../di/Inject");
    var AtomComponent = /** @class */ (function () {
        function AtomComponent(app, element) {
            if (element === void 0) { element = null; }
            this.app = app;
            this.element = element;
            this.mInvalidated = 0;
            this.mPendingPromises = {};
            this.mData = undefined;
            this.mViewModel = undefined;
            this.mLocalViewModel = undefined;
            // public abstract get templateParent(): TC;
            // {
            //     return AtomBridge.instance.templateParent(this.element);
            // }
            this.eventHandlers = [];
            this.bindings = [];
            // if (!app) {
            //     // tslint:disable-next-line:no-console
            //     console.error("app cannot be null while creating control");
            // }
            this.disposables = [];
            // this.element = e;
            var a = this.beginEdit();
            this.preCreate();
            this.create();
            AtomBridge_1.AtomBridge.instance.attachControl(element, this);
            app.callLater(function () { return a.dispose(); });
        }
        Object.defineProperty(AtomComponent.prototype, "data", {
            get: function () {
                if (this.mData !== undefined) {
                    return this.mData;
                }
                var parent = this.parent;
                if (parent) {
                    return parent.data;
                }
                return undefined;
            },
            set: function (v) {
                this.mData = v;
                AtomBridge_1.AtomBridge.instance.refreshInherited(this, "data");
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(AtomComponent.prototype, "viewModel", {
            get: function () {
                if (this.mViewModel !== undefined) {
                    return this.mViewModel;
                }
                var parent = this.parent;
                if (parent) {
                    return parent.viewModel;
                }
                return undefined;
            },
            set: function (v) {
                var old = this.mViewModel;
                if (old && old.dispose) {
                    old.dispose();
                }
                this.mViewModel = v;
                AtomBridge_1.AtomBridge.instance.refreshInherited(this, "viewModel");
            },
            enumerable: true,
            configurable: true
        });
        Object.defineProperty(AtomComponent.prototype, "localViewModel", {
            get: function () {
                if (this.mLocalViewModel !== undefined) {
                    return this.mLocalViewModel;
                }
                var parent = this.parent;
                if (parent) {
                    return parent.localViewModel;
                }
                return undefined;
            },
            set: function (v) {
                var old = this.mLocalViewModel;
                if (old && old.dispose) {
                    old.dispose();
                }
                this.mLocalViewModel = v;
                AtomBridge_1.AtomBridge.instance.refreshInherited(this, "localViewModel");
            },
            enumerable: true,
            configurable: true
        });
        AtomComponent.prototype.bind = function (element, name, path, twoWays, valueFunc, source) {
            var _this = this;
            // remove existing binding if any
            var binding = this.bindings.find(function (x) { return x.name === name && (element ? x.element === element : true); });
            if (binding) {
                binding.dispose();
                types_1.ArrayHelper.remove(this.bindings, function (x) { return x === binding; });
            }
            binding = new PropertyBinding_1.PropertyBinding(this, element, name, path, twoWays, valueFunc, source);
            this.bindings.push(binding);
            return {
                dispose: function () {
                    binding.dispose();
                    types_1.ArrayHelper.remove(_this.bindings, function (x) { return x === binding; });
                }
            };
        };
        /**
         * Remove all bindings associated with given element and optional name
         * @param element T
         * @param name string
         */
        AtomComponent.prototype.unbind = function (element, name) {
            var toDelete = this.bindings.filter(function (x) { return x.element === element && (!name || (x.name === name)); });
            var _loop_1 = function (iterator) {
                iterator.dispose();
                types_1.ArrayHelper.remove(this_1.bindings, function (x) { return x === iterator; });
            };
            var this_1 = this;
            for (var _i = 0, toDelete_1 = toDelete; _i < toDelete_1.length; _i++) {
                var iterator = toDelete_1[_i];
                _loop_1(iterator);
            }
        };
        AtomComponent.prototype.bindEvent = function (element, name, method, key) {
            var _this = this;
            if (!element) {
                return;
            }
            if (!method) {
                return;
            }
            var be = {
                element: element,
                name: name,
                handler: method
            };
            if (key) {
                be.key = key;
            }
            be.disposable = AtomBridge_1.AtomBridge.instance.addEventHandler(element, name, method, false);
            this.eventHandlers.push(be);
            return {
                dispose: function () {
                    be.disposable.dispose();
                    types_1.ArrayHelper.remove(_this.eventHandlers, function (e) { return e.disposable === be.disposable; });
                }
            };
        };
        AtomComponent.prototype.unbindEvent = function (element, name, method, key) {
            var deleted = [];
            for (var _i = 0, _a = this.eventHandlers; _i < _a.length; _i++) {
                var be = _a[_i];
                if (element && be.element !== element) {
                    return;
                }
                if (key && be.key !== key) {
                    return;
                }
                if (name && be.name !== name) {
                    return;
                }
                if (method && be.handler !== method) {
                    return;
                }
                be.disposable.dispose();
                be.handler = null;
                be.element = null;
                be.name = null;
                be.key = null;
                deleted.push(be);
            }
            this.eventHandlers = this.eventHandlers.filter(function (x) { return deleted.findIndex(function (d) { return d === x; }) !== -1; });
        };
        AtomComponent.prototype.hasProperty = function (name) {
            if (this[name] !== undefined) {
                return true;
            }
            var map = PropertyMap_1.PropertyMap.from(this);
            return map.map[name];
        };
        /**
         * Use this method if you want to set attribute on HTMLElement immediately but
         * defer atom control property
         * @param element HTMLElement
         * @param name string
         * @param value any
         */
        AtomComponent.prototype.setPrimitiveValue = function (element, name, value) {
            var _this = this;
            var p = value;
            if (p && p.then && p.catch) {
                this.mPendingPromises[name] = p;
                p.then(function (r) {
                    if (_this.mPendingPromises[name] !== p) {
                        return;
                    }
                    _this.mPendingPromises[name] = null;
                    _this.setPrimitiveValue(element, name, r);
                }).catch(function (e) {
                    if (_this.mPendingPromises[name] !== p) {
                        return;
                    }
                    _this.mPendingPromises[name] = null;
                    // tslint:disable-next-line:no-console
                    console.error(e);
                });
                return;
            }
            if (/^(viewModel|localViewModel)$/.test(name)) {
                this[name] = value;
                return;
            }
            if ((!element || element === this.element) && this.hasProperty(name)) {
                this.runAfterInit(function () {
                    _this[name] = value;
                });
            }
            else {
                this.setElementValue(element, name, value);
            }
        };
        AtomComponent.prototype.setLocalValue = function (element, name, value) {
            var _this = this;
            // if value is a promise
            var p = value;
            if (p && p.then && p.catch) {
                this.mPendingPromises[name] = p;
                p.then(function (r) {
                    if (_this.mPendingPromises[name] !== p) {
                        return;
                    }
                    _this.mPendingPromises[name] = null;
                    _this.setLocalValue(element, name, r);
                }).catch(function (e) {
                    if (_this.mPendingPromises[name] !== p) {
                        return;
                    }
                    _this.mPendingPromises[name] = null;
                    // tslint:disable-next-line:no-console
                    console.error(e);
                });
                return;
            }
            if ((!element || element === this.element) && this.hasProperty(name)) {
                this[name] = value;
            }
            else {
                this.setElementValue(element, name, value);
            }
        };
        AtomComponent.prototype.dispose = function (e) {
            if (this.mInvalidated) {
                clearTimeout(this.mInvalidated);
                this.mInvalidated = 0;
            }
            AtomBridge_1.AtomBridge.instance.visitDescendents(e || this.element, function (ex, ac) {
                if (ac) {
                    ac.dispose();
                    return false;
                }
                return true;
            });
            if (!e) {
                this.unbindEvent(null, null, null);
                for (var _i = 0, _a = this.bindings; _i < _a.length; _i++) {
                    var binding = _a[_i];
                    binding.dispose();
                }
                this.bindings.length = 0;
                this.bindings = null;
                AtomBridge_1.AtomBridge.instance.dispose(this.element);
                this.element = null;
                var lvm = this.mLocalViewModel;
                if (lvm && lvm.dispose) {
                    lvm.dispose();
                    this.mLocalViewModel = null;
                }
                var vm = this.mViewModel;
                if (vm && vm.dispose) {
                    vm.dispose();
                    this.mViewModel = null;
                }
                for (var _b = 0, _c = this.disposables; _b < _c.length; _b++) {
                    var iterator = _c[_b];
                    iterator.dispose();
                }
                this.pendingInits = null;
            }
        };
        // tslint:disable-next-line:no-empty
        AtomComponent.prototype.onPropertyChanged = function (name) {
        };
        AtomComponent.prototype.beginEdit = function () {
            var _this = this;
            this.pendingInits = [];
            var a = this.pendingInits;
            return {
                dispose: function () {
                    if (_this.pendingInits == null) {
                        // case where current control is disposed...
                        return;
                    }
                    _this.pendingInits = null;
                    if (a) {
                        for (var _i = 0, a_1 = a; _i < a_1.length; _i++) {
                            var iterator = a_1[_i];
                            iterator();
                        }
                    }
                    _this.invalidate();
                }
            };
        };
        AtomComponent.prototype.invalidate = function () {
            var _this = this;
            if (this.mInvalidated) {
                clearTimeout(this.mInvalidated);
            }
            this.mInvalidated = setTimeout(function () {
                _this.mInvalidated = 0;
                _this.app.callLater(function () {
                    _this.onUpdateUI();
                });
            }, 5);
        };
        AtomComponent.prototype.onUpdateUI = function () {
            // for implementors..
        };
        AtomComponent.prototype.runAfterInit = function (f) {
            if (this.pendingInits) {
                this.pendingInits.push(f);
            }
            else {
                f();
            }
        };
        AtomComponent.prototype.registerDisposable = function (d) {
            var _this = this;
            this.disposables.add(d);
            return {
                dispose: function () {
                    types_1.ArrayHelper.remove(_this.disposables, function (f) { return f === d; });
                    d.dispose();
                }
            };
        };
        // tslint:disable-next-line:no-empty
        AtomComponent.prototype.create = function () {
        };
        // tslint:disable-next-line:no-empty
        AtomComponent.prototype.preCreate = function () {
        };
        AtomComponent.prototype.setElementValue = function (element, name, value) {
            AtomBridge_1.AtomBridge.instance.setValue(element, name, value);
        };
        AtomComponent.prototype.resolve = function (c, selfName) {
            var result = this.app.resolve(c, true);
            if (selfName) {
                if (typeof selfName === "function") {
                    // this is required as parent is not available
                    // in items control so binding becomes difficult
                    this.runAfterInit(function () {
                        var v = selfName();
                        if (v) {
                            for (var key in v) {
                                if (v.hasOwnProperty(key)) {
                                    var element = v[key];
                                    result[key] = element;
                                }
                            }
                        }
                    });
                }
                else {
                    result[selfName] = this;
                }
            }
            return result;
        };
        AtomComponent = __decorate([
            __param(0, Inject_1.Inject),
            __metadata("design:paramtypes", [App_1.App, Object])
        ], AtomComponent);
        return AtomComponent;
    }());
    exports.AtomComponent = AtomComponent;
});
//# sourceMappingURL=AtomComponent.js.map