/**
 * 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,
		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);
	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) : 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,

	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);

		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,
			r = 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 (!r)
				check = false;
		}

		return check;
	},

	_validateList: function(/* array */g){
		//	summary: check list of validators and return false if any fails
		var i,r,
			g = this.group,
			c = true
		;
		for (i=0;i<g.length;i++){
			r = g[i].validate();
			if (!r) c = false;
		}
		return c;
	},

	_validateGroup: function(/* object */ o){
		//	summary: validate each validator and count true occurencies
		var i,r,
			g  = o.group,
			v  = o.required,
			n  = o.name
			c  = true,
			e  = 0
		;

		for (i=0;i<g.length;i++){
			if (!g[i].isEmpty())
				e++;

			r = g[i].validate();

			if (!r)
				c = false;
		}

		if (e<v){
			this._manageGroupMessage(g,n,true);
		}else{
			this._manageGroupMessage(g,n,false);
		}

		return (e >= v && c==true) ? true : false;
	},

	_manageGroupMessage: function(/* array */g, /* string */ gn, /* boolean */ b){
		var i, d,
			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
		for (i=0;i<g.length;i++){
			d = $(g[i].getNode());

			if (b) {
				d.addClassName(n);
				if (i==0 && o.showMessage)
					this._addError(gn,d);

			} else {
				if (d.hasClassName(n))
					d.removeClassName(n);

				if (i==0 && o.showMessage)
					this._removeError(gn);
			}
		}
	},

	_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;

		// Add error only if not already defines
		if (!e.hasOwnProperty(n)){
			var s,
				o = this.options,
				C = this.getConstants(),
				CV= ch.exmachina.bravofly.ui.Validator.getConstants()
			;
			e[n] = document.createElement("div");
			s = o.customMessages[n];
			e[n].innerHTML = s ? s : o.message;
			$(e[n]).addClassName(C.CLASS_ERROR_GROUP);

			var p = this.hasParentNode(d) ? d.parentNode : null;
			if (!p) return;

			if (d.previousSibling
				&& d.previousSibling.nodeType==1 
				&& $( d.previousSibling ).hasClassName(CV.CLASS_ERROR_MESSAGE)
			)
				p.insertBefore(e[n], d.previousSibling);
			else
				p.insertBefore(e[n], d);
		}
	},

	_removeError: function(n){
		var e = this.errors
			d = e[n]
		;

		// 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){
		//	summary: parse strings like key:0,key2:1...
		var i,g,
			rN = /^\w+$/,
			rV = /^\d+$/,
			o = {}
		;

		g = s.replace(/\s/g,"").split(",");

		for (i=0; i<g.length;i++){
			a = g[i].split(":");
			if (rN.test(a[0]) && rV.test(a[1]))
				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;
};

