/**
 * <div id="app" class="mui-views">
	<div class="mui-view">
		<div class="mui-navbar">
		</div>
		<div class="mui-pages">
		</div>
	</div>
</div>
 * @param {Object} $
 * @param {Object} window
 */
(function($, window) {
	var CLASS_LEFT = $.className('left');
	var CLASS_CENTER = $.className('center');
	var CLASS_RIGHT = $.className('right');
	var CLASS_PAGE = $.className('page');
	var CLASS_PAGE_LEFT = $.className('page-left');
	var CLASS_PAGE_CENTER = $.className('page-center');
	var CLASS_NAVBAR_LEFT = $.className('navbar-left');
	var CLASS_NAVBAR_CENTER = $.className('navbar-center');
	var CLASS_PAGE_SHADOW = $.className('page-shadow');

	var CLASS_TRANSITIONING = $.className('transitioning');

	var SELECTOR_LEFT = '.' + CLASS_LEFT;
	var SELECTOR_CENTER = '.' + CLASS_CENTER;
	var SELECTOR_RIGHT = '.' + CLASS_RIGHT;

	var SELECTOR_ICON = $.classSelector('.icon');
	var SELECTOR_NAVBAR = $.classSelector('.navbar');
	var SELECTOR_NAVBAR_INNER = $.classSelector('.navbar-inner');
	var SELECTOR_PAGES = $.classSelector('.pages');
	var SELECTOR_BTN_NAV = $.classSelector('.btn-nav');
	var SELECTOR_PAGE_LEFT = '.' + CLASS_PAGE_LEFT;
	var SELECTOR_PAGE_CENTER = '.' + CLASS_PAGE_CENTER;
	var SELECTOR_NAVBAR_LEFT = '.' + CLASS_NAVBAR_LEFT;
	var SELECTOR_NAVBAR_CENTER = '.' + CLASS_NAVBAR_CENTER;

	var View = $.Class.extend({
		init: function(element, options) {
			this.view = this.element = element;
			this.options = $.extend({
				animateNavbar: true,
				swipeBackPageActiveArea: 30,
				hardwareAccelerated: true
			}, options);
			this.navbars = this.view.querySelector(SELECTOR_NAVBAR);
			this.pages = this.view.querySelector(SELECTOR_PAGES);

			this.history = []; //history

			this.maxScrollX = this.view.offsetWidth;
			this.x = this.y = 0;
			this.translateZ = this.options.hardwareAccelerated ? ' translateZ(0)' : '';
			this.ratio = 0;
			this.isBack = true;
			this.moved = this.dragging = false;

			this.activeNavbar = this.previousNavbar = null;
			this.activePage = this.previousPage = null;

			this._initPageEventMethod();

			this._initDefaultPage();

			this._initNavBar();

			this.initEvent();
		},
		_initPageEventMethod: function() {
			var self = this;
			$.each(['onPageBeforeShow', 'onPageShow', 'onPageBeforeBack', 'onPageBack'], function(index, event) {
				self[event + 'Callbacks'] = {};
				self[event] = function(page, callback) {
					var eventCallbacks = event + 'Callbacks';
					if (!self[eventCallbacks].hasOwnProperty(page)) {
						self[eventCallbacks][page] = [callback];
					} else {
						self[eventCallbacks][page].push(callback);
					}
				};
			});
		},
		_initDefaultPage: function() {
			var defaultPage = document.querySelector(this.options.defaultPage);
			if (defaultPage) {
				this._appendPage(defaultPage);
			} else {
				throw new Error('defaultPage[' + this.options.defaultPage + '] does not exist');
			}
		},
		initEvent: function() {
			this.view.addEventListener('click', this);
			this.view.addEventListener('tap', this);
			this.pages.addEventListener('drag', this);
			this.pages.addEventListener('dragend', this);
			this.pages.addEventListener('webkitTransitionEnd', this);
		},
		handleEvent: function(event) {
			switch (event.type) {
				case 'click':
					this._click(event);
					break;
				case 'tap':
					this._tap(event);
					break;
				case 'drag':
					this._drag(event);
					break;
				case 'dragend':
					this._dragend(event);
					break;
				case 'webkitTransitionEnd':
					this._webkitTransitionEnd(event);
					break;
			}
		},
		shadow: function() {
			var shadow = document.createElement('div');
			shadow.className = CLASS_PAGE_SHADOW;
			return shadow;
		}(),
		_removePage: function(page, navbar) {
			this._removeNavbar(page, navbar);
			document.body.appendChild(page);
			this._cleanPageClass(page);
		},
		_prependPage: function(page) {
			var navbar = page.querySelector(SELECTOR_NAVBAR_INNER);
			this._prependNavbar(navbar);
			page.classList.add(CLASS_PAGE_LEFT);
			this.pages.insertBefore(page, this.pages.firstElementChild);
		},
		_appendPage: function(page) {
			var navbar = page.querySelector(SELECTOR_NAVBAR_INNER);
			this._appendNavbar(navbar);
			page.classList.add(CLASS_PAGE_CENTER);
			this.pages.appendChild(page);
		},
		_removeNavbar: function(page, navbar) {
			page.insertBefore(navbar, page.firstElementChild);
			this._cleanNavbarClass(navbar);
		},
		_prependNavbar: function(navbar) {
			navbar.classList.add(CLASS_NAVBAR_LEFT);
			this.navbars.insertBefore(navbar, this.navbars.firstElementChild);
		},
		_appendNavbar: function(navbar) {
			navbar.classList.add(CLASS_NAVBAR_CENTER);
			this.navbars.appendChild(navbar);
		},
		_cleanPageClass: function(page) {
			page.classList.remove(CLASS_PAGE_CENTER);
			page.classList.remove(CLASS_PAGE_LEFT);
		},
		_cleanNavbarClass: function(navbar) {
			navbar.classList.remove(CLASS_NAVBAR_CENTER);
			navbar.classList.remove(CLASS_NAVBAR_LEFT);
		},
		_tap: function(event) {
			var target = event.target;
			for (; target && target !== document; target = target.parentNode) {
				if (target.tagName === 'A' && target.hash) {
					var page = document.getElementById(target.hash.replace('#', ''));
					if (page && page.classList.contains(CLASS_PAGE)) {
						event.stopPropagation();
						event.detail.gesture.preventDefault();
						this.go(target.hash);
						break;
					}
				}
			}
		},
		_click: function(event) {
			var target = event.target;
			for (; target && target !== document; target = target.parentNode) {
				if (target.tagName === 'A' && target.hash) {
					var page = document.getElementById(target.hash.replace('#', ''));
					if (page && page.classList.contains(CLASS_PAGE)) {
						event.preventDefault();
						break;
					}
				}
			}
		},
		_cleanStyle: function(el) {
			if (el) {
				el.style.webkitTransform = '';
				el.style.opacity = '';
			}
		},
		_webkitTransitionEnd: function(event) {
			this.dragging = this.moved = false;
			if (this.activePage !== event.target) {
				return;
			}

			this.isInTransition = false;

			this.shadow.parentNode === this.activePage && this.activePage.removeChild(this.shadow);
			this.previousPageClassList.remove(CLASS_TRANSITIONING);
			this.activePageClassList.remove(CLASS_TRANSITIONING);

			var self = this;
			if ($.os.ios && this.options.animateNavbar && this.previousNavElements && this.activeNavElements) {
				var isBack = this.isBack;
				$.each(this.previousNavElements, function(i, el) {
					el.classList.remove(CLASS_TRANSITIONING);
					isBack && self._cleanStyle(el);
				});
				$.each(this.activeNavElements, function(i, el) {
					el.classList.remove(CLASS_TRANSITIONING);
					self._cleanStyle(el);
				});
				if (this.previousNavBackIcon) {
					this.previousNavBackIcon.classList.remove(CLASS_TRANSITIONING);
					isBack && this._cleanStyle(this.previousNavBackIcon);
				}
				if (this.activeNavBackIcon) {
					this.activeNavBackIcon.classList.remove(CLASS_TRANSITIONING);
					this._cleanStyle(this.activeNavBackIcon);
				}
			} else {
				this.previousNavbar.classList.remove(CLASS_TRANSITIONING);
				this.activeNavbar.classList.remove(CLASS_TRANSITIONING);
				this._cleanStyle(this.previousNavbar);
				this._cleanStyle(this.activeNavbar);
			}

			this._cleanStyle(this.previousPage);
			this._cleanStyle(this.activePage);

			if (this.ratio <= 0.5) {
				return;
			}
			if (this.isBack) {
				this._removePage(this.activePage, this.activeNavbar);
				this.previousPageClassList.remove(CLASS_PAGE_LEFT);
				this.previousPageClassList.add(CLASS_PAGE_CENTER);
				this.previousNavbar.classList.remove(CLASS_NAVBAR_LEFT);
				this.previousNavbar.classList.add(CLASS_NAVBAR_CENTER);
				if (this.history.length > 0) {
					this._prependPage(this.history.pop());
				}
				this._initNavBar();
				this._trigger('pageBack', this.activePage);
				this._trigger('pageShow', this.previousPage);
			} else {
				this.previousPageClassList.add(CLASS_PAGE_LEFT);
				this.activePageClassList.add(CLASS_PAGE_CENTER);
				this._trigger('pageShow', this.activePage);
			}


		},
		_trigger: function(eventType, page) {
			var eventCallbacks = 'on' + eventType.charAt(0).toUpperCase() + eventType.slice(1) + 'Callbacks';
			if (this[eventCallbacks].hasOwnProperty(page.id)) {
				var callbacks = this[eventCallbacks][page.id];
				var event = new CustomEvent(eventType, {
					detail: {
						page: page
					},
					bubbles: true,
					cancelable: true
				});
				for (var len = callbacks.length; len--;) {
					callbacks[len].apply(this, event);
				}
			}
			$.trigger(this.view, eventType, {
				page: page
			});
		},
		_initPageTransform: function() {
			this.previousPage = this.pages.querySelector(SELECTOR_PAGE_LEFT);
			this.activePage = this.pages.querySelector(SELECTOR_PAGE_CENTER);
			if (this.previousPage && this.activePage) {
				this.activePage.appendChild(this.shadow);
				this.previousPageClassList = this.previousPage.classList;
				this.activePageClassList = this.activePage.classList;

				this.previousPageStyle = this.previousPage.style;
				this.activePageStyle = this.activePage.style;

				this.previousPageClassList.remove(CLASS_TRANSITIONING);
				this.activePageClassList.remove(CLASS_TRANSITIONING);

				if (this.options.animateNavbar && this.navbars) {
					this.previousNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_LEFT);
					this.activeNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_CENTER);
					if (this.previousNavbar && this.activeNavbar) {
						this.previousNavElements = this.previousNavbar.querySelectorAll(SELECTOR_LEFT + ',' + SELECTOR_CENTER + ',' + SELECTOR_RIGHT);
						this.activeNavElements = this.activeNavbar.querySelectorAll(SELECTOR_LEFT + ',' + SELECTOR_CENTER + ',' + SELECTOR_RIGHT);
						this.previousNavBackIcon = this.previousNavbar.querySelector(SELECTOR_LEFT + SELECTOR_BTN_NAV + ' ' + SELECTOR_ICON);
						this.activeNavBackIcon = this.activeNavbar.querySelector(SELECTOR_LEFT + SELECTOR_BTN_NAV + ' ' + SELECTOR_ICON);
					}
				}
				this.x = 0;
				this.dragging = true;
				return true;
			}
			return false;
		},
		_initNavBar: function() {
			if (this.options.animateNavbar && this.navbars) {
				var inners = this.navbars.querySelectorAll(SELECTOR_NAVBAR_INNER);
				var inner, left, right, center, leftWidth, rightWidth, centerWidth, noLeft, onRight, currLeft, diff, navbarWidth;
				for (var i = 0, len = inners.length; i < len; i++) {
					inner = inners[i];
					left = inner.querySelector(SELECTOR_LEFT);
					right = inner.querySelector(SELECTOR_RIGHT);
					center = inner.querySelector(SELECTOR_CENTER);
					noLeft = !left;
					noRight = !right;
					leftWidth = noLeft ? 0 : left.offsetWidth;
					rightWidth = noRight ? 0 : right.offsetWidth;
					centerWidth = center ? center.offsetWidth : 0;
					navbarWidth = this.maxScrollX;
					onLeft = inner.classList.contains('navbar-left');
					if (noRight) {
						currLeft = navbarWidth - centerWidth;
					}
					if (noLeft) {
						currLeft = 0;
					}
					if (!noLeft && !noRight) {
						currLeft = (navbarWidth - rightWidth - centerWidth + leftWidth) / 2;
					}
					var requiredLeft = (navbarWidth - centerWidth) / 2;
					if (navbarWidth - leftWidth - rightWidth > centerWidth) {
						if (requiredLeft < leftWidth) {
							requiredLeft = leftWidth;
						}
						if (requiredLeft + centerWidth > navbarWidth - rightWidth) {
							requiredLeft = navbarWidth - rightWidth - centerWidth;
						}
						diff = requiredLeft - currLeft;
					} else {
						diff = 0;
					}

					var centerLeft = diff;
					if (center) {
						center.style.marginLeft = -leftWidth + 'px';
						center.mNavbarLeftOffset = -(currLeft + diff) + 30; //这个30是测出来的。后续要实际计算一下
						center.mNavbarRightOffset = navbarWidth - currLeft - diff - centerWidth;
					}

					if (onLeft) center.style.webkitTransform = ('translate3d(' + center.mNavbarLeftOffset + 'px, 0, 0)');

					if (!noLeft) {
						left.mNavbarLeftOffset = -leftWidth;
						left.mNavbarRightOffset = (navbarWidth - leftWidth) / 2;
						if (onLeft) left.style.webkitTransform = ('translate3d(' + left[0].mNavbarLeftOffset + 'px, 0, 0)');
					}

					if (!noRight) {
						right.mNavbarLeftOffset = -(navbarWidth - rightWidth) / 2;
						right.mNavbarRightOffset = rightWidth;
						if (onLeft) right.style.webkitTransform = ('translate3d(' + right[0].mNavbarLeftOffset + 'px, 0, 0)');
					}

				}
			}
		},
		_drag: function(event) {
			if (this.isInTransition) {
				return;
			}
			var detail = event.detail;
			if (!this.dragging) {
				if ((detail.start.x - this.view.offsetLeft) < this.options.swipeBackPageActiveArea) {
					this.isBack = true;
					this._initPageTransform();
				}
			}
			if (this.dragging) {
				var deltaX = 0;
				if (!this.moved) { //start
					deltaX = detail.deltaX;
					$.gestures.touch.lockDirection = true; //锁定方向
					$.gestures.touch.startDirection = detail.direction;
				} else { //move
					deltaX = detail.deltaX - detail.lastDeltaX;
				}
				var newX = this.x + deltaX;
				if (newX < 0 || newX > this.maxScrollX) {
					newX = newX < 0 ? 0 : this.maxScrollX;
				}

				event.stopPropagation();
				detail.gesture.preventDefault();

				if (!this.requestAnimationFrame) {
					this._updateTranslate();
				}

				this.moved = true;
				this.x = newX;
				this.y = 0;
			}
		},
		_dragend: function(event) {
			if (!this.moved) {
				return;
			}

			event.stopPropagation();

			var detail = event.detail;

			this._clearRequestAnimationFrame();

			this._prepareTransition();

			this.ratio = this.x / this.maxScrollX;
			if (this.ratio === 1 || this.ratio === 0) {
				$.trigger(this.activePage, 'webkitTransitionEnd');
				return;
			}
			if (this.ratio > 0.5) {
				this.setTranslate(this.maxScrollX, 0);
			} else {
				this._cleanStyle(this.previousPage);
				this._cleanStyle(this.activePage);
			}
		},
		_prepareTransition: function() {
			this.isInTransition = true;
			this.previousPageClassList.add(CLASS_TRANSITIONING);
			this.activePageClassList.add(CLASS_TRANSITIONING);
			var self = this;
			if (this.options.animateNavbar && this.previousNavElements && this.activeNavElements) {
				this.previousNavbar.classList.add(CLASS_TRANSITIONING);
				this.activeNavbar.classList.add(CLASS_TRANSITIONING);
				$.each(this.previousNavElements, function(i, el) {
					el.classList.add(CLASS_TRANSITIONING);
					self._cleanStyle(el);
				});
				$.each(this.activeNavElements, function(i, el) {
					el.classList.add(CLASS_TRANSITIONING);
					self._cleanStyle(el);
				});
				if (this.previousNavBackIcon) {
					this._cleanStyle(this.previousNavBackIcon);
					this.previousNavBackIcon.classList.add(CLASS_TRANSITIONING);
				}
				if (this.activeNavBackIcon) {
					this._cleanStyle(this.activeNavBackIcon);
					this.activeNavBackIcon.classList.add(CLASS_TRANSITIONING);
				}
			}
		},
		_clearRequestAnimationFrame: function() {
			if (this.requestAnimationFrame) {
				cancelAnimationFrame(this.requestAnimationFrame);
				this.requestAnimationFrame = null;
			}
		},
		_getTranslateStr: function(x, y) {
			if (this.options.hardwareAccelerated) {
				return 'translate3d(' + x + 'px,' + y + 'px,0px) ' + this.translateZ;
			}
			return 'translate(' + x + 'px,' + y + 'px) ';
		},

		_updateTranslate: function() {
			var self = this;
			if (self.x !== self.lastX || self.y !== self.lastY) {
				self.setTranslate(self.x, self.y);
			}
			self.requestAnimationFrame = requestAnimationFrame(function() {
				self._updateTranslate();
			});
		},
		_setNavbarTranslate: function(x, y) {
			var percentage = x / this.maxScrollX;
			//only for ios
			if ($.os.ios && this.options.animateNavbar && this.previousNavElements && this.activeNavElements) {
				var i, len, style, el;
				for (i = 0, len = this.activeNavElements.length; i < len; i++) {
					el = this.activeNavElements[i];
					style = el.style;
					style.opacity = (1 - percentage * (el.classList.contains(CLASS_LEFT) ? 3.5 : 1.3));
					if (!el.classList.contains(CLASS_RIGHT)) {
						var activeNavTranslate = percentage * el.mNavbarRightOffset;
						el.style.webkitTransform = ('translate3d(' + activeNavTranslate + 'px,0,0)');
						if (el.classList.contains(CLASS_LEFT) && this.activeNavBackIcon) {
							this.activeNavBackIcon.style.webkitTransform = ('translate3d(' + -activeNavTranslate + 'px,0,0)');
						}
					}
				}
				for (i = 0, len = this.previousNavElements.length; i < len; i++) {
					el = this.previousNavElements[i];
					style = el.style;
					style.opacity = percentage * 1.3 - 0.3;
					if (!el.classList.contains(CLASS_RIGHT)) {
						var previousNavTranslate = el.mNavbarLeftOffset * (1 - percentage);
						el.style.webkitTransform = ('translate3d(' + previousNavTranslate + 'px,0,0)');
						if (el.classList.contains(CLASS_LEFT) && this.previousNavBackIcon) {
							this.previousNavBackIcon.style.webkitTransform = ('translate3d(' + -previousNavTranslate + 'px,0,0)');
						}
					}
				}
			} else {
				this.activeNavbar.style.opacity = 1 - percentage * 1.3;
				this.previousNavbar.style.opacity = percentage * 1.3 - 0.3;
			}
		},
		setTranslate: function(x, y) {
			this.x = x;
			this.y = y;
			this.previousPage.style.opacity = 0.9 + 0.1 * x / this.maxScrollX;
			if ($.os.ios) { //only for ios
				this.previousPage.style['webkitTransform'] = this._getTranslateStr((x / 5 - this.maxScrollX / 5), y);
			}
			this.activePage.style['webkitTransform'] = this._getTranslateStr(x, y);

			if (this.options.animateNavbar) {
				this._setNavbarTranslate(x, y);
			}
			this.lastX = this.x;
			this.lastY = this.y;
		},
		canBack: function() {
			return this.pages.querySelector(SELECTOR_PAGE_LEFT);
		},
		back: function() {
			if (this.isInTransition) {
				return;
			}
			this.isBack = true;
			this.ratio = 1;
			if (this._initPageTransform()) {
				this._trigger('pageBeforeBack', this.activePage);
				this._trigger('pageBeforeShow', this.previousPage);
				this._prepareTransition();
				this.previousPage.offsetHeight;
				this.activePage.offsetHeight;
				this.setTranslate(this.maxScrollX, 0);
			}
		},
		go: function(pageSelector) {
			if (this.isInTransition) {
				return;
			}
			var nextPage = document.querySelector(pageSelector);

			if (nextPage) {
				var nextNavbar = nextPage.querySelector(SELECTOR_NAVBAR_INNER);

				var previousNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_LEFT);
				var activeNavbar = this.navbars.querySelector(SELECTOR_NAVBAR_CENTER);
				var previousPage = this.pages.querySelector(SELECTOR_PAGE_LEFT);
				var activePage = this.pages.querySelector(SELECTOR_PAGE_CENTER);

				if (previousNavbar && previousPage) {
					this._removePage(previousPage, previousNavbar);
					this.history.push(previousPage); //add to history
				}
				if (activeNavbar) {
					activeNavbar.classList.remove(CLASS_NAVBAR_CENTER);
					activeNavbar.classList.add(CLASS_NAVBAR_LEFT);
				}

				if (activePage) {
					activePage.classList.remove(CLASS_PAGE_CENTER);
					activePage.style.webkitTransform = 'translate3d(0,0,0)';
					activePage.classList.add(CLASS_PAGE_LEFT);
				}

				nextPage.style.webkitTransform = 'translate3d(100%,0,0)';
				this._appendPage(nextPage);
				nextPage.appendChild(this.shadow); //shadow
				nextPage.offsetHeight; //force
				this.isBack = false;
				this.ratio = 1;

				this._initPageTransform();
				this._initNavBar();

				this._setNavbarTranslate(this.maxScrollX, 0);
				//force
				this.previousPage.offsetHeight;
				this.activePage.offsetHeight;
				this.previousNavbar.offsetHeight;
				this.activeNavbar.offsetHeight;

				this._trigger('pageBeforeShow', this.activePage);
				this._prepareTransition();
				this.setTranslate(0, 0);
			}
		}

	});


	$.fn.view = function(options) {
		var self = this[0];
		var viewApi = null;
		var id = self.getAttribute('data-view');
		if (!id) {
			id = ++$.uuid;
			$.data[id] = viewApi = new View(self, options);
		} else {
			viewApi = $.data[id];
		}
		return viewApi;

	}
})(mui, window);