(function () {
    
    if (typeof MS == 'undefined') {
        MS = {};
    }
    
    //YUI shortcuts
    var Dom = YAHOO.util.Dom,
        Event = YAHOO.util.Event,
        Lang = YAHOO.lang,
        Connect = YAHOO.util.Connect,
        Select = YAHOO.util.Selector,
        JSON = YAHOO.lang.JSON;
    
    var StringUtil = {
        /**
        * Checks whether a string is empty or not
        * @param {String} s
        * @return {Boolean}
        */
        empty : function (s) {
            return (s.length == 0);
        },
        
        /**
        * Checks whether specified email is a valid email id or not
        * @param {String} email
        * @return {Boolean}
        */
        email : function (email) {
            var reg = /^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\.)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$/;
            return reg.test(email);
        },
        
        /**
        * Checks whether the specified number/string length is in the specified range
        * @param {String|Number} num
        * @return {Boolean}
        */
        inRange : function (num, min, max) {
            var a = num.constructor === String ? num.length : num, 
                min = min || 0, max = max || 0;

            return (a >= min && a <= max);
        },
        
        /**
        * Checks whether the specified string is in a valid full name or not i.e 'John Smith',
        * separated by exactly 1 space and should not have any other characters except Alphabets
        * @param {String} s
        * @return {Boolean}
        */
        fullName : function (s) {
            var re = /^([a-z]*)\s{1}([a-z]*)$/i;

            return re.test(s);
        },
        
        /**
        * Compares two values in strict order
        * @param {String} first
        * @param {String} second
        * @return {Boolean} 
        */
        compare : function (first, second) {
            return (first === second);
        }
    };

    
    
    function Validate (form, callback) {
        this.form = Dom.get(form);
        this.callback = callback;

        this.reqElements = [];

        this._init();

        this.ERRORS = {
            10001 : '{name} should not be empty',
            10002 : '{name} should be minimum 6 characters long',
            10003 : 'Invalid {name}',
            10004 : '{name} should be in "Firstname Lastname" format.',
            10005 : '{name} do not match'
        };

        this.processing = false;
    }

    Validate.prototype._init = function () {
        var a = this.form.elements, ln = a.length, i = 0, m;
        for (; i < ln; ++i ) {
            m = a[i].className.match(/\s*(required)\s+validate=([^\s]*)/);
            if (m && m[1] && m[2]) {
                this.setValidationType(a[i], m[2]);
                this.reqElements.push( { element : a[i], type : m[2] } );
            }            
        }
        
        Event.on(this.form, 'submit', this._submit, null, this);
    }

    Validate.prototype.setValidationType = function (el, type) {
        Event.on(el, 'change', this.doValidate, type, this);
    }

    Validate.prototype.doValidate = function (e, type) {
        var targ = Event.getTarget(e) || e, v = this.getValue(targ), result = true, bool, str,
        match = targ.className.match(/flabel=([^\s]*)/i), field_label = match ? match[1] : '';


        switch (type) {
            case 'email':            
                bool = !StringUtil.empty(v) ? 
                                ( StringUtil.email(v) ? true : 10003 ) : 
                                10001;

                str = 'Email Id';
                break;
            case 'password':
                bool = !StringUtil.empty(v) ? 
                                ( StringUtil.inRange(v, 6, 20) ? true : 10002 ) : 
                                10001;

                str = 'Password';
                break;
            case 'blank':
                bool = StringUtil.empty(v) ? 10001 : true;
                
                str = field_label || 'Field';
                break;
            case 'full_name':
                v = Lang.trim(v);
                bool = !StringUtil.empty(v) ?
                            ( !StringUtil.fullName(v) ? 10004 : true ) :
                            10001;
                str = 'Full Name';
                break;
            
            case 'compare':
                v = Lang.trim(v);
                //get the value of the field to be compared
                var match = targ.className.match(/compare=([^\s]*)/i);

                if (match && match[1]) {
                    bool = StringUtil.empty(v) ? 10001 :
                                ( StringUtil.compare(v, this.getValue(match[1])) ? true : 10005 );

                    str = field_label || 'Your passwords';
                }
                break;
        }

        if (bool.constructor !== Boolean) {
            result = false
        }
        
        result ? this.onsuccess(bool, targ, str) : this.onerror(bool, targ, str);

        return result;
    }
    
    //do not handle select boxes :TODO
    Validate.prototype.getValue = function (el) {
        el = Dom.get(el);
        switch (el.tagName.toLowerCase()) {
            case 'select':
            case 'select-multiple':
                var option = el.options[el.selectedIndex];
                return Lang.trim(option.value);
                break;
            default:
                return Dom.get(el).value;        
        }        
    }

    Validate.prototype.onerror = function (code, el, text) {
        this.callback.error.apply(this.callback.scope || this, [el, Lang.substitute(this.ERRORS[code], { name : text })]);
    }

    Validate.prototype.onsuccess = function (code, el, text) {
        this.callback.success.apply(this.callback.scope || this, [el]);
    }

    Validate.prototype._submit = function (e) {
        Event.stopEvent(e);

        //if submit is in progress then return
        if (this.processing) { return; }

        var a = this.reqElements;        
        for (var i = 0, ln = a.length; i < ln; ++i) {
            if(!this.doValidate(a[i].element, a[i].type)) return;
        }
        
        //disable submit button
        var submit_el = Select.query('input[type=submit]', this.form, true);
        submit_el.disabled = true;

        //if beforeSubmit handler is specified then execute that
        if (Lang.isFunction(this.callback.beforeSubmit)) {
            this.callback.beforeSubmit.call(this.callback.scope || this, this.form);
        }
        
        //if normal submission then just submit the form
        if (this.form.getAttribute('no-ajax') !== null) {
            return this.form.submit();
        }

        
        this.processing = true;
        Connect.setForm(this.form);

        Connect.asyncRequest('POST', this.form['action'], {
            success: function(xhr) {
                var resp = JSON.parse(xhr.responseText);
                this.callback.submit.apply(this.callback.scope || this, [resp]);
                submit_el.disabled = false;
                this.processing = false;
            },

            failure: function(xhr) {
                this.callback.submit.apply(this.callback.scope || this, [xhr]);
                submit_el.disabled = false;
                this.processing = false;
            },

            scope : this,

            timeout: 25000
        });

    }
    
    MS.Validate = Validate;
    
})();