/* global Vue, __appInstance */
import router from '../ide/router';

import NavigationBar from '../dev/components/NavigationBar/index';

import App from '../ide/App.vue';

// import '@/src/app.js';
import '@/src/app.less';
import appJson from '@/src/app.json';


const defaultRemRatio = 20;

if (appJson.setRem) {
    const remRatio = normalizeRemRatio(appJson.setRem);

    setRem(remRatio);
    window.onresize = function () {
        setRem(remRatio);
    };
}

// 设置 rem 函数
function setRem (remRatio = defaultRemRatio) {
    let htmlWidth = document.documentElement.clientWidth || document.body.clientWidth;

    if (htmlWidth === 0) {
        setTimeout(() => {
            setRem(remRatio);
        }, 50);
        return;
    }

    let htmlDom = document.getElementsByTagName('html')[0];
    htmlDom.style.fontSize = htmlWidth / remRatio + 'px';
}

function normalizeRemRatio (remRatio) {
    if (typeof remRatio === 'boolean') {
        return defaultRemRatio;
    }

    if (Number.isNaN(+remRatio) || remRatio <= 0) {
        return defaultRemRatio;
    }

    return remRatio;
}


const handleNavigationBar = (pathname) => {
    let { tabBar, tabBarMap = {}, pagesMap = {}, window:globalconfig, navigationInstance } = window.__blConfig || {},       
        selected = pagesMap[pathname];

    const hasNavigation = (
        (tabBar && !!tabBarMap[pathname]) || // 是tabBar
        selected.hasNavigationBar || // 页面设置
        (globalconfig && globalconfig.hasNavigationBar && selected.hasNavigationBar === undefined) // window设置
    );

    let style = '';

    if (hasNavigation) {
        if (navigationInstance) {
            navigationInstance.visible = true;
            navigationInstance.selected = selected;

            const instances = window.bl.getCurrentPages()[0].$children,
                fromInstance = instances.some(ele => ele.__route === originPath);
            navigationInstance.backButtonVisible = !(
                (tabBar && !!tabBarMap[pathname]) || // 是tabBar
                (instances.length === 1) || // 同一个地址
                (tabBar && tabBarMap[originPath] && !fromInstance) // 上一个页面是tabBar且执行了redirect
            );

        } else {
            window.__blConfig.navigationInstance = NavigationBar({
                config: window.__blConfig,
                selected
            });
        }

        style = selected.navigationBarTextStyle || globalconfig.navigationBarTextStyle || 'white';
        
    } else {
        if (navigationInstance) {
            navigationInstance.visible = false;
        }
        style = 'black';
    }

    window.Service.callNative('setNavigationBarColor', JSON.stringify({style}));
    window.bl.setStatusBarStyle({style});

    const event = new Event('resize-content-height');
    document.dispatchEvent(event);
};

Vue.directive('bili', {
    bind(el, binding) {
        let lastClickTime = 0;
        let clickTimer = null;
        let arg = binding.arg;
        let value = binding.value;
        let modifiers = binding.modifiers;

        if (!arg) {
            console.warn('[vue-bili] event type argument is required');
        }

        if (!value) return;

        if (!value.params) {
            value.params = [];
        }

        let startTx, startTy, endTx, endTy;

        el.addEventListener('touchstart', (e) => {
            let touch = e.touches[0];
            startTx = touch.clientX;
            startTy = touch.clientY;
        }, false);

        el.addEventListener('touchend', (e) => {
            let touch = e.changedTouches[0];
            endTx = touch.clientX;
            endTy = touch.clientY;

            if (Math.abs(startTx - endTx) < 6 && Math.abs(startTy - endTy) < 6) {
                if (arg === 'tap') {
                    if (typeof value === 'function') {
                        value();
                    } else if (typeof value == 'object') {
                        if (value.event) {
                            value.method(e, ...value.params);
                        } else {
                            value.method(...value.params);
                        }
                    }
                }

                let nowTime = new Date().getTime();
                if (nowTime - lastClickTime < 300) {
                    /* 双击 */
                    lastClickTime = 0;
                    clickTimer && clearTimeout(clickTimer);

                    if (arg === 'dbclick') {
                        if (typeof value === 'function') {
                            value();
                        } else if (typeof value == 'object') {
                            if (value.event) {
                                value.method(e, ...value.params);
                            } else {
                                value.method(...value.params);
                            }
                        }
                    }
                } else {
                    /* 单击 */
                    lastClickTime = nowTime;
                    clickTimer = setTimeout(() => {
                        if (arg === 'click') {
                            if (typeof value === 'function') {
                                value();
                            } else if (typeof value == 'object') {
                                if (value.event) {
                                    value.method(e, ...value.params);
                                } else {
                                    value.method(...value.params);
                                }
                            }
                        }
                    }, 300);
                }

                if (arg === 'tap' || arg === 'click' || arg === 'dbclick') {
                    if (modifiers && modifiers.stop) {
                        e.stopPropagation();
                    } else if (modifiers && modifiers.prevent) {
                        e.preventDefault();
                    }
                }
            }
        }, false);
    }
});

Vue.mixin({ // 页面级事件
    data() {
        return {
            tabClickInfo: null,
            maxHeight: 90,
            refreshHeight: 50,
            triggerFresh: false,
            startPos: 0,
            moveHeight: 0,
            init: true,
            preScrollTop: 0,
            flag: true,
            __route: '',
            __origin: ''
        };
    },
    enablePullDownRefresh: false,
    enableScroll: -1,
    enableBounce: 0,
    created() {
        this.__route = window.__blConfig.currentPath;
    },
    activated() {
        if (!this.$parent || this.$parent.$el.id !== 'app') {
            return;
        }

        window.bl.showTabBar({
            fail() {
                // 进入非tab页，隐藏tabBar
                const { tabBar = {} } = window.__blConfig || {};
                if (tabBar.instance) {
                    tabBar.instance.visible = false;
                }
            }
        });
        handleNavigationBar(this.__route);


        //点击tab触发的页面切换
        if (window.app.tabClickInfo) {
            this.$options.onTabItemTap && this.$options.onTabItemTap(window.app.tabClickInfo);
        }
        window.app.tabClickInfo = null;

        const { pagesMap = {}, window:globalConfig = {} } = window.__blConfig;
        const pageInfo = pagesMap[this.$route.path];

        window.app.$refs.bl__app.$el.style['background-color'] = pageInfo.backgroundColor || globalConfig.backgroundColor || '#fff';

        this.$el.scrollTop = this.$route.meta.scrollTop || 0;

        this.startScroll();
        this.startPullDownrefresh();
    },
    deactivated() {
        if (!this.$parent || this.$parent.$el.id !== 'app') {
            return;
        }
        this.$el.onscroll = null;
        this.stopScroll();
        this.stopPullDownrefresh();
    },
    methods: {
        touchStart(e) {
            this.startPos = e.touches[0].pageY;
        },
        touchMove(e) {
            this.moveHeight = e.touches[0].pageY - this.startPos;
            if (this.moveHeight > 0 && this.moveHeight <= this.maxHeight) {
                this.$el.style.paddingTop = `${this.moveHeight}px`;
            }        
        },
        touchEnd() {
            if (parseInt(this.$el.style.paddingTop, 10) < this.refreshHeight) { // 小于可刷新的高度
                this.$el.style.paddingTop = '0';
                return;
            }
            this.$options.onPullDownRefresh && this.$options.onPullDownRefresh();

            if (this.triggerFresh) {
                this.triggerFresh = false;
                return;
            }
            const vm = this;
            setTimeout(function() {
                vm.$el.style.paddingTop = '0';
            }, 1000);
        },
        startPullDownrefresh() {
            const { pagesMap = {}, window:globalConfig = {} } = window.__blConfig;
            const pageInfo = pagesMap[this.$route.path];

            if (
                pageInfo.enablePullDownRefresh ||
                (globalConfig.enablePullDownRefresh && pageInfo.enablePullDownRefresh == undefined) ||
                (globalConfig.enablePullDownRefresh == undefined && pageInfo.enablePullDownRefresh == undefined && this.$options.enablePullDownRefresh)
            ) {
                this.$el.addEventListener('touchstart', this.touchStart, false);
                this.$el.addEventListener('touchmove', this.touchMove, false);
                this.$el.addEventListener('touchend', this.touchEnd, false);
            }

        },
        stopPullDownrefresh() {
            this.$el.removeEventListener('touchstart', this.touchStart, false);
            this.$el.removeEventListener('touchmove', this.touchMove, false);
            this.$el.removeEventListener('touchend', this.touchEnd, false);
        },
        scroll(e) {
            
            const { pagesMap = {}, window:globalConfig = {} } = window.__blConfig,
                pageInfo = pagesMap[this.$route.path],
                onReachBottomDistance = pageInfo.onReachBottomDistance || globalConfig.onReachBottomDistance || 50,
                enableScrollEvent = pageInfo.enableScrollEvent || (globalConfig.enableScrollEvent && pageInfo.enableScrollEvent == undefined);
            
            const { offsetHeight, scrollTop, scrollHeight } = e.srcElement;

            if (this.init && scrollTop === this.$el.scrollTop) {
                this.init = false;
                return;
            }

            if (scrollTop > this.preScrollTop) { // 向下滚动
                if (this.flag && (offsetHeight + scrollTop + onReachBottomDistance) >= scrollHeight) { // 第一次触底
                    enableScrollEvent && this.$options.onReachBottom && this.$options.onReachBottom();
                    this.flag = false;
                }
            } else { // 出底
                if (!this.flag && (scrollTop >= onReachBottomDistance || scrollTop === 0)) {
                    this.flag = true;
                }
            }

            this.preScrollTop = scrollTop;
            
            enableScrollEvent && this.$options.onPageScroll && this.$options.onPageScroll({
                scrollTop: e.srcElement.scrollTop
            });
        },
        startScroll() {
            const { pagesMap = {}, window:globalConfig = {} } = window.__blConfig;
            const pageInfo = pagesMap[this.$route.path];

            if (
                this.$options.enableScroll === 0 ||
                (this.$options.enableScroll === -1 && pageInfo.disableScroll) ||
                (this.$options.enableScroll === -1 && globalConfig.disableScroll && pageInfo.disableScroll == undefined)
            ) {
                this.$el.style.overflow = 'hidden';
                return;
            }
            this.$el.style.overflow = 'auto';
            this.init = true;
            this.preScrollTop = this.$el.scrollTop;
            this.$el.addEventListener('scroll', this.scroll, false);
        },
        stopScroll() {
            this.$el.removeEventListener('scroll', this.scroll, false);
        }
    }
});

// Vue.use(components);

Vue.config.productionTip = false;

App.__isPage = true;

Vue.prototype.triggerEvent = function(eventName, eventPayload, { bubbles = false } = {}) {
    if (bubbles) {
        let parent = this;
        while (parent) {
            parent.$emit(eventName, {
                detail: eventPayload,
            });
            parent = parent.$parent;
        }
    } else {
        this.$emit(eventName, {
            detail: eventPayload,
        });
    }
};
Vue.prototype._mkarray = function(node, section, value) {
    const keys = section.split(/\[(\d+)\]/).filter(i => i !== '');
    const lastIndex = keys.pop();
    keys.forEach(key => {
        if (!Array.isArray(node[key])) {
            Vue.set(node, key, []);
        }
        node = node[key];
    });
    if (value === undefined) {
        Object.prototype.toString.call(node[lastIndex]) === '[object Object]' ? (value = node[lastIndex]) : (value = {});
    }
    Vue.set(node, lastIndex, value);
    return node[lastIndex];
};
Vue.prototype.setData = function(data) {
    if (typeof data === 'object') {
        for (const key in data) {
            if (data.hasOwnProperty(key)) {
                const value = data[key];

                const pathArray = key.split('.');
                const lastKey = pathArray.pop();

                let node = this.$data;
                for (let i = 0; i < pathArray.length; i++) {
                    const section = pathArray[i];
                    if (/\[\d+\]/.test(section)) {
                        node = this._mkarray(node, section);
                    } else {
                        if (typeof node[section] !== 'object' || node[section] == null) {
                            Vue.set(node, section, {});
                        }
                        node = node[section];
                    }
                }
                if (/\[\d+\]/.test(lastKey)) {
                    this._mkarray(node, lastKey, value);
                } else {
                    Vue.set(node, lastKey, value);
                }
            }
        }
    }
};
Vue.prototype.$methodInService = function(methodName, args) {
    if (typeof this[methodName] === 'function') {
        this[methodName].apply(this, args);
    }
};

Vue.prototype.createSelectorQuery = function() {
    return window.bl.createSelectorQuery(this).in(this);
};
Vue.prototype.createIntersectionObserver = function(options) {
    return window.bl.createIntersectionObserver(this, options);
};

let topPath = '';
const { appId, virtualId = '', envVersion = '' } = window.__devtoolsConfig || {};

let originPath;

router.beforeEach((to, from, next) => {
    window.__blConfig.currentPath = to.path;
    originPath = from.path;
    if (from.meta.keepAlive) { // 保存页面滚动位置
        const vm = (window.bl.getCurrentPages()[0].$children || []).find(ele => ele.__route === from.path);
        from.meta.scrollTop = (vm && vm.$el && vm.$el.scrollTop) || 0;
    }

    if (to.matched.length == 0) {
        __appInstance.onPageNotFound && __appInstance.onPageNotFound({
            appId: window.__devtoolsConfig.appId,
            path: to.path, 
            query: to.query
        });
        next(false);
    } else {
        next();
    }
});

window.app = new Vue({
    router,
    render: h => h(App, {
        ref: 'bl__app'
    }),
    created() {
        document.addEventListener('resize-content-height', this.handler, this);
    },
    mounted() {
        if (!sessionStorage.bili_sapp_hasOnLaunch) {
            topPath = window.location.href;
            sessionStorage.bili_sapp_topPath = topPath;
            sessionStorage.bili_sapp_hasOnLaunch = true;
            sessionStorage.bili_sapp_globalData = __appInstance.globalData ? JSON.stringify(__appInstance.globalData) : '{}';
        } else {
            __appInstance.globalData = JSON.parse(sessionStorage.bili_sapp_globalData);
        }
        __appInstance.onLaunch && __appInstance.onLaunch({
            appId,
            virtualId,
            envVersion,
            path: topPath.split('?')[0],
            query: this.$route.query,
            refererInfo: this.$route.query.refererInfo || {}
        });
        __appInstance.onShow && __appInstance.onShow({
            path: window.location.href.split('?')[0],
            query: this.$route.query,
            refererInfo: this.$route.query.refererInfo || {},
            topPath: sessionStorage.bili_sapp_topPath
        });
    },
    beforeDestroy() {
        document.removeEventListener('resize-content-height', this.handler, this);
    },
    methods: {
        handler() {
            this.$nextTick(() => {
                const { navigationInstance, tabBar = {} } = window.__blConfig;
                let container = window.bl.getCurrentPages()[0].$el,
                    tabHeight = 0,
                    navigatorHeight = 0,
                    padding = '';

                if (navigationInstance && navigationInstance.visible) { // 有navigation
                    navigatorHeight = navigationInstance.$el.clientHeight;
                }
                if (tabBar.instance && tabBar.instance.visible) { // 当前页面是tab页
                    tabHeight = tabBar.instance.$el.offsetHeight;
                    if (tabBar.position === 'top') {
                        tabBar.instance.$el.style.top = `${navigatorHeight}px`;
                        padding = `${navigatorHeight + tabHeight}px 0 0`;
                    } else {
                        padding = `${navigatorHeight}px 0 ${tabHeight}px`;
                    }
                } else { // 只有navigator高度
                    padding = `${navigatorHeight}px 0 0`;
                }
                container.style.padding = padding;
            });
        }
    }
});

window.app.$page = {
    id: 1
};

/* eslint-disable no-new */
window.__SmallApp.init = () => {
    Object.keys(API.default).forEach(key => {
        if (key === 'internal') {
            Object.keys(API.default.internal).forEach(ele => {
                if (!window.bl.internal[ele]) {
                    window.bl.internal[ele] = API.default.internal[ele];
                }
            });
        } else {
            if (!window.bl[key]){
                window.bl[key] = API.default[key];
            }
        }
    });
    Vue.use(window.COMPONENT.default);

    import('@/src/app.js').then(() => {
        // 监听页面后退前进刷新等事件，在执行操作前更新sessionStorage里的globalData
        window.onunload = () => {
            sessionStorage.bili_sapp_globalData = JSON.stringify(__appInstance.globalData);
        };
        window.app.$mount('#app');
    });
};
