/**
 * Manage the dom of a list of trips given a datasource
 * 
 * @author Angelo Selvini <angelo@exmachina.ch>
 */


if(!ch){
	var ch = {};
}
if(!ch.exmachina){
	ch.exmachina = {};
}
if(!ch.exmachina.bravofly){
	ch.exmachina.bravofly = {};
}
if(!ch.exmachina.bravofly.manager){
	ch.exmachina.bravofly.manager = {};
}

ch.exmachina.bravofly.manager.ValidatorGroup= function(/* object */ctorArgs){
	var t,ag,fg,n,f,tg,
		o = {}
	;

	this.target = t = ctorArgs["target"];
	if (!t || !t.nodeType || t.nodeType != 1)
		throw new Error("Validator Group requires target");

	// defaults
	this.name      = ctorArgs["name"]   || "validatorGroupWidget"+parseInt(Math.random()*1000,10);
	this.listening = false;

	this.options   =
	{ message    : t.getAttribute("bf:message")     || "Validation group error."
	, showMessage: t.getAttribute("bf:showMessage") == "false" ? false : true
	, customMessages: {}
	};

	ag = t.getAttribute("bf:groups");
	this.ruleGroup = ag ? this._parseAttrGroup(ag,true) : null;

	tg = t.getAttribute("bf:groupTargets");
	this.targetGroup = tg ? this._parseAttrGroup(tg,false) : null;


	fg = t.getAttribute("bf:fields");

	this.fields = fg ? this._parseAttrFields(fg) : null;

	// Parse personal messages
	for (n in this.ruleGroup){
		var an = "bf:message"+ n.toLowerCase(),
			av = t.getAttribute(an)
		;
		if (av){
			this.options.customMessages[n.toLowerCase()] = av;
		}
	}
	this.event  = t.getAttribute("bf:event") || "submit";

	this.group  = [];
	this.errors = {};
};

ch.exmachina.bravofly.manager.ValidatorGroup.prototype = {
	type: "ch.exmachina.bravofly.manager.ValidatorGroup",

	options : null,
	params  : null,
	group   : null,
	errors  : null,
	fields  : null,

	messageBuffer: {},
	targetGroup: null,

	setOptions: function(/* object */ o){
		if (!o) return;

		var i = 0,
			c = this.options,
			l = [ { type:"s", name:"message"}
				, { type:"s", name:"fields" }
				, { type:"s", name:"rule"   }
				]
		;

		c = ch.exmachina.bravofly.utils.validateOptions(
				{ optsNew: o
				, optsOld: c
				, rules: l
				}
			);
	},

	setParams: function(/* object */ p){
		if (!p) return;

		if (p.event && (/^(submit)$/).test(p.event)){
			this.event = p.event;
		}
		if (p.groups){
			this.ruleGroup = this._parseAttrGroup(p.groups,true);
		}

		if (p.fields){
			this.fields = this._parseAttrFields(p.fields);
		}

	},

	// Prepare dom
	create: function(){
		// summary: look for validators
		var t = this.target,
			l = $(t).select("*[bf:type]")
		;
	},
	
	// Attach entries
	draw: function(){
		var f = this.fields;
		if (this.fields)
			this._fillFields();
		
	},

	_fillFields: function(){
		// summary: fill in validator automagically
		var i,n,
			f = this.fields,
			g = this.group
		;

		for (i=0;i<g.length;i++){
			n = g[i].name;
			if (f.hasOwnProperty(n)){
				g[i].setValue(f[n]);
			}
		}
	},

	// Update data
	update: function(){
		// Avoid useless chars for now
		this.draw();
	},

	setListening: function(/* boolean */ b){
		if (b && !this._listening)
			this._addListeners();
		else if (!b)
			this._removeListeners();
	},

	_addListeners: function(){
		var o  = this.options,
			t  = $(this.target),
			nn = t.nodeName
		;
		this._listening = true;

		this._monitor(true, this.event);
	},

	_removeListeners: function(){
		var t = $(this.target);
		this._listening = false;

		this._monitor(false, this.event);
	},

	_monitor: function(/* boolean */ b, /* string */ e){
		var t = this.target;
		if (b){
			this.target.observe(e, this.delegate(this, this.handleEvent));
			if (t && t.nodeName == "FORM"){
				t.validateAndSubmit = this.delegate(this, this._submit,e);
			}
		}
		else {
			this.stopObserving(e);
		}
	},
	
	_submit: function(e){
		var t = this.target,
			r = this.validate()
		;

		if (r && t.nodeName == "FORM"){
			t.submit();
		}

		return false;
	},

	handleEvent: function(e){
		Event.stop(e);
		this._submit();
	},

	validate: function(){
		var i, n, grp, dom,
			r = {valid:true},
			check = true,
			rg = this.ruleGroup,
			C = this.getConstants(),
			l = this._parseGroups()
		;


		for (n in l){

			grp = l[n];
			if (n == C.SINGLES || !rg[n]){
				if (grp.length){
				r = this._validateList(grp);
				}
			}else{
				r = this._validateGroup({group:grp,name:n,required:rg[n]});
			}

			if (!dom && r.dom){dom = r.dom;}
			if (!r.valid){
				check = false;
			}
		}

		if (dom){
			Element.scrollTo(dom.error||dom.target);
		}

		return check;
	},

	_validateList: function(/* array */ grp){
		//	summary: check list of validators and return false if any fails
		var i,r,dom,
			g = this.group,
			c = true
		;
		for (i=0;i<g.length;i++){
			r = g[i].validate();
			if (!r){
				c = false;
				if (!dom) { dom = g[i];}
			}
		}
		return {valid:c, dom: dom};
	},

	_validateGroup: function(/* object */ o){

		//	summary: validate each validator and count true occurencies
		var i,r,dom,
			g  = o.group,
			v  = parseInt(o.required,10),
			n  = o.name,
			c  = true,
			e  = 0
		;

		for (i=0;i<g.length;i++){
			if (!g[i].isEmpty()){
				//if (!dom){ dom = g[i]; }
				e++;
			}

			r = g[i].validate();

			if (!r){
				if (!dom){ dom = g[i];}
				c = false;
			}
		}

		if (e<v){
			// If just no errors but just no fields filled in
			// take the first group element's target
			var errDom = this._manageGroupMessage(g,n,true);
			if (!dom){ dom = errDom;}
		}else{
			this._manageGroupMessage(g,n,false);
		}

		return { valid: ((e >= v && c===true) ? true : false), dom: dom};
	},

	_manageGroupMessage: function(/* array */g, /* string */ gn, /* boolean */ b){
		var i, d, dom,
			o = this.options,
			C = this.getConstants(),
			n = C.CLASS_GROUP_REQUIRED
		;

		// Add class for each Validator in group
		// Add error message before the first one
		// if there are no associated target
		for (i=0;i<g.length;i++){
			d = $(g[i].getNode());

			if (b) {
				d.addClassName(n);
				if (i===0 && o.showMessage)
					dom = this._addError(gn,d);

			} else {
				if (d.hasClassName(n))
					d.removeClassName(n);

				if (i===0 && o.showMessage)
					this._removeError(gn);
			}
		}
		return dom;
	},

	_addError: function(n,d){
		//	summary: add error message
		//	description: by convention error message is placed
		// 		before first Validator of the group.
		//		Check if the Validator error is already present
		//		monitoring the presence of a sibling with its
		//		error class.
		var e = this.errors,
			t = this.targetGroup
		;

		// Add error only if not already defines
		var s, dom,
			o = this.options,
			C = this.getConstants(),
			CV= ch.exmachina.bravofly.ui.Validator.getConstants()
		;

		if (t && t.hasOwnProperty(n)){
			// Manage error in named container
			var node = $(t[n]);
			node.addClassName(C.CLASS_ERROR_GROUP);

			if (node){
				var message = node.select(".bf-message")[0];
				// Save old title for further reuse

				if (message){
					if (!this.messageBuffer.hasOwnProperty(n)){
						this.messageBuffer[n] = message.innerHTML;
					}
					s = o.customMessages[n];
					message.innerHTML = s ? s : o.message;
				}
			}
			dom = node;

		} else {
			if (e.hasOwnProperty(n)){return;}

			var p = this.hasParentNode(d) ? d.parentNode : null;
			if (!p) return;

			e[n] = document.createElement("div");
			s = o.customMessages[n];
			e[n].innerHTML = s ? s : o.message;
			$(e[n]).addClassName(C.CLASS_ERROR_GROUP);

			// Just guess where to add
			if (d.previousSibling
				&& d.previousSibling.nodeType==1 
				&& $( d.previousSibling ).hasClassName(CV.CLASS_ERROR_MESSAGE)
			)
				dom = p.insertBefore(e[n], d.previousSibling);
			else
				dom = p.insertBefore(e[n], d);
		}
		// Reference to error node
		return {error:dom};
	},

	_removeError: function(n){
		var e = this.errors,
			t = this.targetGroup,
			C = this.getConstants(),
			d = e[n]
		;
		if (t && t.hasOwnProperty(n)){
			var node = $(t[n]);
			node.removeClassName(C.CLASS_ERROR_GROUP);

			if (node){
				var message = node.select(".bf-message")[0];
				message && (message.innerHTML = this.messageBuffer[n]);
			}

		} else {
			// Remove dom, could be dangerous
			// to keep it in place
			if (d)
				d.parentNode.removeChild(d);

			delete e[n];
		}
	},

	_parseAttrFields: function(/* string */ f){
		var p = null
		;

		if (f){
			try{
				p = f.evalJSON();
			}catch(err){
				//console.debug("error %o-%o",err,f);
			}
		}
		return p;
	},
	_parseGroups: function(){
		//	summary: create groups to be checked

		var i,  w, wg,
			C = this.getConstants(),
			g = this.group,
			l = {}
		;

		l[C.SINGLES]=[];

		for (i=0;i<g.length;i++){
			w = g[i];

			// Check apart linked validators
			wg = w.hasGroup();

			if (wg) {
				// Create array of linked widgets
				if (!l.hasOwnProperty(wg)){
					l[wg]=[];
				}

				l[wg].push(w);

			} else {
				l[C.SINGLES].push(w);
			}
		}

		return l;
	},

	_parseAttrGroup: function(/* string */ s, /* strict */ strict){
		//	summary: parse strings like key:0,key2:1...
		var i,g,a,
			rN = /^\w+$/,
			rV = /^\d+$/,
			o = {}
		;

		g = s.replace(/\s/g,"").split(",");

		for (i=0; i<g.length;i++){
			a = g[i].split(":");
			if (strict && rN.test(a[0]) && rV.test(a[1]))
				o[a[0]]=a[1];
			else if (!strict)
				o[a[0]]=a[1];
		}

		return o;
	},

	addValidator: function(/* ch.exmachina.bravofly.ui.Validator */ v){
		if (v && v.getType && v.getType() == "ch.exmachina.bravofly.ui.Validator")
			this.group.push(v);
	},

	getType: function(){
		return this.type;
	},

	getNode: function(){
		return this.target;
	},

	length: function(){
		return this.list.length;
	},

	// Remove everything
	destroy: function(){
		var n,
			e = this.errors
		;
		for (n in e)
			this._removeError(n);
	},

	getConstants: function(){
		return window.ch.exmachina.bravofly.manager.ValidatorGroup.getConstants();
	},

	hasParentNode: ch.exmachina.bravofly.utils.hasParentNode,
	delegate: ch.exmachina.bravofly.utils.delegate
};

ch.exmachina.bravofly.manager.ValidatorGroup.getConstants = function() {
	var cnst =
	{ SINGLES: "_MAIN_"
	, CLASS_ERROR_GROUP: "vg_vldGrpError"
	, CLASS_GROUP_REQUIRED: "vg_vldGrpRequired"
	};
	return cnst;
};


