class Router {
	constructor(arg) {
		if (arg && arg.constructor !== Object) {
			return console.error(`Routing configuration must be an Object`)
		}
		Router.$root = this;
	}

	// 路由跳转方法映射
	routeMap = {
		push: 'navigateTo',
		replace: 'redirectTo',
		replaceAll: 'reLaunch',
		pushTab: 'switchTab'
	}

	/**
	 * 执行路由跳转
	 */
	_pushTo() {
		return new Promise((resolve, reject) => {
			let {
				page,
				params,
				method
			} = this.tempRoute
			// 对首次进入页面执行路由守卫时如果放行，method page params都为空 此时应该直接中断流程，无需抛出异常
			if (!method && !page && !params) {
				return
			}

			let urlParams = '?'
			if (!page) {
				reject(new Error('参数page未填写'))
				return
			} else if (params && typeof(params) === 'object') {
				// 处理参数，转换为url字符串
				Object.keys(params).forEach(k => {
					// 深度对象转为json字符串（包含数组）
					if (typeof(params[k]) === 'object') {
						if (params[k]) {
							const json = JSON.stringify(params[k])
							urlParams += `${k}=${json}&`
						} else {
							urlParams += `${k}=&`
						}
					} else if (typeof(params[k]) === 'number' || typeof(params[k]) === 'string' || typeof(params[k]) ===
						'boolean') {
						// 基础值直接写入
						urlParams += `${k}=${params[k]}&`
					} else if (typeof(params[k]) === 'undefined') {
						urlParams += `${k}=&`
					}
				})
			}

			// 参数组装
			if (urlParams.length === 1) {
				urlParams = ''
			} else {
				urlParams = urlParams.substr(0, urlParams.length - 1)
			}

			// 设置路由跳转方式
			if (!method) {
				method = 'navigateTo'
			}
			if (this.routeMap[method]) {
				method = this.routeMap[method]
			}

			// 调用系统跳转方法
			uni[method]({
				url: page + urlParams,
				success: () => {
					// 执行路由后置守卫
					if (this._afterEach && typeof(this._afterEach) === 'function') {
						this._afterEach.call(this, this.tempRoute, this.route)
					}

					// 更新路由信息
					this.route = {
						path: page + urlParams,
						params: params || {},
						page
					}
					this.tempRoute = null
					resolve()
				},
				fail: (e) => {
					reject(new Error('路由跳转失败！'))
				}
			})
		})
	}

	/**动态的导航到一个新 URL 保留浏览历史
	 * navigateTo
	 * @param {Object} rule
	 */
	push(arg) {
		const rule = {
			method: 'navigateTo'
		}
		if (typeof(arg) === 'string') {
			rule.page = arg
		} else if (typeof(arg) === 'object') {
			rule.page = arg.page
			rule.params = arg.params
		}
		this.next(rule)
	}

	/**动态的导航到一个新 URL 关闭当前页面，跳转到的某个页面。
	 * redirectTo
	 * @param {Object} rule
	 */
	replace(arg) {
		const rule = {
			method: 'redirectTo'
		}
		if (typeof(arg) === 'string') {
			rule.page = arg
		} else if (typeof(arg) === 'object') {
			rule.page = arg.page
			rule.params = arg.params
		}
		this.next(rule)
	}

	/**动态的导航到一个新 URL 关闭所有页面，打开到应用内的某个页面
	 * 	reLaunch
	 * @param {Object} rule
	 */
	replaceAll(arg) {
		const rule = {
			method: 'reLaunch'
		}
		if (typeof(arg) === 'string') {
			rule.page = arg
		} else if (typeof(arg) === 'object') {
			rule.page = arg.page
			rule.params = arg.params
		}
		this.next(rule)
	}

	/** 跳转Tabbar
	 * 	switchTab
	 * @param {Object} rule
	 */
	pushTab(arg) {
		const rule = {
			method: 'switchTab'
		}
		if (typeof(arg) === 'string') {
			rule.page = arg
		} else if (typeof(arg) === 'object') {
			rule.page = arg.page
			rule.params = arg.params
		}
		this.next(rule)
	}


	/**
	 * 返回到指定层级页面上
	 */
	back(delta = 1) {
		// 返回上级
		if (delta.constructor != Number) {
			this._errorHandler(new Error('返回层级参数必须是一个Number类型且必须大于0：'))
			return
		}
		uni.navigateBack({
			delta
		})
	}

	/**
	 * 分发路由
	 * @param {Object} args
	 */
	_next() {
		return new Promise((resolve, reject) => {
			if (this._beforeEach && typeof(this._beforeEach) === 'function') {
				// 需要传给守卫 to from next
				this._beforeEach.call(this, this.tempRoute, this.route, resolve)
			} else {
				this._pushTo().catch(e => {
					reject(e)
				})
			}
		})
	}

	next(args) {
		if (args) {
			// 保存临时数据
			if (typeof(args) === 'object') {
				this.tempRoute = {
					// 第一次调用next一定存在method，后续循环调用可能不会存在，不存在时使用上次缓存的method
					method: args.method || this.tempRoute.method,
					page: args.page,
					params: args.params
				}
			} else if (typeof(args) === 'string') {
				this.tempRoute = {
					page: args
				}
			} else if (!args) {
				// 中断路由 args = false
				this.tempRoute = null
				return
			}

			if (!this.route) {
				this.route = {
					page: '/' + getCurrentPages()[0].route
				}
			}

			this._next().then(args => {
				this.next(args)
			}).catch(e => {
				this.tempRoute = null
				this._errorHandler(e)
			})
		} else {
			this._pushTo().catch(e => {
				this.tempRoute = null
				this._errorHandler(e)
			})
		}
	}

	/**
	 * 应用启动时执行一次路由检查（前置守卫，若通过则不做事情）
	 */
	doBeforeHooks() {
		this.tempRoute = {}
		this.next({})
	}

	// 设置路由前置/后置守卫
	beforeEach(fn) {
		this._beforeEach = fn
	}
	afterEach(fn) {
		this._afterEach = fn
	}
	// 设置路由跳转错误处理
	onError(fn) {
		if (fn && typeof(fn) === 'function') {
			this._errorHandler = fn
		}
	}

	// 获取当前路由信息
	getCurrentRoute() {
		return this.route
	}

}

// 路由对象属性定义
Router.$root = null
// 当前路由内容
Router.route = null
// 临时路由信息
Router.tempRoute = null
// 路由前置后置守卫
Router._beforeEach = null
Router._afterEach = null
// 路由跳转错误处理
Router._errorHandler = function(e) {
	console.error(e)
}

Router.install = function(Vue) {
	Vue.mixin({
		onLaunch: function() {},
		onLoad: function(props) {
			// 首次进入页面时,缓存中不存在当前路由信息，需要初始化路由信息
			if (!Router.$root.getCurrentRoute()) {
				const rt = {
					params: {},
					page: '/' + getCurrentPages()[0].route
				}
				if (props) {
					Object.keys(props).forEach(k => {
						// url转的对象全部都是字符串，需要识别其中的对象和基本数据类型
						try {
							const obj = JSON.parse(props[k])
							if (typeof(obj) === 'string') {
								// 只有字符串还会是字符串，数字、布尔、数组均会转换为正常类型
								rt.params[k] = props[k]
							} else {
								rt.params[k] = obj
							}
						} catch (e) {
							rt.params[k] = props[k]
						}
					})
				}
				Router.$root.route = rt

				// 执行路由前置守卫
				Router.$root.doBeforeHooks()
			}

			// 自动获取页面标题(app端可能获取不到)
			const pages = getCurrentPages()
			let pageTitle = pages[pages.length - 1].pageTitle
			// #ifdef H5
			if (!pageTitle) {
				pageTitle = document.title
			}
			// #endif
			Router.$root.route.pageTitle = pageTitle
		},

		onShow() {
			if (!getCurrentPages().length) {
				return
			}
			// 获取当前路由信息
			const pages = getCurrentPages()
			const page = pages[pages.length - 1]
			let pageTitle = page.pageTitle
			const rt = {
				params: {},
				page: '/' + page.route,
			}
			if (!pageTitle) {
				// #ifdef H5
				rt.pageTitle = document.title
				// #endif
			} else {
				rt.pageTitle = pageTitle
			}
			
			const route = Router.$root.route
			// 若当前页面地址不等于缓存中地址,则更新缓存路由信息
			if (!route || route.page !== rt.page) {
				Router.$root.route = rt
			}
		}
	})
	Object.defineProperty(Vue.prototype, "$Router", {
		get: function() {
			return Router.$root
		}
	})
	Object.defineProperty(Vue.prototype, "$Route", {
		get: function() {
			return Router.$root.getCurrentRoute()
		}
	})
}

export default Router
