/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
  Modified by Evan You @yyx990803
*/

import listToStyles from './listToStyles'

var hasDocument = typeof document !== 'undefined'

if (typeof DEBUG !== 'undefined' && DEBUG) {
	if (!hasDocument) {
		throw new Error(
			'vue-style-loader cannot be used in a non-browser environment. ' +
			"Use { target: 'node' } in your Webpack config to indicate a server-rendering environment."
		)
	}
}

/*
type StyleObject = {
  id: number;
  parts: Array<StyleObjectPart>
}

type StyleObjectPart = {
  css: string;
  media: string;
  sourceMap: ?string
}
*/

var stylesInDom = {
	/*
	  [id: number]: {
	    id: number,
	    refs: number,
	    parts: Array<(obj?: StyleObjectPart) => void>
	  }
	*/
}

var head = hasDocument && (document.head || document.getElementsByTagName('head')[0])
var singletonElement = null
var singletonCounter = 0
var isProduction = false
var noop = function() {}
var options = null
var ssrIdKey = 'data-vue-ssr-id'

// Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
// tags it will allow on a page
var isOldIE = typeof navigator !== 'undefined' && /msie [6-9]\b/.test(navigator.userAgent.toLowerCase())

export default function addStylesClient(parentId, list, _isProduction, _options) {
	isProduction = _isProduction

	options = _options || {}

	var styles = listToStyles(parentId, list)
	addStylesToDom(styles)

	return function update(newList) {
		var mayRemove = []
		for (var i = 0; i < styles.length; i++) {
			var item = styles[i]
			var domStyle = stylesInDom[item.id]
			domStyle.refs--
			mayRemove.push(domStyle)
		}
		if (newList) {
			styles = listToStyles(parentId, newList)
			addStylesToDom(styles)
		} else {
			styles = []
		}
		for (var i = 0; i < mayRemove.length; i++) {
			var domStyle = mayRemove[i]
			if (domStyle.refs === 0) {
				for (var j = 0; j < domStyle.parts.length; j++) {
					domStyle.parts[j]()
				}
				delete stylesInDom[domStyle.id]
			}
		}
	}
}

function addStylesToDom(styles /* Array<StyleObject> */ ) {
	for (var i = 0; i < styles.length; i++) {
		var item = styles[i]
		var domStyle = stylesInDom[item.id]
		if (domStyle) {
			domStyle.refs++
			for (var j = 0; j < domStyle.parts.length; j++) {
				domStyle.parts[j](item.parts[j])
			}
			for (; j < item.parts.length; j++) {
				domStyle.parts.push(addStyle(item.parts[j]))
			}
			if (domStyle.parts.length > item.parts.length) {
				domStyle.parts.length = item.parts.length
			}
		} else {
			var parts = []
			for (var j = 0; j < item.parts.length; j++) {
				parts.push(addStyle(item.parts[j]))
			}
			stylesInDom[item.id] = {
				id: item.id,
				refs: 1,
				parts: parts
			}
		}
	}
}

function createStyleElement() {
	var styleElement = document.createElement('style')
	styleElement.type = 'text/css'
	head.appendChild(styleElement)
	return styleElement
}

function addStyle(obj /* StyleObjectPart */ ) {
	var update, remove
	var styleElement = document.querySelector('style[' + ssrIdKey + '~="' + obj.id + '"]')

	if (styleElement) {
		if (isProduction) {
			// has SSR styles and in production mode.
			// simply do nothing.
			return noop
		} else {
			// has SSR styles but in dev mode.
			// for some reason Chrome can't handle source map in server-rendered
			// style tags - source maps in <style> only works if the style tag is
			// created and inserted dynamically. So we remove the server rendered
			// styles and inject new ones.
			styleElement.parentNode.removeChild(styleElement)
		}
	}

	if (isOldIE) {
		// use singleton mode for IE9.
		var styleIndex = singletonCounter++
		styleElement = singletonElement || (singletonElement = createStyleElement())
		update = applyToSingletonTag.bind(null, styleElement, styleIndex, false)
		remove = applyToSingletonTag.bind(null, styleElement, styleIndex, true)
	} else {
		// use multi-style-tag mode in all other cases
		styleElement = createStyleElement()
		update = applyToTag.bind(null, styleElement)
		remove = function() {
			styleElement.parentNode.removeChild(styleElement)
		}
	}

	update(obj)

	return function updateStyle(newObj /* StyleObjectPart */ ) {
		if (newObj) {
			if (newObj.css === obj.css &&
				newObj.media === obj.media &&
				newObj.sourceMap === obj.sourceMap) {
				return
			}
			update(obj = newObj)
		} else {
			remove()
		}
	}
}

var replaceText = (function() {
	var textStore = []

	return function(index, replacement) {
		textStore[index] = replacement
		return textStore.filter(Boolean).join('\n')
	}
})()

function applyToSingletonTag(styleElement, index, remove, obj) {
	var css = remove ? '' : processCss(obj.css)

	if (styleElement.styleSheet) {
		styleElement.styleSheet.cssText = replaceText(index, css)
	} else {
		var cssNode = document.createTextNode(css)
		var childNodes = styleElement.childNodes
		if (childNodes[index]) styleElement.removeChild(childNodes[index])
		if (childNodes.length) {
			styleElement.insertBefore(cssNode, childNodes[index])
		} else {
			styleElement.appendChild(cssNode)
		}
	}
}

function applyToTag(styleElement, obj) {
	var css = processCss(obj.css)
	var media = obj.media
	var sourceMap = obj.sourceMap

	if (media) {
		styleElement.setAttribute('media', media)
	}
	if (options.ssrId) {
		styleElement.setAttribute(ssrIdKey, obj.id)
	}

	if (sourceMap) {
		// https://developer.chrome.com/devtools/docs/javascript-debugging
		// this makes source maps inside style tags work properly in Chrome
		css += '\n/*# sourceURL=' + sourceMap.sources[0] + ' */'
		// http://stackoverflow.com/a/26603875
		css += '\n/*# sourceMappingURL=data:application/json;base64,' + btoa(unescape(encodeURIComponent(JSON.stringify(
			sourceMap)))) + ' */'
	}

	if (styleElement.styleSheet) {
		styleElement.styleSheet.cssText = css
	} else {
		while (styleElement.firstChild) {
			styleElement.removeChild(styleElement.firstChild)
		}
		styleElement.appendChild(document.createTextNode(css))
	}
}
//fixed by xxxxxx
var UPX_RE = /%\?([+-]?\d+(\.\d+)?)\?%/g
var BODY_RE = /\.\?%PAGE\?%/g
var BODY_SCOPED_RE = /\?%PAGE\?%\[data-v-[a-z0-9]{8}\]/g
var PAGE_SCOPED_RE = /uni-page-body\[data-v-[a-z0-9]{8}\]/g
var VAR_STATUS_BAR_HEIGHT = /var\(--status-bar-height\)/gi
var VAR_WINDOW_TOP = /var\(--window-top\)/gi
var VAR_WINDOW_BOTTOM = /var\(--window-bottom\)/gi
var VAR_WINDOW_LEFT = /var\(--window-left\)/gi
var VAR_WINDOW_RIGHT = /var\(--window-right\)/gi

function processCss(css) {
	var page = getPage()
	if (typeof uni !== 'undefined' && !uni.canIUse('css.var')) { //不支持 css 变量
		var offset = getWindowOffset()
		css = css.replace(VAR_STATUS_BAR_HEIGHT, '0px')
			.replace(VAR_WINDOW_TOP, offset.top + 'px')
			.replace(VAR_WINDOW_BOTTOM, offset.bottom + 'px')
            .replace(VAR_WINDOW_LEFT, '0px')
            .replace(VAR_WINDOW_RIGHT, '0px')
	}
	return css
		.replace(BODY_SCOPED_RE, page)
		.replace(BODY_RE, '')
		.replace(PAGE_SCOPED_RE, 'body.' + page + ' uni-page-body')
		.replace(/\{[\s\S]+?\}/g, function (css) {
      if(typeof uni === 'undefined'){
        return css
      }
			return css.replace(UPX_RE, function (a, b) {
				return uni.upx2px(b) + 'px'
			})
		})
}

function getPage() {
	var app = typeof getApp === 'function' && getApp()
	return app && app.$route && app.$route.meta && app.$route.meta.name || ''
}

function getWindowOffset() {
	var app = typeof getApp === 'function' && getApp()
	if (app && app.$route && app.$route.meta && app.$route.meta.name) {
		return {
			top: app.$route.meta.windowTop,
			// TODO 可配置 TabBar 高度
			bottom: app.$route.meta.isTabBar ? 50 : 0
		}
	}
	return {
		top: 0,
		bottom: 0
	}
}
