$.define(["jQuery", "doc", "body", "util"], "code", function($, doc, $body, util) {
	/**
	 * env={ ce:current Element; cd:current Data; ci:current index in
	 * ListElement array[ci=index] or object[ci=key] es:Element stack; Array
	 * ds:Data stack; Array is:index stack;Array sh:value handler
	 * function(env,key){return [String]} dir:directive handler
	 * Aarray(function(env){}.call(elementObj={},env) }
	 */
	var ch_val_buider = {
			/*{{111-222-333-444}}=={k:111,h:222,p:[333,444]}   */
			/*{{111}}=={k:111,h:s}   */
			/*{{111-222}}=={k:111,h:222,p:undefined}   */

			"c": function( /* env */ ) {
				return this.k;
			},
			"s": function(env) {
				var v = env.cd;
				if(v) v = this.k ? v[this.k] : v;
				if(v) return v;
				return 0===v?"0":"";
			},
			"cp":function(env){
				var v = env.cd;
				if(v) return v;
				return 0===v?"0":"";
			},
			"_index": function(env) {
				return env.ci+1;
			},
			"bool":function(env){
				return env.cd[this.k]?"是":"否";
			},
			"date":function(env){
				var v = env.cd[this.k];
				return v?(v.substring(0,4)+"年"+v.substring(4,6)+"月"+v.substring(6,8)+"日"):((this.p && this.p[0])||"");
			}
		},
		simpleAttrHandler = function(env) {
			env.ce.setAttribute(this.n, this.v);
		},
		AttrHandler = function(env) {
			var ret = [],
				va = this.v;
			for(var i = 0; i < va.length; ++i) {
				var item = va[i];
				ret.push[env.sh[item.h].call(this, env)];
			}
			env.ce.setAttribute(this.n, ret.join(""));
		},
		/**
		 * r:array s:text value f:lslast value in array Is * Text
		 */
		strSplit_s = function(r, s, f) {
			if(f) {
				var tmp = r[r.length - 1];
				tmp.k = tmp.k + s;
			} else {
				r.push({ k: s, h: "c" });
			}
		},
		strSplit_o = function(r /* array */ , shell /* trim value like {{shell}} */ ) {
			var tmp = shell.split("-");
			var obj = { k: tmp.shift(), h: "s" };
			if(tmp.length) { obj.h = tmp.shift(); }
			if(tmp.length) { obj.p = tmp; }
			r.push(obj);
		},
		strSplit = function(s) { // s.length>4
			var r = [],
				len = s.length,
				si = 0,
				/* parse start index */
				ei, /* end shell ei = s.indexOf("}}",bi+2) */
				shell, /* {{shell}} */
				tmp, /* last shell */
				f /* prev is not shell */ ,
				bi = s.indexOf("{{"); /* begin shell bi = s.indexOf("{{",si) */
			if(bi >= 0) {
				ei = s.indexOf("}}", bi);
				if(ei>0){
				while(true) {
					if(bi > si) {
						r.push({ k: s.substring(si, bi), h: "c" });
						f = true;
					}
					shell = s.substring(bi + 2, ei).trim();
					si = ei + 2;
					if(shell.length) {
						f = false;
						strSplit_o(r, shell);
					} else {
						strSplit_s(r, s.substring(bi, si), f);
						f = true;
					}
					if(si >= len) return r;
					bi = s.indexOf("{{", si);
					if(bi < 0) { strSplit_s(r, s.substring(si), f); return r; }
					ei = s.indexOf("}}", bi);
					if(ei < 0) { strSplit_s(r, s.substring(si), f); return r; }
				}}
			}
			return s;
		},
		strCompile = function(s) {
			var r = strSplit(s);
			if(typeof r != "string") {
				return r.length > 1 ? r : r[0];
				/*
				 * [{k:"",h:"",p:[]},...] :
				 * {k:"",h:"",p:[]}
				 */
			}
			return s; // String;
		},
		simpleAttrHand = function(env) {
			env.ce.setAttribute(this.n, this.v);
		},
		singleAttrHand = function(env) {
			env.ce.setAttribute(this.n, env.sh[this.v.h].call(this.v, env));
		},
		arrayAttrHand = function(env) {
			var item, ret = [],
				vs = this.v,
				len = vs.length;
			for(var i = 0; i < len; ++i) {
				item = vs[i];
				ret.push(env.sh[item.h].call(item, env));
			}
			env.ce.setAttribute(this.n, ret.join(""));
		},
		/* ret.h(env); attribute compiler */
		attrCompile = function(attr) {
			var s = attr.value || "",
				ret = { n: attr.name, v: s, h: simpleAttrHand };
			if(s.length > 4) {
				s = strCompile(s);
				if(typeof s != "string") {
					ret.v = s;
					// is Array
					ret.h = s.length ? arrayAttrHand : singleAttrHand;
				}
			}
			return ret;
		},
		/* ret.h(env); textNode compiler */
		textCompile = function(s /* s = textNode.nodeValue */ ) {
			var len = s.length,
				ret = { v: s, h: "_t" };
			if(len > 4) {
				s = strCompile(s);
				if(typeof s != "string") {
					ret.v = s;
					// is Array
					ret.h = s.length ? "_at" : "_st";
				}
			}
			return ret;
		},
		ch_dir_container = {
			/* simpleTextHand= */
			"_t": function(env) { env.ce.appendChild(doc.createTextNode(this.v)); },
			/* singleTextHand= */
			"_st": function(env) { env.ce.appendChild(doc.createTextNode(env.sh[this.v.h].call(this.v, env))); },
			/* arrayTextHand= */
			"_at": function(env) {
				var item, ret = [],
					vs = this.v,
					len = vs.length;
				for(var i = 0; i < len; ++i) {
					item = vs[i];
					ret.push(env.sh[item.h].call(item, env));
				}
				env.ce.appendChild(doc.createTextNode(ret.join("")));
			},
			"_": function(env) {
				var ele = doc.createElement(this.n),
					as = this.as,
					es = this.es,
					item;
				env.ce.appendChild(ele);
				env.es.push(env.ce);
				env.ce = ele;
				for(var i = 0; i < as.length; ++i) {
					as[i].h(env);
				}
				for(var i = 0; i < es.length; ++i) {
					item = es[i];
					env.dir[item.h].call(item, env);
				}
				env.ce = env.es.pop();
			},
			"list": function(env) {
				var hand = env.dir["_"],
					as = this.as,
					es = this.es,
					item, data = env.cd,
					p = this.p;
				if(data && p && p.length && p[0]) {
					env.ds.push(data);
					data = env.cd = data[p[0]] || [];
					env.is.push(env.ci);
					for(var i = 0; i < data.length; ++i) {
						env.ci = i;
						env.cd = data[i];
						hand.call(this, env);
					}
					env.ci = env.is.pop();
					env.cd = env.ds.pop();
				} else if(data && data.length) {
					env.is.push(env.ci);
					env.ds.push(data);
					for(var i = 0; i < data.length; ++i) {
						env.ci = i;
						env.cd = data[i];
						hand.call(this, env);
					}
					env.cd=env.ds.pop();
					env.ci = env.is.pop();
				}
			},
			"array.empty":function(env){
				if((!env.cd) || (!env.cd.length)){
					env.dir["_"].call(this,env);
				}
			},
			"each": function(env) {
				var hand = env.dir["_"],
					as = this.as,
					es = this.es,
					item, data = env.cd,
					p = this.p;
				if(data && p && p.length && p[0]) {
					env.ds.push(data);
					env.is.push(env.ci);
					for(var i = 0; i < p.length; ++i) {
						env.ci = p[i];
						env.cd = data[env.ci];
						hand.call(this, env);
					}
					env.ci = env.is.pop();
					env.cd = env.ds.pop();
				} else if(data) {
					env.is.push(env.ci);
					env.ds.push(data);
					for(var key in data) {
						env.ci = k;
						env.cd = data[key];
						hand.call(this, env);
					}
					env.cd = env.ds.pop();
					env.ci = env.is.pop();
				}
			},
			"val": function(env) {
				var hand = env.dir["_"],
					items, item, data = env.cd,
					p = this.p;
				if(p && p.length && p[0]) {
					items = p[0].split("\\.");
					env.ds.push(data);
					while(items.length && data) {
						data = data[items.shift()];
					}
					if(items.length === 0) {
						env.cd = data;
						hand.call(this,env);
					}
					env.cd = env.ds.pop();
				}
			},
			"valTag": function(env) {
				var items, item, data = env.cd,
					p = this.p,es = this.es;
				if(p && p.length && p[0]) {
					items = p[0].split("\\.");
					env.ds.push(data);
					while(items.length && data) {
						data = data[items.shift()];
					}
					if(items.length === 0) {
						env.cd = data;
						for(var i = 0; i < es.length; ++i) {
							item = es[i];
							env.dir(item.h).call(item, env);
						}
					}
					env.cd = env.ds.pop();
				}
			}
		},
		childCompile = function(chs) {
			var ret = [],stack = [],ev, as, es, attrs, ch, ele;
			hObj = { r: ret, i: 0, c: chs, t: false, s: "", l: chs.length };
			while(hObj) {
				while(hObj.i < hObj.l) {
					ele = hObj.c[hObj.i++];
					if(ele.nodeType == 1) {
						if(hObj.t) {
							hObj.r.push(textCompile(hObj.s));
						}
						hObj.t = false;
						attrs = ele.attributes;
						ch = (ele.getAttribute("ch-dir") || "_").split("-");
						ev = { n: ele.nodeName, h: (ch.shift()||"_"), p: ch };
						as = ev.as = [];
						es = ev.es = [];
						hObj.r.push(ev);
						for(var i = 0; i < attrs.length; ++i) {
							as.push(attrCompile(attrs[i]));
						}
						as = ele.childNodes.length;
						if(as) {
							stack.push(hObj);
							hObj = { r: es, i: 0, c: ele.childNodes, t: false, s: "", l: as };
							stack.push(hObj);
							break;
						}
						
					} else if(ele.nodeType == 3) {
						hObj.s = hObj.t ? (hObj.s + ele.nodeValue) : ele.nodeValue;
						if(hObj.i>=hObj.l){
							hObj.r.push(textCompile(hObj.s));							
						}else{
							hObj.t = true;
						}
					}
				}
				hObj = stack.pop();
			}
			return ret;
		},
		//		/* ret.h(env) Element compiler */
		//		eleCompile = function(ele) {
		//			var attrs = ele.attributes,
		//				cdir = ele.getAttribute("ch-dir") || "_",
		//				ret = { n: ele.nodeName },
		//				as = ret.as = [],
		//				es = ret.es = [],
		//				lisT, s, ch;
		//			ch = cdir.split("-");
		//			ret.h = ch.shift();
		//			if(ch.length) ret.p = ch;
		//			for(var i = 0; i < attrs.length; ++i) {
		//				as.push(attrCompile(attrs[i]));
		//			}
		//			childCompile(ele.childNodes, ret.es);
		//			return ret;
		//		},
		parseElement = function(ele) {
			var env = { sh: {}, dir: {} },
				chs = childCompile(ele.childNodes);
			$.extend(env.sh, ch_val_buider);
			$.extend(env.dir, ch_dir_container);
			return {
				"shell": function(name, hand) {
					if(hand) {
						env.sh[name] = hand;
						return this;
					} else return env.sh[name];
				},
				"dir": function(name, hand) {
					if(hand) {
						env.dir[name] = hand;
						return this;
					} else return env.dir[name];
				},
				"fill": function(pe, data) {
					env.es = [];
					env.ds = [];
					env.is = [];
					env.ce = pe, env.cd = data;
					for(var i = 0; i < chs.length; ++i) {
						var ch = chs[i];
						env.dir[ch.h].call(ch, env);
					}
					return this;
				}
			};
		},
		parseCode = function(ele) {
			var ele=ele.jquery?ele[0]:ele, hand = parseElement(ele),
				$ele = $(ele),
				bh = util.nochange,
				lses = [];
			return $.extend(hand, {
				"val": function(data) {
					data = bh(data);
					if(false === data) return this;
					$ele.empty();
					var docf = doc.createDocumentFragment();
					this.fill(docf, data);
					ele.appendChild(docf);
					for(var i = 0; i < lses.length; ++i) {
						lses[i]();
					}
					return this;
				},
				"empty": function() {
					$(ele).empty();
					return this;
				},
				"listen": function(h) {
					if(h) lses.push(h);
					return this;
				},
				"before": function(h) {
					if(h) {
						bh = h;
					}
				}
			});
		},
		parseHtmlTemplate = function(c) {
			var $div = $("<div style='display:none;'></div>");
			$div.appendTo($body).html(c);
			var h = parseCode($div[0]);
			$div.remove();
			return $.extend(h, {
				"appendTo": function(pe, data) {
					var docf = doc.createDocumentFragment();
					this.fill(docf, data);
					pe.appendChild(docf);
				}
			});
		};
	return {
		"parse": parseElement,
		"parseCode": parseCode,
		"template": parseHtmlTemplate
	};
});