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);
};
(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", "./core/AtomBinder", "./core/AtomDispatcher", "./di/RegisterSingleton", "./di/ServiceProvider", "./services/BusyIndicatorService"], factory);
    }
})(function (require, exports) {
    "use strict";
    var App_1;
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.App = exports.AtomMessageAction = void 0;
    const AtomBinder_1 = require("./core/AtomBinder");
    const AtomDispatcher_1 = require("./core/AtomDispatcher");
    const RegisterSingleton_1 = require("./di/RegisterSingleton");
    const ServiceProvider_1 = require("./di/ServiceProvider");
    const BusyIndicatorService_1 = require("./services/BusyIndicatorService");
    class AtomHandler {
        constructor(message) {
            this.message = message;
            this.list = new Array();
        }
    }
    class AtomMessageAction {
        constructor(msg, a) {
            this.message = msg;
            this.action = a;
        }
    }
    exports.AtomMessageAction = AtomMessageAction;
    let App = App_1 = class App extends ServiceProvider_1.ServiceProvider {
        constructor() {
            super(null);
            /**
             * This must be set explicitly as it can be used outside to detect
             * if app is ready. This will not be set automatically by framework.
             */
            this.appReady = false;
            this.busyIndicators = [];
            // tslint:disable-next-line:ban-types
            this.readyHandlers = [];
            this.onError = (error) => {
                // tslint:disable-next-line:no-console
                console.log(error);
            };
            this.screen = {};
            this.bag = {};
            this.put(App_1, this);
            this.dispatcher = new AtomDispatcher_1.AtomDispatcher();
            this.dispatcher.start();
            this.put(AtomDispatcher_1.AtomDispatcher, this.dispatcher);
            setTimeout(() => {
                this.invokeReady();
            }, 5);
        }
        get url() {
            return this.mUrl;
        }
        set url(v) {
            this.mUrl = v;
            AtomBinder_1.AtomBinder.refreshValue(this, "url");
        }
        get contextId() {
            return "none";
        }
        createBusyIndicator() {
            this.busyIndicatorService = this.busyIndicatorService
                || this.resolve(BusyIndicatorService_1.BusyIndicatorService);
            return this.busyIndicatorService.createIndicator();
        }
        syncUrl() {
            // must be implemented by platform specific app
        }
        callLater(f) {
            this.dispatcher.callLater(f);
        }
        updateDefaultStyle(content) {
            throw new Error("Platform does not support StyleSheets");
        }
        waitForPendingCalls() {
            return this.dispatcher.waitForAll();
        }
        /**
         * This method will run any asynchronous method
         * and it will display an error if it will fail
         * asynchronously
         *
         * @template T
         * @param {() => Promise<T>} tf
         * @memberof AtomDevice
         */
        runAsync(tf) {
            try {
                const p = tf();
                if (p && p.then && p.catch) {
                    p.catch((error) => {
                        this.onError("runAsync");
                        this.onError(error);
                    });
                }
            }
            catch (e) {
                this.onError("runAsync");
                this.onError(e);
            }
        }
        /**
         * Broadcast given data to channel, only within the current window.
         *
         * @param {string} channel
         * @param {*} data
         * @returns
         * @memberof AtomDevice
         */
        broadcast(channel, data) {
            const ary = this.bag[channel];
            if (!ary) {
                return;
            }
            for (const entry of ary.list) {
                entry.call(this, channel, data);
            }
        }
        /**
         * Subscribe for given channel with action that will be
         * executed when anyone will broadcast (this only works within the
         * current browser window)
         *
         * This method returns a disposable, when you call `.dispose()` it will
         * unsubscribe for current subscription
         *
         * @param {string} channel
         * @param {AtomAction} action
         * @returns {AtomDisposable} Disposable that supports removal of subscription
         * @memberof AtomDevice
         */
        subscribe(channel, action) {
            let ary = this.bag[channel];
            if (!ary) {
                ary = new AtomHandler(channel);
                this.bag[channel] = ary;
            }
            ary.list.push(action);
            return {
                dispose: () => {
                    ary.list = ary.list.filter((a) => a !== action);
                    if (!ary.list.length) {
                        this.bag[channel] = null;
                    }
                }
            };
        }
        main() {
            // load app here..
        }
        // tslint:disable-next-line:no-empty
        onReady(f) {
            if (this.readyHandlers) {
                this.readyHandlers.push(f);
            }
            else {
                this.invokeReadyHandler(f);
            }
        }
        invokeReady() {
            for (const iterator of this.readyHandlers) {
                this.invokeReadyHandler(iterator);
            }
            this.readyHandlers = null;
        }
        // tslint:disable-next-line:ban-types
        invokeReadyHandler(f) {
            const indicator = this.createBusyIndicator();
            const a = f();
            if (a && a.then && a.catch) {
                a.then((r) => {
                    // do nothing
                    indicator.dispose();
                });
                a.catch((e) => {
                    indicator.dispose();
                    // tslint:disable-next-line:no-console
                    // console.error("XFApp.onReady");
                    // tslint:disable-next-line:no-console
                    console.error(typeof e === "string" ? e : JSON.stringify(e));
                });
            }
        }
    };
    App = App_1 = __decorate([
        RegisterSingleton_1.RegisterSingleton,
        __metadata("design:paramtypes", [])
    ], App);
    exports.App = App;
});
//# sourceMappingURL=App.js.map