"use strict";
var __extends = (this && this.__extends) || function (d, b) {
    for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
    function __() { this.constructor = d; }
    d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var core_1 = require('@angular/core');
var nav_util_1 = require('./nav-util');
var nav_util_2 = require('./nav-util');
var util_1 = require('../util/util');
var view_controller_1 = require('./view-controller');
var ion_1 = require('../components/ion');
var nav_controller_1 = require('./nav-controller');
var nav_params_1 = require('./nav-params');
var swipe_back_1 = require('./swipe-back');
var NavControllerBase = (function (_super) {
    __extends(NavControllerBase, _super);
    function NavControllerBase(parent, _app, config, _keyboard, elementRef, _zone, renderer, _cfr, _gestureCtrl, _trnsCtrl, _linker) {
        _super.call(this, config, elementRef, renderer);
        this._app = _app;
        this._keyboard = _keyboard;
        this._zone = _zone;
        this._cfr = _cfr;
        this._gestureCtrl = _gestureCtrl;
        this._trnsCtrl = _trnsCtrl;
        this._linker = _linker;
        this._children = [];
        this._ids = -1;
        this._init = false;
        this._queue = [];
        this._trnsId = null;
        this._trnsTm = 0;
        this._views = [];
        this.parent = parent;
        this.config = config;
        this._sbEnabled = config.getBoolean('swipeBackEnabled');
        this._sbThreshold = config.getNumber('swipeBackThreshold', 40);
        this.id = 'n' + (++ctrlIds);
        this.viewDidLoad = new core_1.EventEmitter();
        this.viewWillEnter = new core_1.EventEmitter();
        this.viewDidEnter = new core_1.EventEmitter();
        this.viewWillLeave = new core_1.EventEmitter();
        this.viewDidLeave = new core_1.EventEmitter();
        this.viewWillUnload = new core_1.EventEmitter();
    }
    NavControllerBase.prototype.push = function (page, params, opts, done) {
        return this._queueTrns({
            insertStart: -1,
            insertViews: [nav_util_1.convertToView(this._linker, page, params)],
            opts: opts,
        }, done);
    };
    NavControllerBase.prototype.insert = function (insertIndex, page, params, opts, done) {
        return this._queueTrns({
            insertStart: insertIndex,
            insertViews: [nav_util_1.convertToView(this._linker, page, params)],
            opts: opts,
        }, done);
    };
    NavControllerBase.prototype.insertPages = function (insertIndex, insertPages, opts, done) {
        return this._queueTrns({
            insertStart: insertIndex,
            insertViews: nav_util_1.convertToViews(this._linker, insertPages),
            opts: opts,
        }, done);
    };
    NavControllerBase.prototype.pop = function (opts, done) {
        return this._queueTrns({
            removeStart: -1,
            removeCount: 1,
            opts: opts,
        }, done);
    };
    NavControllerBase.prototype.popTo = function (indexOrViewCtrl, opts, done) {
        var startIndex = view_controller_1.isViewController(indexOrViewCtrl) ? this.indexOf(indexOrViewCtrl) : util_1.isNumber(indexOrViewCtrl) ? indexOrViewCtrl : -1;
        return this._queueTrns({
            removeStart: startIndex + 1,
            removeCount: -1,
            opts: opts,
        }, done);
    };
    NavControllerBase.prototype.popToRoot = function (opts, done) {
        return this._queueTrns({
            removeStart: 1,
            removeCount: -1,
            opts: opts,
        }, done);
    };
    NavControllerBase.prototype.popAll = function () {
        var promises = [];
        for (var i = this._views.length - 1; i >= 0; i--) {
            promises.push(this.pop(null));
        }
        return Promise.all(promises);
    };
    NavControllerBase.prototype.remove = function (startIndex, removeCount, opts, done) {
        if (removeCount === void 0) { removeCount = 1; }
        return this._queueTrns({
            removeStart: startIndex,
            removeCount: removeCount,
            opts: opts,
        }, done);
    };
    NavControllerBase.prototype.setRoot = function (pageOrViewCtrl, params, opts, done) {
        return this._queueTrns({
            insertStart: 0,
            insertViews: [nav_util_1.convertToView(this._linker, pageOrViewCtrl, params)],
            removeStart: 0,
            removeCount: -1,
            opts: opts
        }, done);
    };
    NavControllerBase.prototype.setPages = function (pages, opts, done) {
        return this._queueTrns({
            insertStart: 0,
            insertViews: nav_util_1.convertToViews(this._linker, pages),
            removeStart: 0,
            removeCount: -1,
            opts: opts
        }, done);
    };
    NavControllerBase.prototype._queueTrns = function (ti, done) {
        var _this = this;
        var promise;
        var resolve = done;
        var reject = done;
        if (done === undefined) {
            promise = new Promise(function (res, rej) {
                resolve = res;
                reject = rej;
            });
        }
        ti.resolve = function (hasCompleted, isAsync, enteringName, leavingName, direction) {
            _this._trnsId = null;
            resolve && resolve(hasCompleted, isAsync, enteringName, leavingName, direction);
            _this._sbCheck();
            _this.setTransitioning(false);
            _this._nextTrns();
        };
        ti.reject = function (rejectReason, trns) {
            _this._trnsId = null;
            _this._queue.length = 0;
            while (trns) {
                if (trns.enteringView && (trns.enteringView._state !== nav_util_1.ViewState.LOADED)) {
                    trns.enteringView._destroy(_this._renderer);
                }
                if (!trns.parent)
                    break;
            }
            if (trns) {
                _this._trnsCtrl.destroy(trns.trnsId);
            }
            _this._sbCheck();
            reject && reject(false, false, rejectReason);
            _this.setTransitioning(false);
            _this._nextTrns();
        };
        if (ti.insertViews) {
            ti.insertViews = ti.insertViews.filter(function (v) { return v !== null; });
            if (!ti.insertViews.length) {
                ti.reject('invalid views to insert');
                return;
            }
        }
        else if (util_1.isPresent(ti.removeStart) && !this._views.length && !this._isPortal) {
            ti.reject('no views in the stack to be removed');
            return;
        }
        this._queue.push(ti);
        this._nextTrns();
        return promise;
    };
    NavControllerBase.prototype._nextTrns = function () {
        if (this.isTransitioning()) {
            return false;
        }
        var ti = this._queue.shift();
        if (!ti) {
            this.setTransitioning(false);
            return false;
        }
        this.setTransitioning(true, ACTIVE_TRANSITION_MAX_TIME);
        var viewsLength = this._views.length;
        var activeView = this.getActive();
        var enteringView;
        var leavingView = activeView;
        var destroyQueue = [];
        var opts = ti.opts || {};
        var resolve = ti.resolve;
        var reject = ti.reject;
        var insertViews = ti.insertViews;
        ti.resolve = ti.reject = ti.opts = ti.insertViews = null;
        var enteringRequiresTransition = false;
        var leavingRequiresTransition = false;
        if (util_1.isPresent(ti.removeStart)) {
            if (ti.removeStart < 0) {
                ti.removeStart = (viewsLength - 1);
            }
            if (ti.removeCount < 0) {
                ti.removeCount = (viewsLength - ti.removeStart);
            }
            leavingRequiresTransition = (ti.removeStart + ti.removeCount === viewsLength);
            for (var i = ti.removeStart; i <= ti.removeCount; i++) {
                destroyQueue.push(this._views[i]);
            }
            for (var i = viewsLength - 1; i >= 0; i--) {
                var view = this._views[i];
                if (destroyQueue.indexOf(view) < 0 && view !== leavingView) {
                    enteringView = view;
                    break;
                }
            }
            opts.direction = opts.direction || nav_util_1.DIRECTION_BACK;
        }
        if (insertViews) {
            if (ti.insertStart < 0 || ti.insertStart > viewsLength) {
                ti.insertStart = viewsLength;
            }
            enteringRequiresTransition = (ti.insertStart === viewsLength);
            enteringView = insertViews[insertViews.length - 1];
            if (util_1.isPresent(opts.id)) {
                enteringView.id = opts.id;
            }
            for (var i = 0; i < insertViews.length; i++) {
                var view = insertViews[i];
                var existingIndex = this._views.indexOf(view);
                if (existingIndex > -1) {
                    this._views.splice(ti.insertStart + i, 0, this._views.splice(existingIndex, 1)[0]);
                }
                else {
                    view._setNav(this);
                    view.id = this.id + '-' + (++this._ids);
                    this._views.splice(ti.insertStart + i, 0, view);
                }
            }
            if (enteringRequiresTransition) {
                opts.direction = opts.direction || nav_util_1.DIRECTION_FORWARD;
            }
        }
        for (var i = 0; i < destroyQueue.length; i++) {
            var view = destroyQueue[i];
            if (view && view !== enteringView && view !== leavingView) {
                view._willLeave();
                this.viewWillLeave.emit(view);
                this._app.viewWillLeave.emit(view);
                view._didLeave();
                this.viewDidLeave.emit(view);
                this._app.viewDidLeave.emit(view);
                view._willUnload();
                this.viewWillUnload.emit(view);
                this._app.viewWillUnload.emit(view);
            }
        }
        for (var i = 0; i < destroyQueue.length; i++) {
            var view = destroyQueue[i];
            if (view && view !== enteringView && view !== leavingView) {
                view._destroy(this._renderer);
            }
        }
        destroyQueue.length = 0;
        if (enteringRequiresTransition || leavingRequiresTransition && enteringView !== leavingView) {
            if (!opts.animation) {
                if (util_1.isPresent(ti.removeStart)) {
                    opts.animation = (leavingView || enteringView).getTransitionName(opts.direction);
                }
                else {
                    opts.animation = (enteringView || leavingView).getTransitionName(opts.direction);
                }
            }
            this._transition(enteringView, leavingView, opts, resolve, reject);
        }
        else {
            resolve(true, false);
        }
        return true;
    };
    NavControllerBase.prototype._transition = function (enteringView, leavingView, opts, resolve, reject) {
        var _this = this;
        this._trnsId = this._trnsCtrl.getRootTrnsId(this);
        if (this._trnsId === null) {
            this._trnsId = this._trnsCtrl.nextId();
        }
        var animationOpts = {
            animation: opts.animation,
            direction: opts.direction,
            duration: (opts.animate === false ? 0 : opts.duration),
            easing: opts.easing,
            isRTL: this.config.platform.isRTL(),
            ev: opts.ev,
        };
        var trns = this._trnsCtrl.get(this._trnsId, enteringView, leavingView, animationOpts);
        this._sbTrns && this._sbTrns.destroy();
        if (trns.parent) {
            trns.parent.hasChildTrns = true;
        }
        else {
            if (opts.progressAnimation) {
                this._sbTrns = trns;
            }
        }
        trns.registerStart(function () {
            _this._trnsStart(trns, enteringView, leavingView, opts, resolve);
            if (trns.parent) {
                trns.parent.start();
            }
        });
        if (enteringView && util_1.isBlank(enteringView._state)) {
            this._viewInit(trns, enteringView, opts);
        }
        var shouldContinue = this._viewTest(trns, enteringView, leavingView, opts, resolve, reject);
        if (shouldContinue) {
            this._postViewInit(trns, enteringView, leavingView, opts, resolve);
        }
    };
    NavControllerBase.prototype._viewInit = function (trns, enteringView, opts) {
        var componentProviders = core_1.ReflectiveInjector.resolve([
            { provide: nav_controller_1.NavController, useValue: this },
            { provide: view_controller_1.ViewController, useValue: enteringView },
            { provide: nav_params_1.NavParams, useValue: enteringView.getNavParams() }
        ]);
        var componentFactory = this._cfr.resolveComponentFactory(enteringView.component);
        var childInjector = core_1.ReflectiveInjector.fromResolvedProviders(componentProviders, this._viewport.parentInjector);
        enteringView.init(componentFactory.create(childInjector, []));
        enteringView._state = nav_util_1.ViewState.INITIALIZED;
    };
    NavControllerBase.prototype._viewTest = function (trns, enteringView, leavingView, opts, resolve, reject) {
        var _this = this;
        var promises = [];
        if (leavingView) {
            var leavingTestResult = leavingView._lifecycleTest('Leave');
            if (util_1.isPresent(leavingTestResult) && leavingTestResult !== true) {
                if (leavingTestResult instanceof Promise) {
                    promises.push(leavingTestResult);
                }
                else {
                    reject((leavingTestResult !== false ? leavingTestResult : "ionViewCanLeave rejected"), trns);
                    return false;
                }
            }
        }
        if (enteringView) {
            var enteringTestResult = enteringView._lifecycleTest('Enter');
            if (util_1.isPresent(enteringTestResult) && enteringTestResult !== true) {
                if (enteringTestResult instanceof Promise) {
                    promises.push(enteringTestResult);
                }
                else {
                    reject((enteringTestResult !== false ? enteringTestResult : "ionViewCanEnter rejected"), trns);
                    return false;
                }
            }
        }
        if (promises.length) {
            Promise.all(promises).then(function () {
                _this._postViewInit(trns, enteringView, leavingView, opts, resolve);
            }, function (rejectReason) {
                reject(rejectReason, trns);
            }).catch(function (rejectReason) {
                reject(rejectReason, trns);
            });
            return false;
        }
        return true;
    };
    NavControllerBase.prototype._postViewInit = function (trns, enteringView, leavingView, opts, resolve) {
        if (enteringView && enteringView._state === nav_util_1.ViewState.INITIALIZED) {
            this._viewInsert(enteringView, enteringView._cmp, this._viewport);
        }
        if (!trns.hasChildTrns) {
            trns.start();
        }
    };
    NavControllerBase.prototype._viewInsert = function (view, componentRef, viewport) {
        view._didLoad();
        this.viewDidLoad.emit(view);
        this._app.viewDidLoad.emit(view);
        viewport.insert(componentRef.hostView, viewport.length);
        view._state = nav_util_1.ViewState.PRE_RENDERED;
        var pageElement = componentRef.location.nativeElement;
        if (view._cssClass) {
            this._renderer.setElementClass(pageElement, view._cssClass, true);
        }
        var cssClassName = util_1.pascalCaseToDashCase(view.component.name);
        this._renderer.setElementClass(pageElement, cssClassName, true);
        componentRef.changeDetectorRef.detectChanges();
    };
    NavControllerBase.prototype._trnsStart = function (trns, enteringView, leavingView, opts, resolve) {
        var _this = this;
        this._trnsId = null;
        nav_util_2.setZIndex(this, enteringView, leavingView, opts.direction, this._renderer);
        enteringView && enteringView._domShow(true, this._renderer);
        if (leavingView) {
            leavingView._domShow(true, this._renderer);
        }
        trns.init();
        if ((!this._init && this._views.length === 1 && !this._isPortal) || this.config.get('animate') === false) {
            opts.animate = false;
        }
        if (opts.animate === false) {
            trns.duration(0);
        }
        trns.beforeAddRead(function () {
            _this._zone.run(_this._viewsWillLifecycles.bind(_this, enteringView, leavingView));
        });
        trns.onFinish(function () {
            _this._zone.run(_this._trnsFinish.bind(_this, trns, opts, resolve));
        });
        var duration = trns.getDuration();
        this.setTransitioning(true, duration);
        if (!trns.parent) {
            if (duration > DISABLE_APP_MINIMUM_DURATION) {
                this._app.setEnabled(false, duration);
            }
            if (opts.progressAnimation) {
                trns.progressStart();
            }
            else {
                trns.play();
            }
        }
    };
    NavControllerBase.prototype._viewsWillLifecycles = function (enteringView, leavingView) {
        if (enteringView) {
            enteringView._willEnter();
            this.viewWillEnter.emit(enteringView);
            this._app.viewWillEnter.emit(enteringView);
        }
        if (leavingView) {
            leavingView._willLeave();
            this.viewWillLeave.emit(leavingView);
            this._app.viewWillLeave.emit(leavingView);
        }
    };
    NavControllerBase.prototype._trnsFinish = function (trns, opts, resolve) {
        var hasCompleted = trns.hasCompleted;
        var enteringName;
        var leavingName;
        if (hasCompleted) {
            if (trns.enteringView) {
                enteringName = trns.enteringView.name;
                trns.enteringView._didEnter();
                this.viewDidEnter.emit(trns.enteringView);
                this._app.viewDidEnter.emit(trns.enteringView);
            }
            if (trns.leavingView) {
                leavingName = trns.leavingView.name;
                trns.leavingView._didLeave();
                this.viewDidLeave.emit(trns.leavingView);
                this._app.viewDidLeave.emit(trns.leavingView);
            }
            this._cleanup(trns.enteringView);
        }
        if (!trns.parent) {
            this._trnsCtrl.destroy(trns.trnsId);
            this._app.setEnabled(true);
            if (opts.updateUrl !== false) {
                this._linker.navChange(opts.direction);
            }
            if (opts.keyboardClose !== false && this._keyboard.isOpen()) {
                this._keyboard.close();
            }
        }
        resolve(hasCompleted, true, enteringName, leavingName, opts.direction);
    };
    NavControllerBase.prototype._cleanup = function (activeView) {
        var _this = this;
        var activeViewIndex = this.indexOf(activeView);
        var reorderZIndexes = false;
        for (var i = this._views.length - 1; i >= 0; i--) {
            var view = this._views[i];
            if (i > activeViewIndex) {
                view._willUnload();
                this.viewWillUnload.emit(view);
                this._app.viewWillUnload.emit(view);
                view._destroy(this._renderer);
            }
            else if (i < activeViewIndex && !this._isPortal) {
                view._domShow(false, this._renderer);
            }
            if (view._zIndex <= 0) {
                reorderZIndexes = true;
            }
        }
        if (!this._isPortal) {
            if (reorderZIndexes) {
                this._views.forEach(function (view) {
                    view._setZIndex(view._zIndex + nav_util_1.INIT_ZINDEX + 1, _this._renderer);
                });
            }
        }
    };
    NavControllerBase.prototype.getActiveChildNav = function () {
        return this._children[this._children.length - 1];
    };
    NavControllerBase.prototype.registerChildNav = function (nav) {
        this._children.push(nav);
    };
    NavControllerBase.prototype.unregisterChildNav = function (nav) {
        var index = this._children.indexOf(nav);
        if (index > -1) {
            this._children.splice(index, 1);
        }
    };
    NavControllerBase.prototype.destroy = function () {
        for (var i = this._views.length - 1; i >= 0; i--) {
            this._views[i]._willUnload();
            this._views[i]._destroy(this._renderer);
        }
        this._views.length = 0;
        this._sbGesture && this._sbGesture.destroy();
        this._sbTrns && this._sbTrns.destroy();
        this._sbGesture = this._sbTrns = null;
        if (this.parent && this.parent.unregisterChildNav) {
            this.parent.unregisterChildNav(this);
        }
    };
    NavControllerBase.prototype.swipeBackStart = function () {
        if (this.isTransitioning() || this._queue.length > 0)
            return;
        var opts = {
            direction: nav_util_1.DIRECTION_BACK,
            progressAnimation: true
        };
        this._queueTrns({
            removeStart: -1,
            removeCount: 1,
            opts: opts,
        }, null);
    };
    NavControllerBase.prototype.swipeBackProgress = function (stepValue) {
        if (this._sbTrns && this._sbGesture) {
            this._app.setEnabled(false, ACTIVE_TRANSITION_MAX_TIME);
            this.setTransitioning(true, ACTIVE_TRANSITION_MAX_TIME);
            this._sbTrns.progressStep(stepValue);
        }
    };
    NavControllerBase.prototype.swipeBackEnd = function (shouldComplete, currentStepValue) {
        if (this._sbTrns && this._sbGesture) {
            this._sbTrns.progressEnd(shouldComplete, currentStepValue);
        }
    };
    NavControllerBase.prototype._sbCheck = function () {
        var _this = this;
        if (this._sbEnabled && !this._isPortal) {
            if (!this._sbGesture) {
                var opts = {
                    edge: 'left',
                    threshold: this._sbThreshold
                };
                this._sbGesture = new swipe_back_1.SwipeBackGesture(this.getNativeElement(), opts, this, this._gestureCtrl);
            }
            if (this.canSwipeBack()) {
                if (!this._sbGesture.isListening) {
                    this._zone.runOutsideAngular(function () {
                        console.debug('swipeBack gesture, listen');
                        _this._sbGesture.listen();
                    });
                }
            }
            else if (this._sbGesture.isListening) {
                console.debug('swipeBack gesture, unlisten');
                this._sbGesture.unlisten();
            }
        }
    };
    NavControllerBase.prototype.canSwipeBack = function () {
        return (this._sbEnabled &&
            !this._children.length &&
            !this.isTransitioning() &&
            this._app.isEnabled() &&
            this.canGoBack());
    };
    NavControllerBase.prototype.canGoBack = function () {
        var activeView = this.getActive();
        return !!(activeView && activeView.enableBack()) || false;
    };
    NavControllerBase.prototype.isTransitioning = function () {
        return (this._trnsTm > Date.now());
    };
    NavControllerBase.prototype.setTransitioning = function (isTransitioning, durationPadding) {
        if (durationPadding === void 0) { durationPadding = 2000; }
        this._trnsTm = (isTransitioning ? Date.now() + durationPadding : 0);
    };
    NavControllerBase.prototype.getActive = function () {
        return this._views[this._views.length - 1];
    };
    NavControllerBase.prototype.isActive = function (view) {
        return (view === this.getActive());
    };
    NavControllerBase.prototype.getByIndex = function (index) {
        return this._views[index];
    };
    NavControllerBase.prototype.getPrevious = function (view) {
        if (!view) {
            view = this.getActive();
        }
        return this._views[this.indexOf(view) - 1];
    };
    NavControllerBase.prototype.first = function () {
        return this._views[0];
    };
    NavControllerBase.prototype.last = function () {
        return this._views[this._views.length - 1];
    };
    NavControllerBase.prototype.indexOf = function (view) {
        return this._views.indexOf(view);
    };
    NavControllerBase.prototype.length = function () {
        return this._views.length;
    };
    NavControllerBase.prototype.isSwipeBackEnabled = function () {
        return this._sbEnabled;
    };
    NavControllerBase.prototype.dismissPageChangeViews = function () {
        for (var i = 0; i < this._views.length; i++) {
            var view = this._views[i];
            if (view.data && view.data.dismissOnPageChange) {
                view.dismiss();
            }
        }
    };
    NavControllerBase.prototype.setViewport = function (val) {
        this._viewport = val;
    };
    Object.defineProperty(NavControllerBase.prototype, "rootNav", {
        get: function () {
            console.warn('nav.rootNav() has been deprecated, please use app.getRootNav() instead');
            return this._app.getRootNav();
        },
        enumerable: true,
        configurable: true
    });
    NavControllerBase.prototype.present = function () {
        console.warn('nav.present() has been deprecated.\n' +
            'Please inject the overlay\'s controller and use the present method on the instance instead.');
        return Promise.resolve();
    };
    return NavControllerBase;
}(ion_1.Ion));
exports.NavControllerBase = NavControllerBase;
var ctrlIds = -1;
var DISABLE_APP_MINIMUM_DURATION = 64;
var ACTIVE_TRANSITION_MAX_TIME = 5000;
