$.define(["jQuery", "util", "doc", "win", "body"], "spa", function($, util, doc, win, body) {
	var head = doc.head || doc.getElementsByTagName("head")[0] || doc.documentElement,
		baseElement = head.getElementsByTagName("base")[0],
		ahref = { an: "href", av: "javascript:;" },
		spa_modal_index = 0,
		spa_css_ref = 0,
		resCache = {},
		cssCache = {},
		htmlCache = { "#": "#" },
		modelCache = {"_def_error_form":{}},
		scriptCache = {}, resUri, menuUri, menuEle,
		main, mainEle,
		cfg = {
			ajaxCfg: { mask: true },
			mask: true,
			loadEnabled: true
		},
		load_res = function() {
			if(resUri) {
				util.get(resUri, null, function(data) {
					/**
					 * res =[{id:"",uri:"",css:"",script:""},{id:"",uri:"",css:"",script:""},...........]
					 */
					if(data && data.length) {
						data.forEach(function(item) {
							resCache[item.id] = item;
						});
					}
					menuUri ? load_menu() : showMain();
				},false, cfg.ajaxCfg);
			}
		},
		load_menu = function() {
			util.get(menuUri, null, function(menu) {
				build_menu(menu);
				showMain();
			},false, cfg.ajaxCfg);
		},
		build_menu = function(menu) {
			if(menuEle && menuEle.length && menu && menu.length) {
				var ul = { tn: "ul", attrs: [{ an: "class", av: "nav nav-root" }], chs: [] };
				build_menu_item(ul.chs, menu);
				menuEle.empty();
				util.appendChild(menuEle[0], ul);
				menuEle.find(".nav-hand").on("click", function(e) {
					var $this = $(this);
					menuEle.find(".nav-hand.active").removeClass("active");
					$this.addClass("active");
					if($this.hasClass("spa-modal")) {
						showModal($this.attr("res"));
					} else {
						location.hash = "#" + $this.attr("res");
						showMain();
					}
				});
				menuEle.find(".nav-branch-hand").on("click", function(e) {
					var prt = $(this).parent();
					if(prt.hasClass("open")) {
						prt.removeClass("open");
					} else {
						prt.parent().children(".open").removeClass("open");
						prt.addClass("open");
					}
				});
			}
		},
		build_menu_item = function(po, items) {
			var item, res, caption, iconClass;
			items.forEach(function(item) {
				var li = { tn: "li" },
					attrs = li.attrs = [],
					chses = li.chs = [],
					lia = { tn: "a", attrs: [ahref], chs: [] };
				chses.push(lia);
				var ic = "icon-" + (item.icon || (item.res ? "book" : "branch"));
				lia.chs.push({ tn: "i", attrs: [{ an: "class", av: ic }] });
				lia.chs.push(item.caption);
				var lac = "";
				if(item.res) {
					lia.attrs.push({ an: "res", av: item.res });
					lac = lac + (item.modal ? " nav-hand spa-modal" : " nav-hand");
					lia.attrs.push({ an: "class", av: lac })
				} else {
					lia.chs.push({ tn: "i", attrs: [{ an: "class", av: "icon fold" }] });
					lac = lac + " nav-branch-hand";
					attrs.push({ an: "class", av: "nav-parent" });
					var ul = { tn: "ul", attrs: [{ an: "class", av: "nav" }], chs: [] };
					chses.push(ul);
					build_menu_item(ul.chs, item.children);
				}
				lia.attrs.push({ an: "class", av: lac });
				po.push(li);
			});
		},
		loadModelCss = function(model) {
			if(model.css) {
				var uri = model.css;
				if(cssCache[uri]) {
					cssCache[uri]['ref'] = cssCache[uri]['ref'] + 1;
				} else {
					++spa_css_ref;
					var css_id = "___spa_css_id_" + spa_css_ref;
					cssCache[uri] = { ref: 1, id: css_id };
					var link = doc.createElement('link');
					link.rel = 'stylesheet';
					link.href = model.css;
					link.media = 'all';
					link.setAttribute("id", css_id);
					head.appendChild(link);
				}
			}
		},
		showMainInternal = function(model) {
			cleanMain();
			main = model;
			loadModelCss(model);
			if(model.uri) {
				mainEle.html(htmlCache[model.uri]);
			}
			mainEle.attr("spa-model-id", model.id);
			if(model.factory && model.factory.main) {
				model.factory.main.call();
			}
		},
		showModalInternal = function(model, data) {
			loadModelCss(model);
			var ly = util.showModal(model.uri ? htmlCache[model.uri] : null);
			++spa_modal_index;
			ly.ctn.addClass("spa-modal").addClass("spa-modal-index-" + spa_modal_index).attr("spa-model-id", model.id);
			if(model.factory && model.factory.modal) {
				model.factory.modal(data);
			}
		},
		cacheModel = function(model) {
			var m = modelCache[model.id] = {};
			m.uri = model.uri;
			m.factory = model.factory;
			m.css = model.css;
			m.id = model.id;
			m.data = model.data;
			delete resCache[model.id];
			return m;
		},
		afterLoadByMain = function(model, data) {
			showMainInternal(cacheModel(model));
		},
		afterLoadByModal = function(model, data) {
			showModalInternal(cacheModel(model), data);
		},
		loadModel = function(model, handler, data) {
			if(htmlCache[model.uri || "#"]) {
				model.state = 11;
				if(model.script) {
					loadModelScript(model, handler, data);
				} else {
					handler(model, data);
				}
			} else {
				if(cfg.mask) {
					util.showLoading();
				}
				model.state = 10;
				$.ajax({ url: model.uri, dataType: "html", type: "GET" }).done(function(hc) {
					model.state = 11;
					htmlCache[model.uri] = hc;
					if(cfg.mask) {
						util.hideLoading();
					}
					if(model.script) {
						loadModelScript(model, handler, data);
					} else {
						handler(model, data);
					}
				}).fail(function(jqXHR, textStatus, errorThrown) {
					model.state = 12;
					if(cfg.mask) { util.hideLoading(); }
					util.raise({
						code: "loadModelHtml_" + (textStatus || ""),
						msg: textStatus,
						detailMsg: textStatus,
						xhr: jqXHR,
						eObj: errorThrown,
						url: model.uri
					});
				});
			}
		},
		removeModelCss = function(model) {
			if(model.css) {
				var cm = cssCache[model.css];
				if(cm) {
					cm.ref = cm.ref - 1;
					if(!cm.ref) {
						var link = doc.getElementById(cm.id);
						if(link) link.parentNode.removeChild(link);
						delete cssCache[model.css];
					}
				}
			}
		},
		loadModelScript = function(model, handler, data) {
			model.factory = scriptCache[model.script];
			if(model.factory) {
				model.state = 31;
				handler.call(model, data);
				return;
			}
			var node = doc.createElement("script");
			node.async = true;
			node.src = model.script;
			node.charset = "UTF-8";
			var supportOnload = "onload" in node;
			if(cfg.mask) {
				util.showLoading();
			}
			window.spa_define = function(factoryBuilder) {
				model.state = 30;
				if(cfg.mask) {
					util.showLoading();
				}
				try {
					scriptCache[model.script] = model.factory = factoryBuilder(spa);
					model.state = 31;
					if(cfg.mask) {
						util.hideLoading();
					}
				} catch(err) {
					model.state = 32;
					if(cfg.mask) {
						util.hideLoading();
					}
					util.raise({
						code: "buildModelFactory_" + err.toString(),
						msg: err.toString() || "",
						detailMsg: err.toString() || "",
						url: model.script
					});
				}
				handler(model, data);
			};

			if(supportOnload) {
				node.onload = function() {
					if(model.state < 21) model.state = 21;
					node.onerror = null;
					node.onload = null;
					head.removeChild(node);
					if(cfg.mask) {
						util.hideLoading();
					}
				};
				node.onerror = function() {
					node.onload = null;
					node.onerror = null;
					head.removeChild(node);
					if(cfg.mask) {
						util.hideLoading();
					}
					if(model.state < 22) {
						model.state = 22;
						util.raise({
							code: "loadModelScript_",
							msg:"",
							detailMsg: "",
							url: model.script
						});
					}
				};
			} else {
				node.onreadystatechange = function() {
					if(/loaded|complete/.test(node.readyState)) {
						node.onreadystatechange = null;
						if(model.state < 21) {
							model.state = 21;
							var to = model.timeout || 1000;
							setTimeout(function() {
								if(model.state == 21) {
									model.state = 22;
									head.removeChild(node);
									if(cfg.mask) {
										util.hideLoading();
									}
									util.raise({
										code: "loadModelScript_timeout",
										msg: "",
										detailMsg: "",
										url: model.script
									});
								}
							}, model.timeout || 1000);
						} else {
							head.removeChild(node);
							if(cfg.mask) {
								util.hideLoading();
							}
						}
					}
				};
			}
			model.state = 20;
			baseElement ?
				head.insertBefore(node, baseElement) :
				head.appendChild(node);
		},
		cleanMain = function() {
			if(main) {
				removeModelCss(main);
				if(main.factory && main.factory.mainDestory) { main.factory.mainDestory() };
				mainEle.empty();
				main = null;
			}
		},
		getModel = function(id) {
			return modelCache[id];
		},
		showModal = function(id, data) {
			var model = modelCache[id];
			if(model) {
				showModalInternal(model, data);
			} else {
				model = resCache[id];
				if(model) {
					loadModel(model, afterLoadByModal, data);
				} else {
					util.raise({ code: "invalid_model", msg: "不正确的模块[" + id + "]", detailMsg: "不正确的模块[" + id + "]" });
				}
			}
		},
		showMain = function() {
			var id = win.location.hash;
			if(id && id.length > 1) {
				id = id.substring(1);
			} else return;
			if(main && id == main.id) return;
			var model = modelCache[id];
			if(model) {
				showMainInternal(model);
			} else {
				var model = resCache[id];
				if(model) {
					loadModel(model, afterLoadByMain);
				} else {
					util.raise({ code: "invalid_model", msg: "不正确的模块[" + id + "]", detailMsg: "不正确的模块[" + id + "]" });
				}
			}
		},
		getLastModalIndex = function() {
			return spa_modal_index;
		},
		getLastModalCtn = function() {
			return $(".spa-modal-index-" + spa_modal_index);
		},
		getLastModalModel = function() {
			var ctn = $(".spa-modal-index-" + spa_modal_index);
			var id = modalCtn.attr("spa-model-id");
			return getModel(id);
		},
		closeModal = function() {
			var ctn = $(".spa-modal-index-" + spa_modal_index);
			var id = ctn.attr("spa-model-id");
			var inx = util.listModalIndex();
			if(ctn.hasClass("layer-" + inx)) {
				var model = modelCache[id];
				if(model) {
					if(model.factory.modalDestory) {
						model.factory.modalDestory.call(this);
					}
					if(model.css) {
						removeModelCss(model);
					}
					--spa_modal_index;
					util.closeModal();
				}
			} else {
				util.raise({ code: "invalid_dir", msg: "can't close modal:has top layer", detailMsg: "can't close modal:has top layer" });
			}
		},
		build = function(config) {
			$.extend(cfg, config);
			resUri = cfg.resUri || body.attr("resource");
			menuUri = cfg.menuUri || body.attr("menu");
			mainEle = cfg.mainEle || $(".spa-main");
			menuEle = cfg.menuEle || $(".spa-menu");
			resCache = cfg.resCache || resCache;
			htmlCache = cfg.htmlCache || htmlCache;
			htmlCache["#"] = "#";
			if(cfg.loadEnabled) load_res();
		},
	    spa = {
			build: build,
			closeModal: closeModal,
			getLastModalModel: getLastModalModel,
			getLastModalCtn: getLastModalCtn,
			getLastModalIndex: getLastModalIndex,
			showMain: showMain,
			showModal: showModal,
			mainChildren:function(selector){
				return mainEle.children(selector);
			},
			modalChildren:function(selector){
				return getLastModalCtn.children(selector);
			},
			findInMain:function(selector){
				return mainEle.find(selector);
			},
			findInModal:function(selector){
				return getLastModalCtn().find(selector);
			}
		};
		$.spa=spa;
	return spa;
});