import { Injectable, OpaqueToken } from '@angular/core';
import { Location, LocationStrategy, HashLocationStrategy } from '@angular/common';
import { App } from '../components/app/app';
import { convertToViews, isNav, isTab, isTabs, DIRECTION_BACK } from './nav-util';
import { isArray, isPresent } from '../util/util';
import { UrlSerializer } from './url-serializer';
import { ViewController } from './view-controller';
export var DeepLinker = (function () {
    function DeepLinker(app, serializer, location) {
        this.app = app;
        this.serializer = serializer;
        this.location = location;
        this.segments = [];
        this.history = [];
    }
    DeepLinker.prototype.init = function () {
        var _this = this;
        var browserUrl = normalizeUrl(this.location.path());
        console.debug("DeepLinker, init load: " + browserUrl);
        this.segments = this.serializer.parse(browserUrl);
        this.historyPush(browserUrl);
        this.location.subscribe(function (locationChg) {
            _this.urlChange(normalizeUrl(locationChg.url));
        });
    };
    DeepLinker.prototype.urlChange = function (browserUrl) {
        if (!this.isCurrentUrl(browserUrl)) {
            if (this.isBackUrl(browserUrl)) {
                console.debug("DeepLinker, browser urlChange, back to: " + browserUrl);
                this.historyPop();
            }
            else {
                console.debug("DeepLinker, browser urlChange, forward to: " + browserUrl);
                this.historyPush(browserUrl);
            }
            var appRootNav = this.app.getRootNav();
            if (appRootNav) {
                if (browserUrl === '/') {
                    if (isPresent(this.indexAliasUrl)) {
                        browserUrl = this.indexAliasUrl;
                    }
                    else {
                        appRootNav.goToRoot({
                            updateUrl: false,
                            isNavRoot: true
                        });
                        return;
                    }
                }
                this.segments = this.serializer.parse(browserUrl);
                this.loadNavFromPath(appRootNav);
            }
        }
    };
    DeepLinker.prototype.navChange = function (direction) {
        if (direction) {
            var activeNav = this.app.getActiveNav();
            if (activeNav) {
                this.segments = this.pathFromNavs(activeNav);
                var browserUrl = this.serializer.serialize(this.segments);
                this.updateLocation(browserUrl, direction);
            }
        }
    };
    DeepLinker.prototype.updateLocation = function (browserUrl, direction) {
        if (this.indexAliasUrl === browserUrl) {
            browserUrl = '/';
        }
        if (direction === DIRECTION_BACK && this.isBackUrl(browserUrl)) {
            console.debug("DeepLinker, location.back(), url: '" + browserUrl + "'");
            this.historyPop();
            this.location.back();
        }
        else if (!this.isCurrentUrl(browserUrl)) {
            console.debug("DeepLinker, location.go('" + browserUrl + "')");
            this.historyPush(browserUrl);
            this.location.go(browserUrl);
        }
    };
    DeepLinker.prototype.getComponentFromName = function (componentName) {
        var segment = this.serializer.createSegmentFromName(componentName);
        if (segment && segment.component) {
            return segment.component;
        }
        return null;
    };
    DeepLinker.prototype.createUrl = function (nav, nameOrComponent, data, prepareExternalUrl) {
        if (prepareExternalUrl === void 0) { prepareExternalUrl = true; }
        var segment = this.serializer.createSegmentFromName(nameOrComponent);
        if (segment) {
            var path = this.pathFromNavs(nav, segment.component, data);
            var url = this.serializer.serialize(path);
            return prepareExternalUrl ? this.location.prepareExternalUrl(url) : url;
        }
        return '';
    };
    DeepLinker.prototype.pathFromNavs = function (nav, component, data) {
        var segments = [];
        var view;
        var segment;
        var tabSelector;
        while (nav) {
            if (!component && isNav(nav)) {
                view = nav.getActive(true);
                if (view) {
                    component = view.component;
                    data = view.data;
                }
            }
            segment = this.serializer.serializeComponent(component, data);
            component = data = null;
            if (!segment) {
                break;
            }
            segments.push(segment);
            if (isTab(nav)) {
                tabSelector = this.getTabSelector(nav);
                segments.push({
                    id: tabSelector,
                    name: tabSelector,
                    component: null,
                    data: null
                });
                nav = nav.parent && nav.parent.parent;
            }
            else {
                nav = nav.parent;
            }
        }
        return segments.reverse();
    };
    DeepLinker.prototype.getTabSelector = function (tab) {
        if (isPresent(tab.tabUrlPath)) {
            return tab.tabUrlPath;
        }
        if (isPresent(tab.tabTitle)) {
            return this.serializer.formatUrlPart(tab.tabTitle);
        }
        return "tab-" + tab.index;
    };
    DeepLinker.prototype.getSelectedTabIndex = function (tabsNav, pathName, fallbackIndex) {
        var _this = this;
        if (fallbackIndex === void 0) { fallbackIndex = 0; }
        var indexMatch = pathName.match(/tab-(\d+)/);
        if (indexMatch) {
            return parseInt(indexMatch[1], 10);
        }
        var tab = tabsNav._tabs.find(function (t) {
            return (isPresent(t.tabUrlPath) && t.tabUrlPath === pathName) ||
                (isPresent(t.tabTitle) && _this.serializer.formatUrlPart(t.tabTitle) === pathName);
        });
        return isPresent(tab) ? tab.index : fallbackIndex;
    };
    DeepLinker.prototype.initNav = function (nav) {
        var path = this.segments;
        if (nav && path.length) {
            if (!nav.parent) {
                path[0].navId = nav.id;
                return path[0];
            }
            for (var i = 1; i < path.length; i++) {
                if (path[i - 1].navId === nav.parent.id) {
                    path[i].navId = nav.id;
                    return path[i];
                }
            }
        }
        return null;
    };
    DeepLinker.prototype.initViews = function (segment) {
        var views;
        if (isArray(segment.defaultHistory)) {
            views = convertToViews(this, segment.defaultHistory);
        }
        else {
            views = [];
        }
        var view = new ViewController(segment.component, segment.data);
        view.id = segment.id;
        views.push(view);
        return views;
    };
    DeepLinker.prototype.loadNavFromPath = function (nav, done) {
        var _this = this;
        if (!nav) {
            done && done();
        }
        else {
            this.loadViewFromSegment(nav, function () {
                _this.loadNavFromPath(nav.getActiveChildNav(), done);
            });
        }
    };
    DeepLinker.prototype.loadViewFromSegment = function (navInstance, done) {
        var segment = this.initNav(navInstance);
        if (!segment) {
            done();
            return;
        }
        if (isTabs(navInstance)) {
            navInstance.select(this.getSelectedTabIndex(navInstance, segment.name), {
                updateUrl: false,
                animate: false
            });
            done();
            return;
        }
        var nav = navInstance;
        var view;
        var count = nav.length() - 1;
        for (var i = count; i >= 0; i--) {
            view = nav.getByIndex(i);
            if (view && view.id === segment.id) {
                if (i === count) {
                    done();
                }
                else {
                    nav.popTo(view, {
                        animate: false,
                        updateUrl: false,
                    }, done);
                }
                return;
            }
        }
        nav.push(segment.component, segment.data, {
            id: segment.id, animate: false, updateUrl: false
        }, done);
    };
    DeepLinker.prototype.isBackUrl = function (browserUrl) {
        return (browserUrl === this.history[this.history.length - 2]);
    };
    DeepLinker.prototype.isCurrentUrl = function (browserUrl) {
        return (browserUrl === this.history[this.history.length - 1]);
    };
    DeepLinker.prototype.historyPush = function (browserUrl) {
        if (!this.isCurrentUrl(browserUrl)) {
            this.history.push(browserUrl);
            if (this.history.length > 30) {
                this.history.shift();
            }
        }
    };
    DeepLinker.prototype.historyPop = function () {
        this.history.pop();
        if (!this.history.length) {
            this.historyPush(this.location.path());
        }
    };
    DeepLinker.decorators = [
        { type: Injectable },
    ];
    DeepLinker.ctorParameters = [
        { type: App, },
        { type: UrlSerializer, },
        { type: Location, },
    ];
    return DeepLinker;
}());
export function setupDeepLinker(app, serializer, location) {
    var deepLinker = new DeepLinker(app, serializer, location);
    deepLinker.init();
    return deepLinker;
}
export var UserDeepLinkConfig = new OpaqueToken('USERLINKS');
export function provideDeepLinker(userDeepLinkConfig) {
    return [
        UrlSerializer,
        Location,
        { provide: UserDeepLinkConfig, useValue: userDeepLinkConfig },
        { provide: LocationStrategy, useClass: HashLocationStrategy },
        {
            provide: DeepLinker,
            useFactory: setupDeepLinker,
            deps: [
                App,
                UrlSerializer,
                Location
            ]
        },
    ];
}
export function normalizeUrl(browserUrl) {
    browserUrl = browserUrl.trim();
    if (browserUrl.charAt(0) !== '/') {
        browserUrl = '/' + browserUrl;
    }
    if (browserUrl.length > 1 && browserUrl.charAt(browserUrl.length - 1) === '/') {
        browserUrl = browserUrl.substr(0, browserUrl.length - 1);
    }
    return browserUrl;
}
