var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
(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/AtomLoader", "../../core/AtomUri", "../../services/NavigationService", "../../view-model/AtomWindowViewModel", "../core/AtomUI", "../styles/AtomFrameStyle", "./AtomControl"], factory);
    }
})(function (require, exports) {
    "use strict";
    Object.defineProperty(exports, "__esModule", { value: true });
    exports.AtomFrame = void 0;
    const AtomBinder_1 = require("../../core/AtomBinder");
    const AtomLoader_1 = require("../../core/AtomLoader");
    const AtomUri_1 = require("../../core/AtomUri");
    const NavigationService_1 = require("../../services/NavigationService");
    const AtomWindowViewModel_1 = require("../../view-model/AtomWindowViewModel");
    const AtomUI_1 = require("../core/AtomUI");
    const AtomFrameStyle_1 = require("../styles/AtomFrameStyle");
    const AtomControl_1 = require("./AtomControl");
    /**
     * Creates and hosts an instance of AtomControl available at given URL. Query string parameters
     * from the url will be passed to view model as initial property values.
     *
     * By default stack is turned off, so elements and controls will be destroyed when new control is hosted.
     */
    class AtomFrame extends AtomControl_1.AtomControl {
        constructor(app, e) {
            super(app, e || document.createElement("section"));
        }
        get canGoBack() {
            return this.stack.length ? true : false;
        }
        get url() {
            return this.mUrl;
        }
        set url(value) {
            if (this.mUrl === value) {
                return;
            }
            if (value === undefined) {
                return;
            }
            this.mUrl = value;
            this.runAfterInit(() => {
                this.app.runAsync(() => this.loadForReturn(value === null ? null : new AtomUri_1.AtomUri(value), true));
            });
        }
        onBackCommand() {
            return __awaiter(this, void 0, void 0, function* () {
                if (!this.stack.length) {
                    // tslint:disable-next-line: no-console
                    console.warn(`FrameStack is empty !!`);
                    return;
                }
                const ctrl = this.current;
                if (ctrl) {
                    yield this.navigationService.remove(ctrl);
                }
                // this.popStack();
            });
        }
        /**
         * This will pop page from the stack and set as current
         * @param windowClosed true if current page was closed by User Action
         */
        popStack(windowClosed) {
            if (!this.stack.length) {
                // tslint:disable-next-line: no-console
                console.warn(`FrameStack is empty !!`);
                return;
            }
            const last = this.stack.pop();
            AtomBinder_1.AtomBinder.refreshItems(this.stack);
            const old = this.current;
            this.current = last.page;
            this.current.element.style.display = "";
            if (old) {
                this.navigationService.remove(old).catch((e) => 
                // tslint:disable-next-line: no-console
                console.log(e));
            }
            this.setUrl(last.url);
            if (this.saveScrollPosition) {
                setTimeout(() => {
                    window.scrollTo(0, last.scrollY);
                }, 200);
            }
        }
        canChange() {
            const c = this.current;
            if (!c) {
                return Promise.resolve(true);
            }
            return this.navigationService.remove(c);
        }
        push(ctrl) {
            if (this.current) {
                if (this.keepStack) {
                    this.current.element.style.display = "none";
                    this.stack.add({
                        url: this.current._$_url,
                        page: this.current,
                        scrollY: this.navigationService.screen.scrollTop
                    });
                }
                else {
                    if (this.current === ctrl) {
                        return;
                    }
                    const c1 = this.current;
                    const e1 = c1.element;
                    if (e1) {
                        this.navigationService.remove(c1);
                    }
                }
            }
            const element = ctrl.element;
            const e = this.pagePresenter || this.element;
            (e).appendChild(element);
            this.current = ctrl;
            if (this.saveScrollPosition) {
                window.scrollTo(0, 0);
            }
        }
        load(url, clearHistory) {
            return __awaiter(this, void 0, void 0, function* () {
                // we will not worry if we cannot close the page or not
                // as we are moving in detail view, we will come back to page
                // without loosing anything
                if (clearHistory) {
                    if (!(yield this.canChange())) {
                        return;
                    }
                }
                const { view, disposables } = yield AtomLoader_1.AtomLoader.loadView(url, this.app, true, () => new AtomWindowViewModel_1.AtomWindowViewModel(this.app));
                const urlString = url.host ? url.toString() : url.pathAndQuery;
                view._$_url = urlString;
                this.push(view);
                const e = view.element;
                this.navigationService.currentTarget = e;
                this.setUrl(urlString);
                disposables.add({
                    dispose: () => {
                        const closed = this.current === view;
                        e.innerHTML = "";
                        e.remove();
                        this.navigationService.currentTarget = null;
                        this.popStack(closed);
                    }
                });
                return view;
            });
        }
        toUpperCase(s) {
            return s.split("-")
                .filter((t) => t)
                .map((t) => t.substr(0, 1).toUpperCase() + t.substr(1))
                .join("");
        }
        setUrl(urlString) {
            this.mUrl = urlString;
            AtomBinder_1.AtomBinder.refreshValue(this, "url");
            AtomBinder_1.AtomBinder.refreshValue(this, "canGoBack");
        }
        loadForReturn(url, clearHistory) {
            return __awaiter(this, void 0, void 0, function* () {
                const hasHistory = this.keepStack;
                this.keepStack = !clearHistory;
                if (url === null) {
                    if (hasHistory && clearHistory) {
                        this.clearStack();
                    }
                    return;
                }
                const page = yield this.load(url, clearHistory);
                if (hasHistory) {
                    if (clearHistory) {
                        this.clearStack();
                    }
                }
                try {
                    return yield page.returnPromise;
                }
                catch (ex) {
                    // this will prevent warning in chrome for unhandled exception
                    if ((ex.message ? ex.message : ex) === "cancelled") {
                        // tslint:disable-next-line: no-console
                        console.warn(ex);
                        return;
                    }
                    // throw new Error( ex.stack ? (ex + "\r\n" + ex.stack ) : ex);
                    throw ex;
                }
            });
        }
        clearStack() {
            // clear stack... irrespective of cancellation !!
            for (const iterator of this.stack) {
                const e = iterator.page.element;
                if (e) {
                    iterator.page.dispose();
                    e.innerHTML = "";
                    e.remove();
                }
            }
            this.stack.clear();
        }
        preCreate() {
            this.name = null;
            this.stack = [];
            this.keepStack = false;
            this.current = null;
            this.currentDisposable = null;
            this.saveScrollPosition = false;
            this.navigationService = this.app.resolve(NavigationService_1.NavigationService);
            this.defaultControlStyle = AtomFrameStyle_1.default;
            this.pagePresenter = null;
            this.mUrl = null;
            AtomUI_1.AtomUI.assignID(this.element);
            this.runAfterInit(() => {
                this.setPrimitiveValue(this.element, "styleClass", this.controlStyle.name);
            });
            this.backCommand = () => this.app.runAsync(() => this.onBackCommand());
            // hook navigation...
            const d = this.navigationService.registerNavigationHook((url, { target, clearHistory, cancelToken }) => {
                if (this.name) {
                    if (target !== this.name) {
                        return undefined;
                    }
                }
                else {
                    if (target !== "frame"
                        && url.protocol !== "frame:") {
                        return undefined;
                    }
                }
                if (cancelToken) {
                    cancelToken.registerForCancel(() => {
                        this.backCommand();
                    });
                }
                return this.loadForReturn(url, clearHistory);
            });
            this.registerDisposable(d);
        }
    }
    exports.AtomFrame = AtomFrame;
});
//# sourceMappingURL=AtomFrame.js.map