(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", "./AtomBridge", "./AtomComponent", "./AtomOnce", "./AtomWatcher"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.PropertyBinding = void 0;
    const AtomBridge_1 = require("./AtomBridge");
    const AtomComponent_1 = require("./AtomComponent");
    const AtomOnce_1 = require("./AtomOnce");
    const AtomWatcher_1 = require("./AtomWatcher");
    class PropertyBinding {
        constructor(target, element, name, path, twoWays, valueFunc, source) {
            this.target = target;
            this.element = element;
            this.name = name;
            this.twoWays = twoWays;
            this.source = source;
            this.isTwoWaySetup = false;
            this.name = name;
            this.twoWays = twoWays;
            this.target = target;
            this.element = element;
            this.updaterOnce = new AtomOnce_1.AtomOnce();
            if (valueFunc) {
                if (typeof valueFunc !== "function") {
                    this.fromSourceToTarget = valueFunc.fromSource;
                    this.fromTargetToSource = valueFunc.fromTarget;
                }
                else {
                    this.fromSourceToTarget = valueFunc;
                }
            }
            this.watcher = new AtomWatcher_1.AtomWatcher(target, path, (...v) => {
                this.updaterOnce.run(() => {
                    if (this.disposed) {
                        return;
                    }
                    // set value
                    for (const iterator of v) {
                        if (iterator === undefined) {
                            return;
                        }
                    }
                    const cv = this.fromSourceToTarget ? this.fromSourceToTarget.apply(this, v) : v[0];
                    if (this.target instanceof AtomComponent_1.AtomComponent) {
                        this.target.setLocalValue(this.element, this.name, cv);
                    }
                    else {
                        this.target[name] = cv;
                    }
                });
            }, source);
            this.path = this.watcher.path;
            if (this.target instanceof AtomComponent_1.AtomComponent) {
                this.target.runAfterInit(() => {
                    if (!this.watcher) {
                        // this is disposed ...
                        return;
                    }
                    this.watcher.init(true);
                    if (twoWays) {
                        this.setupTwoWayBinding();
                    }
                });
            }
            else {
                this.watcher.init(true);
                if (twoWays) {
                    this.setupTwoWayBinding();
                }
            }
        }
        setupTwoWayBinding() {
            if (this.target instanceof AtomComponent_1.AtomComponent) {
                if (this.element
                    && (this.element !== this.target.element || !this.target.hasProperty(this.name))) {
                    // most likely it has change event..
                    let events = [];
                    if (typeof this.twoWays !== "boolean") {
                        events = this.twoWays;
                    }
                    this.twoWaysDisposable = AtomBridge_1.AtomBridge.instance.watchProperty(this.element, this.name, events, (v) => {
                        this.setInverseValue(v);
                    });
                    return;
                }
            }
            const watcher = new AtomWatcher_1.AtomWatcher(this.target, [[this.name]], (...values) => {
                if (this.isTwoWaySetup) {
                    this.setInverseValue(values[0]);
                }
            });
            watcher.init(true);
            this.isTwoWaySetup = true;
            this.twoWaysDisposable = watcher;
        }
        setInverseValue(value) {
            if (!this.twoWays) {
                throw new Error("This Binding is not two ways.");
            }
            this.updaterOnce.run(() => {
                if (this.disposed) {
                    return;
                }
                const first = this.path[0];
                const length = first.length;
                let v = this.target;
                let i = 0;
                let name;
                for (i = 0; i < length - 1; i++) {
                    name = first[i].name;
                    if (name === "this") {
                        v = this.source || this.target;
                    }
                    else {
                        v = v[name];
                    }
                    if (!v) {
                        return;
                    }
                }
                name = first[i].name;
                v[name] = this.fromTargetToSource ? this.fromTargetToSource.call(this, value) : value;
            });
        }
        dispose() {
            if (this.twoWaysDisposable) {
                this.twoWaysDisposable.dispose();
                this.twoWaysDisposable = null;
            }
            this.watcher.dispose();
            this.disposed = true;
            this.watcher = null;
        }
    }
    exports.PropertyBinding = PropertyBinding;
});
//# sourceMappingURL=PropertyBinding.js.map