/**
 * Validates known tag contents
 * 
 * @author Angelo Selvini <angelo@exmachina.ch>
 * @requires ch.exmachina.bravofly.ui.Pager
 * @requires ch.exmachina.bravofly.utils
 */


if(!ch){
	var ch = {};
}
if(!ch.exmachina){
	ch.exmachina = {};
}
if(!ch.exmachina.bravofly){
	ch.exmachina.bravofly = {};
}
if(!ch.exmachina.bravofly.ui){
	ch.exmachina.bravofly.ui = {};
}

ch.exmachina.bravofly.ui.Validator = function(/* object */ ctorArgs){
	var t, rawDepends;

	this.target = t = ctorArgs["target"];
	if (!t || !t.nodeType || t.nodeType != 1)
		throw new Error("Validator requires target");

	// defaults
	this.name      = t.getAttribute("bf:name") || ctorArgs["name"]   || "validatorWidget"+parseInt(Math.random()*1000, 10);
	this.listening = false;
	

	this.options   =
	{ message    : t.getAttribute("bf:message")     || "Error."
	, must       : t.getAttribute("bf:must")        == "true" ? true : false
	, listen     : t.getAttribute("bf:listen")      == "false" ? false : true
	, showMessage: t.getAttribute("bf:showMessage") == "false" ? false : true
	};

	// Process dependency
	rawDepends   = t.getAttribute("bf:depends");
	if (rawDepends) {
		/* Depends requires 2 or 3 values
		 * 1 - name of the form's control
		 * 2 - the value to match
		 * 3 - optional true|false for radios and checkboxes
		 * If the condition doesn't match the control's validation
		 * is _skipped_.
		 */
		this.depends = rawDepends.split(":");
	}

	this.rule    = t.getAttribute("bf:rule")  || null;
	this.group   = t.getAttribute("bf:group") || null;
	this.regexp  = /.+/;
};

ch.exmachina.bravofly.ui.Validator.prototype = {

	type: "ch.exmachina.bravofly.ui.Validator",

	_listening: false,

	group  : null,
	regexp : null,

	depends: null,

	params : null,
	target : null,
	options: null,

	setOptions: function(/* object */ o){
		if (!o) return;

		var i = 0,
			c = this.options,
			l = [ { type:"s", name:"message"}
				, { type:"s", name:"rule"   }
				, { type:"s", name:"group"   }
				]
		;

		c = ch.exmachina.bravofly.utils.validateOptions(
				{ optsNew: o
				, optsOld: c
				, rules: l
				}
			);
	},

	setParams: function(/* object */ p){
		if (p.rule){
			this.rule = p.rule;
			this._setRule();
		}
	},

	// DOM
	create: function(){
		// summary: setup input
		var e,
			C = this.getConstants(), 
			t = this.target
		;
		if (!this._isTag()) return;
		this._setRule();
	},

	_setRule: function(){
		var re = this.regexp,
			r = this.rule,
			C = this.getConstants()
		;

		switch(r){
			case null:
				re = /.*/;
				break;
			case C.EMAIL:
				re = /^([A-Za-z0-9\._]\.?)+@(\w\.?)+(\.\w{2,4})$/i;
				break;
			case C.PHONE:
				re = /^\+?(\d+(\.|\s|-)?)+$/;
				break;
			case C.NUMBERS:
				re = /^\d+$/;
				break;
			case C.WORDS:
				re = /^\D+$/;
				break;
			default:
				re = new RegExp(r);
				break;
		}
		this.regexp = re;
	},

	draw: function(){
		// summary: evaluate template with parameters
	},

	update: function(){
	},

	validate: function(e){
		// summary: evaluates conditions
		var r, v,
			o  = this.options,
			t  = this.target,
			nn = t.nodeName
		;
		v = this._getValByNodeName(nn);

		// Check dependency
		if (this.depends && (this.depends instanceof Array)){
			var vldCheck = false,
				size    = this.depends.length,
				name    = this.depends[0],
				value   = this.depends[1],
				checked = this.depends[2] == "true" ? true : false,
				reValue = value ? new RegExp(value) : null;

			Element.select(document.body, "*[name='"+ name +"']").each( function(item){

				if (size === 3) {
					// Check for value and checked (radio/checkbox)
					if (item.value == value && item.checked == checked) {
						vldCheck = true;
					}
				} else {
					// Check for value only
					if (!(reValue.test(item.value)) ) {
						vldCheck = true;
					}
				}
			});

			if (!vldCheck){
				// Ignore validation if field depends and its
				// constraints not filled
				this._manageClass(true);
				this._manageMessage(false);
				/*
				*/
				return true;
			}

		}

		// Check if compulsory
		if (o.must && (v === null || v === "" || v === undefined)){
			r = false;

		// Check if matches
		} else if (v) {
			r = this.regexp.test(v);

		// Don't mind value
		} else {
			r = true;

		}
		
		this._manageClass(r);
		this._manageMessage(!r && o.showMessage);

		return r;
	},

	hasGroup: function(){
		return this.group; 
	},

	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;

		if (!o.listen) { return; }

		if (/^(INPUT|TEXTAREA)$/.test(nn) ){
			t.observe("blur", this.delegate(this, this.validate));
		} else if ( nn == "SELECT") {
			t.observe("change", this.delegate(this, this.validate));
		}

	},

	_removeListeners: function(){
		var t = $(this.target);
		this._listening = false;

		t.stopObserving("blur");
	},

	_manageMessage: function(/* boolean */ b){
		b ?  this._addError() : this._hideError();
	},

	_addError: function(){
		var e = this.error
		;
		if (e){
			e.setStyle({display:""});
		} else {
			var d,
				t = this.target,
				C = this.getConstants()
			;
			d = document.createElement("div");
			$(d).addClassName(C.CLASS_ERROR_MESSAGE);
			d.innerHTML = this.options.message;

			e = this.error = $(d);

			if (this.hasParentNode(t))
				t.parentNode.insertBefore(e,t);
		}
	},

	_hideError: function(){
		var e = this.error
		;
		if (e)
			e.setStyle({display:"none"});
	},

	_removeError: function(){
		var e = this.error
		;
		if (e && this.hasParentNode(e))
			e.parentNode.removeChild(e);
	},

	resetError: function(){
		this._manageClass(true);
		this._manageMessage(false);
	},

	_getValByNodeName: function(/* string */ n){
		var v = null;
		switch(n){
			case "TEXTAREA":
			case "SELECT":
			case "INPUT":
				v=this.target.value;
				break;
			default: break;
		}
		return v;
	},
	setValue: function(/* object */ v){
		var t = this.target,
			n = t.nodeName
		;
		switch(n){
			case "INPUT":
			case "TEXTAREA":
			case "SELECT":
				t.value=v;
				break;
			default: break;
		}
		return v;
	},

	_manageClass: function(/* boolean */b){
		// summary: add/remove error class name
		var t = $(this.target),
			c = this.getConstants()["CLASS_ERROR"]
		;

		if (b && t.hasClassName(c))
			t.removeClassName(c);
		else if (!b)
			t.addClassName(c);
	},

	_isTag: function(){
		return (/^INPUT|TEXTAREA|SELECT$/).test(this.target.nodeName);
	},

	_getValue: function(){
		var v,
			t = this.target
		;
		switch (t.nodeName){
			case "INPUT":
				v = t.value;
				break;
			default: break;
		}
		return v;
	},

	isEmpty: function(){
		var t = this.target,
			v = this._getValue()
		;
		return (v!==undefined && v!==null && v!=="") ? false : true;
	},

	getNode: function(){
		return this.target;
	},

	getType: function(){
		return this.type;
	},

	destroy: function(){
		var p = this.pagers
		;

		this.setListening(false);
		this._manageClass(false);
		this._manageMessage(false);
	},

	getConstants: function(){
		return window.ch.exmachina.bravofly.ui.Validator.getConstants();
	},

	hasParentNode: ch.exmachina.bravofly.utils.hasParentNode,
	delegate: ch.exmachina.bravofly.utils.delegate
};

ch.exmachina.bravofly.ui.Validator.getConstants = function() {
	var cnst =
	{ EMAIL: "email"
	, PHONE: "phone"
	, WORDS: "words"
	, NUMBERS: "numbers"
	, CLASS_ERROR: "vg_vldError"
	, CLASS_ERROR_MESSAGE: "vg_vldErrorMsg"
	};
	return cnst;
};


