var checking_label;

var Validator = new Class({

    //Implements: [Events, Options],
    options: {
        onValidate: function (response, obj) {
	    if ($type($('validator_error_' + response.name))) {
	        $('validator_error_' + response.name).remove();
	    }
        //obj.message.set('class', 'message').set('text', response.message);
        },
        onInvalidate: function (response) {
	    var hash    = new Hash(response.message);
	    this.result = false;
            hash.each(function(val, key) {
	        if (!$type($('validator_error_' + key))) {
		    //console.log(1);
		    //console.log('set:'+key+'--'+val);
		    new Element('span', {
		        'class': 'error',
		  	'id': 'validator_error_' + key,
		  	'styles': {
			    'display': 'block',
			    'left': 20
			}
		    }).setHTML(val).injectAfter($(key));
		}
		else {
		    //console.log('replace:'+key+'--'+val);
		    $('validator_error_'+key).setHTML(val);
		}
            });

        },
        onError: function (obj, response) {
       	    obj.message.set('class', 'error').set('text', 'An error has occurred');
        },
        onLoading: function (obj){
        },
	onForm: function(obj){
	    //test on form validation
	},
	onSuccess: function () {
	    var response = [];
	    if (this.req.response && this.req.response.text.length>0){
	        response = Json.evaluate(this.req.response.text);
	    }
	    if (this.local_messages.length>0) {
	        response = this.local_messages.merge(response);
	      	this.local_messages = [];
	    }
	    if (response) {
	        test = true;
		$A(response).each(function(el) {
		    if (el.type.toInt() > 1) {
	    	        test = false;
			this.onInvalidate(el);
		    } else if (test) {
			this.onValidate(el);
		    }
		},this);
		this.onFinish(this, response);
	    } else {
	      	  this.onError(this, response);
	    }
        },
	url: '/batch.validator.php'
    },

    initialize: function (form, options) {
        this.setOptions(options);
	this.requestedElement;
	this.local_rules = ['required', 'minlength','maxlength'];
	this.result = true;
	this.messages;
	this.local_messages = [];
	this.url = this.options.url;
        this.req = new Json.Remote(this.url,{
            onRequest: this.onLoading.bind(this),
            onComplete: this.onLoaded.bind(this),
            onSuccess: this.onSuccess.bind(this),
            onFailure: this.onError.bind(this)
        });
      
  	if(!form) {
	    return;
	}
  	  
        this.form = $(form);
	this.elements = $A(this.form.getElements('input'));
	this.elements.merge(this.form.getElements('select'));	  
	this.elements.each(function(el) {
	    el.addEvent('blur',this.onBlur.bindWithEvent(this));
	},this);
	  
	this.form.getElements('select').each(function(el) {
	    el.addEvent('change',this.onBlur.bindWithEvent(this));
	},this);
	//load messages for local matching
	new Json.Remote(this.url+'?get_message=1',{
	    onComplete: function (txt) {
	        this.messages = new Hash(txt);
            }.bind(this)
        }).send();	  
	  
	//this.onFinish.bind(this);
	return this;
    },
    validateElement: function (el2) {
    	//console.log(el2);
        ret = [];
        if ($type(el2.getProperty('rule'))) {
            this.requestedElement = el2.id;
            //check if some rules requires server side parsing
            var rules = el2.getProperty('rule').split('|');
            var test = false;
            //console.log(rules);
            var local_rules = rules.filter(function(el3, index) {
                var res;
                this.local_rules.each(function(el4) {
                    if (el3.indexOf(el4)>-1) {
                        res = el4;
                    }
                });
                return res;
            }.bind(this));

            var remote_rules = rules.filter(function(el3, index) {
                if (!local_rules.contains(el3)) {
                    return el3;
                }
            }.bind(this));

	    var test = true;
            if (local_rules.length>0) {
                //console.log(local_rules);
                //process local rules
                local_rules.each(function (el3) {
                    this.local_messages.push(this.processLocal(el3, el2.getValue(), el2));
                }.bind(this));
            }
                                //console.log('local messages');
            if (test && remote_rules.length>0) {
	        ret.push({
                    'field_name': el2.getProperty('id'),
                    'field_value': el2.getValue(),
                    'field_rule': remote_rules.join('|')
                });	
                test = false;
					
            }
            //console.log(ret);
            return ret;
        }
    },
    validate: function (form, options) {
        form = $(form);
        this.elements = $A(form.getElements('input'));
        this.elements.merge(form.getElements('select'));
        this.result = true;
        var param = [];
        this.local_messages = [];
        this.elements.each(function(el2) {
            param = this.validateElement(el2);
        }, this);
        this.$events = []; //reset event prior to add new one
        this.setOptions(options);
            
	if (this.local_messages.length>0 ) {
	    this.fireEvent('onSuccess', this);
	}
        if ($type(param) == 'array' && param.length > 0) {
            this.req.send(param);
            this.fireEvent('onSuccess', this);
        }
    },
    onBlur: function (e) {
        (function(e){
	    var evt = new Event(e);
	    el2 = $(evt.target);
	    param = this.validateElement(el2);
            if ($type(param) == 'array' && param.length > 0) {
	        this.req.send(param);
            }
	    return this.fireEvent('onSuccess', this);
			
	}).delay(100,this,e);
    },

    onValidate: function (data) {
        //this.form.reset();
        return this.fireEvent('onValidate', [data, this]);
    },

    onInvalidate: function (data) {
        return this.fireEvent('onInvalidate', [data, this]);
    },

    onError: function () {
    	return this.fireEvent('onError', this);
    },

    onSuccess: function () {
        return this.fireEvent('onSuccess', this);
    },

    onLoading: function () {
        if (checking_label== '') {
	    checking_label='Checking...';
	}
	if (this.requestedElement) {
	    new Element('div', {
	        'class': 'wait',
		'id': 'validator_wait_' + this.requestedElement,
		'styles': {
		    'display': 'block',
		    'left': 20,
		    'width':100,
		    'padding-left':20
		}
	    }).setHTML(checking_label).injectAfter(this.requestedElement);//.morph({'left':0, 'opacity':1})		
	}
        //this.submitBtn.set({ value: 'Loading...', disabled: true });
        return this.fireEvent('onLoading', this);
    },

    onLoaded: function () {
        //this.submitBtn.set({ value: this.submitBtn.orgVal, disabled: false });
        return this.fireEvent('onLoaded', this);
    },
    onFinish: function (obj, response) {
        this.response = response;
    	
	if ($type(response[0]) && $type(response[0].name) && $type($('validator_wait_' + response[0].name))) {
	    $('validator_wait_' + response[0].name).remove();
	    this.requestedElement = '';
	}	
	return this.fireEvent('onFinish', this);
    },
	
    //specific function that can be done in javascript
    processLocal: function (type, value, name) {
        var full_type = type;
	var name = name.getProperty('id');
	//clean the type
	var re = new RegExp('[a-z]*', 'i');
	type = type.match(re);
	//console.log(this.messages);
	var mess = this.messages.get(type);
	//console.log(type);
	if (type == 'minlength') {
	    var re = new RegExp('minlength\{([0-9]*)\}', 'i');
	    var minval = full_type.match(re);
	    if (parseInt(value.length) < parseInt(minval[1])) {
	        var obj = {};
		obj[name] = mess.replace('!1', minval[1]);
		return  {'type':100, 'name': name, 'message':obj};
	    } else {
	        return {'type':1, 'name': name, 'message':'ok'}
	    } 
	} else if (type == 'maxlength') {
	    var re = new RegExp('maxlength\{([0-9]*)\}', 'i');
	    var maxval = full_type.match(re);
	    if (parseInt(value.length) > parseInt(maxval[1])) {
	        var obj = {};
		obj[name] = mess.replace('!1', maxval[1]);
		return  {'type':100, 'name': name, 'message':obj};
	    } else {
	        return {'type':1, 'name': name, 'message':'ok'}
	    }
	} else {
            var pattern = mess[0].substring(1, mess[0].length-1);
            //console.log(pattern);
            var re = new RegExp(pattern, 'i');
            //console.log(re.exec(value));
		 	
            if (value==0 || !re.exec(value)) {
                //console.log(mess[1]);
		var obj = {};
		obj[name] = mess[1];
		return  {'type':100, 'name': name, 'message':obj};
            } else {
                return {'type':1, 'name': name, 'message':'ok'}
            }
        }
    }
});

Validator.implement(new Options(), new Events());
