var Validator_Element = new Class({
    valid: true,
    validate_input: true,
    params: {},
    validators: {
        min_length: function(v, param) {
            v = String(v);
            if (v.length < parseInt(param)) {
                return false;
            }
            return true;
        },
        max_length: function(v, param) {
            v = String(v);
            if (v.length > parseInt(param)) {
                return false;
            }
            return true;
        },
        exact_length: function(v, param) {
            v = String(v);
            if (v.length != parseInt(param)) {
                return false;
            }
            return true;
        },
        alpha: function(v, param) {
            v = String(v);
            if (v.match(/[^a-zA-Z\s]/)) {
                return false;
            }
            return true;
        },
        alpha_dash: function(v, param) {
            v = String(v);
            if (v.match(/[^a-zA-Z0-9_\-]/)) {
                return false;
            }
            return true;
        },
        alpha_numeric: function(v, param) {
            v = String(v);
            if (v.match(/[^a-zA-Z0-9]/)) {
                return false;
            }
            return true;
        },
        numeric: function(v, param) {
            v = parseFloat(v);
            if (isNaN(v)) {
                return false;
            }
            return true;
        },
        integer: function(v, param) {
            v = String(v);
            if (v != '' && !v.match(/^\b[\d]+\b$/i)) {
                return false;
            }
            return true;
        },
        time: function(v, param) {
            v = String(v);
            if (v != '' && !v.match(/^([0-1][0-9]|[2][0-3]):([0-5][0-9])$/i)) {
                return false;
            }
            return true;
        },
        is_natural: function(v, param) {
            if (v.match(/[^0-9]/)) {
                return false;
            }
            return true;
        },
        is_natural_no_zero: function(v, param) {
            var v = String(that.getValue());
            if (v.match(/[^1-9]/)) {
                return false;
            }
            return true;
        },
        allowed_charset: function(v, param) {
            var v = String(that.getValue());
            var re = new RegExp("[^" + param + "]");
            if (v.match(re)) {
                return false;
            }
            return true;
        },
        required: function(v, param) {
            if (v == '') {
                return false;
            }
            return true;
        },
        terms: function(v, param) {
            if ('' == v) {
                return false;
            }
            return true;
        },
        house: function(v, param) {
            if ('' == v) {
                return false;
            }
            return true;
        },
        email: function(v) {
            v = String(v);

            if (v != '' && !v.match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i)) {
                return false;
            }
            return true;
        },
        mobile: function(v) {
            v = String(v);
            str = v.replace(/ /g, '');
            if (!str.match(/^\+?(?:44|0|44 0)\s?7[5789][\s\d]{8}$/i)) {
                return false;
            }
            return true;

        },

        phone: function(v) {
            v = String(v);
            if (v != '' && !v.match(/^\b[\d\s]+\b$/i)) {
                return false;
            }
            return true;
        },
        valid_emails: function(v) {
            v = String(v).split(',');
            for (var i = 0, i2 = v.length; i < i2; i++) {
                v[i] = v[i].replace(/\n/g, "");
                if (!v[i].match(/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i)) {
                    return false;
                }
            }
            return true;
        },
        checkAge: function(d, m, y) {
            /* the minumum age you want to allow in */
            var min_age = 13;
            var max_age = 26;

            /* change "age_form" to whatever your form has for a name="..." */
            var year = parseInt(y);
            var month = m - 1;
            var day = d;

            var minDate = new Date((year + min_age), month, day);
            var maxDate = new Date((year + max_age), month, day);

            var today = new Date;


            if ((today.getTime() - minDate.getTime()) < 0 || (today.getTime() - maxDate.getTime()) > 0) {
                return false;
            } else {
                return true;
            }
        },


        //:TODO: these are not implemented yet
        valid_url: function(v, param) {
            return true;
        },
        valid_ip: function(v, param) {
            return true;
        },
        matches: function(v, param) {
            return true;
        }
    },
    initialize: function(element, form_validator, type, params) {
        this.params = Object.extend({
            initial_status: '',
            initial_error: '',
            validated: false
        },
			params || {}
		);
        this.form = form_validator;
        this.type = type;
        this.validation_url = window.location.href.replace(/#.*$/ig, ' ');
        this.element = $(element);
        this.element.validator = this;
        this.setupElementNodes();
        this.setupErrorNodes();
        this.setupLabelNode();
        this.setupObservers();

        this.validators.valid_email = this.validators.email;
        this.validators.isset = this.validators.required;
        this.validators.is_numeric = this.validators.numeric;
    },
    setupElementNodes: function() {
        if (this.type == 'radio') {
            this.element_container = this.element;
            this.element_group = $(this.element.parentNode.parentNode);
        }
        else {
            this.element_container = $(this.element.parentNode);
            if (this.element.parentNode.id == 'day' || this.element.parentNode.id == 'month' || this.element.parentNode.id == 'year') {
                this.element_group = $(this.element.parentNode.parentNode.parentNode);
            } else {
                this.element_group = $(this.element.parentNode.parentNode);
            }
        }
    },
    setupErrorNodes: function() {
        //TODO: Set these values when form is prefilled
        this.error_group = this.element_group.getElements('.errorInput');
        if (this.error_group) {
            this.error = $(this.error_group.firstChild);
        } else {
            this.error_group = null;
            this.error = null;
        }
    },
    setupLabelNode: function() {
        if (this.type == 'radio') {
            this.label = this.element.parentNode.parentNode.getElements('p');
        } else if (this.element.value == 'DD' || this.element.value == 'MM' || this.element.value == 'YYYY') {
            this.label = new Array($('dob'));
        } else {
            this.label = $$('label[for=' + this.element.id + ']');
        }
        if (this.label[0]) {
            this.label = this.label[0];
            this.label_required = this.label.getElements('.required');

            var aSpans = this.label.getElements('span');

            for (var i = 0; i < aSpans.length; i++) {
                var span = aSpans[i];
                if (span.innerHTML == '!') {
                    this.label_mark = span;
                }
            }
        }
    },
    setupObservers: function() {
        if (this.type == 'checkbox') {//TODO: Fix Safari Bug, no focus onblur...
            this.element.addEvent('click', this.validate.bind(this));
        } else if (this.type == 'radio') {
            aInputs = this.element.getElements('input');
            for (var i = 0; i < aInputs.length; i++) {
                aInputs[i].addEvent('click', this.validate.bind(this));
            }
        } else {
            this.element.addEvent('blur', this._validate.bind(this));
        }
        if (this.type != 'radio' && this.type != 'checkbox') {
            this.element.addEvent('focus', this.setFocus.bind(this));
        }
    },
    removeObservers: function() {
        //TODO: Only remove observers I\'ve created		
        this.validate_input = false;
    },
    _validate: function() {
        if (this.validate_input) {
            this.validate();
        }
    },
    validate: function(params, onSuccess) {
        var method = 'post';
        if (!params) {
            var params = 'ajax=true' + '&' + '_submitted=' + this.form.form.id + '&' + this.element.id + '=' + this.getValue();
        }
        if (!onSuccess) {
            var onSuccess = this._parseXML.bind(this);
        }
        this.trimValue();

        //[fs] extracts validation tests + parameters from class names		
        var aClasses = this.element.get('class').toLowerCase().split(' ');
        var oClasses = {};
        var aClass, v, sErr;

        while (aClass = aClasses.shift()) {
            aClass = aClass.split('-');
            if (aClass.length > 1 && 'js' == aClass[0]) {
                aClass.push(1);

                oClasses[aClass[1]] = aClass[2];
            }
        }

        //[fs]
        for (var k in oClasses) {
            if (typeof this.validators[k] == 'undefined') {
                continue;
            }

            elementValue = this.getValue();

            if (this.element.getParent().hasClass('dob')) {
                if ($('day').getFirst().value != '' && $('day').getFirst().value != 'DD' && $('month').getFirst().value != '' && $('month').getFirst().value != 'MM' && $('year').getFirst().value != '' && $('year').getFirst().value != 'YYYY') {
                    var dobResult = this.validators.checkAge($('day').getFirst().value, $('month').getFirst().value, $('year').getFirst().value);

                    if (!dobResult && this.element.parentNode.id == 'year') {
                        this.setInvalid('Sorry, Think Big is a program for 13 to 25 year olds', params);

                        return;
                    }
                }
            }



            if (!this.validators[k](elementValue, oClasses[k])) {
                if (this.element.hasClass('js-contact')) {
                    k = 'contact';
                }

                if ($defined(this.form.aErrorTxt[k])) {


                    sErr = this.form.aErrorTxt[k].replace(/%s/, this.getLabel());
                    sErr = sErr.replace(/%s/, oClasses[k]);
                } else {
                    sErr = 'There is an error';
                }

                this.setInvalid(sErr, params);

                return;
            }

        }
        this.setValid();
        this.form.validationRequested = true;
    },
    getLabel: function() {
        if (1 == this.label.nodeType
			&& 0 < this.label.childNodes.length) {
            return this.label.firstChild.nodeValue;
        } else {
            return '';
        }
    },
    getValue: function() {
        if (this.type == 'checkbox') {
            if (this.element.checked) { return 'on'; }
            else { return ''; }
        }
        else if (this.type == 'radio') {
            aRadioInputs = this.element.getElements('input');

            var button = new Array;

            for (var i = 0; i < aRadioInputs.length; i++) {
                button[i] = $(aRadioInputs[i].id);
            }

            var set = false;
            for (var i = 0; i < button.length; i++) {
                if (button[i].checked) {
                    set = true;
                }
            }

            if (set) {
                return true;
            } else {
                return '';
            }
        }
        else if (this.type == 'select-one') {
            if (this.element.value == "0") { return ''; }
            else { return this.element.value; }
        }
        else { return this.element.value; }
    },
    setFocus: function() {
        this.element_container.removeClass('iconFail');
        this.element_container.removeClass('iconPass');
        this.element_container.addClass('iconFocus');
        this.element.addClass('focus');
    },
    trimValue: function(v) {
        if ('text' != this.type) {
            return;
        }
        this.element.value = this.element.value.replace(/^\s+/, "").replace(/\s+$/, "");
    },
    setValue: function(v) {
        switch (this.type) {
            case 'select':
            case 'select-one':

                //[fs] not sure whether .value = v will work for this 
                //if it doesn't, uncomment the break and write code here
                //break;

            case 'select-multiple':
                //break;

            case 'text':
            default:
                this.element.value = v;
                break;
        }
    },
    setValid: function(v) {
        this.valid = true;
        //if (this.type != 'checkbox' && this.type != 'radio') {
        if (this.type != 'checkbox' && this.type != 'radio') {
            this.element_container.removeClass('iconFail');
            this.element_container.removeClass('iconFocus');
            this.element_container.addClass('iconPass');
            this.element.removeClass('invalid');
            this.element.removeClass('focus');
            this.element.addClass('valid');
        }

        if (v == 'mgm') {
            elements = $$('.line');
            for (var i = 2; i < elements.length; i++) {
                elements[i].setStyle('display', '');
            }
        }

        this.removeErrors();
    },
    setInvalid: function(error, v) {
        this.valid = false;
        if (this.type != 'checkbox' && this.type != 'radio') {
            this.element_container.removeClass('iconFocus');
            this.element_container.removeClass('iconPass');
            this.element_container.addClass('iconFail');
            this.element.removeClass('valid');
            this.element.removeClass('focus');
            this.element.addClass('invalid');
        }
        this._setErrorMarks();

        this._setErrorMessage(error, v);
    },
    removeErrors: function() {
        if (this.error_group && this.slideError && this.slideError.open) {
            this.slideError.toggle();
        }
        if (this.label_required && this.label && this.label.getParent()) {
            this.label.getParent().removeClass('error');
            //this.label_required.set({"html":'*',"class":'required'});
        }
    },
    removeRequired: function() {
        if (this.label_required) {
            this.label_required.setStyle('display', 'none');
        }
    },
    addRequired: function() {
        if (this.label_required) {
            this.label_required.setStyle('display', '');
        }
    },
    isValid: function() {
        this.validate();
        return this.valid;
    },
    _removeError: function(event) {
        if (this.error) {
            this.error.remove();
            this.error = null;
        }
        if (this.error_group) {
            this.error_group.remove();
            this.error_group = null;
        }
    },
    _setErrorMarks: function() {
        if (this.label && !this.label_mark) {
            if (this.label_required && this.label != null && this.label.getParent() != null) {
                this.label.getParent().addClass('error');
                //this.label.addClass('error');
                //this.label_required.set({"html":'*!', "class":'required error'});
            }
        }
    },
    _setErrorMessage: function(error, v) {
        if (error && this.error && this.error_group) {
            if (error.length != this.error.innerHTML.length) {
                this.slideError.hide();
                this.error_group.set({ html: '<p>' + error + '</p>', 'styles': { 'display': ''} });
                this.error.innerHTML = error;
                this.slideError.toggle();
            } else {
                if (!this.slideError.open) {
                    this.slideError.toggle();
                }
            }
        } else if (error) {
            this.error_group = new Element('div', {
                'class': 'errorInput error'
            });

            this.error = new Element('p', {
                'class': 'error'
            });

            this.error.innerHTML = error;
            this.error_group.appendChild(this.error);
            this.element_group.appendChild(this.error_group);
            this.slideError = new Fx.Slide(this.error_group).hide();
            this.element.validator = this;
            this.slideError.toggle();
        }
    },
    _parseXML: function(transport) {
        if (transport.responseXML) {
            var response = transport.responseXML;
            validated = response.getElementsByTagName('validated');
            if (validated[0].firstChild.nodeValue == 'true') {
                this.setValid();
            }
            else {
                var aError = response.getElementsByTagName('error');
                for (var i = 0; i < aError.length; i++) {
                    if (aError[i].firstChild) {
                        var error = aError[i].firstChild.nodeValue;
                    }
                    else {
                        var error = '';
                    }
                    this.setInvalid(error);
                }
            }
            this._removeValidationFlag();
        }
        else {
            this._error();
        }
    },
    _removeValidationFlag: function() {
        this.form.validationRequested = false;
    },
    _error: function() {
        this._removeValidationFlag();
        this.setInvalid('Sorry, an error occured');
        //alert('An error occured')
    }
});

var Validator_Form = new Class({
    aErrorTxt: {},
    elements: [],
    params: {
        initial_state: {}
    },
    validationRequested: false,
    initialize: function(form_id, params) {

        this.params = Object.extend(this.params, params || {});
        this.form = $(form_id);
        this.form.validator = this;
        this.valid = true;
        this.create();
    },
    loadErrors: function(oMessages) {
        this.aErrorTxt = oMessages;
    },
    create: function() {
        var aElements = this.form.getElements('input');
        var aSelects = this.form.getElements('select');
        var aTextareas = this.form.getElements('textarea');
        aElements = aElements.combine(aSelects);
        aElements = aElements.combine(aTextareas);
        var aRadioGroups = [];
        for (var i = 0; i < aElements.length; i++) {
            var element = aElements[i];
            if (element.getAttribute('type') == 'radio') {
                $aStr = element.name.split('$');
                var incl = "";
                for (var j = 0; j < $aStr.length; j++) {
                    if ($aStr[j] == '' || $aStr[j] == 'options') {
                        break;
                    }
                    else if (j == 0) {
                        incl = $aStr[j];
                    }
                    else {
                        incl += '_' + $aStr[j];
                    }
                    //($aStr[0] + '_' + $aStr[1] + '_' + $aStr[2] + '_' + $aStr[3]);
                }
                aRadioGroups.include(incl);
            }
            else if (element.getAttribute('type') != 'submit' && element.getAttribute('type') != 'hidden' && element.getAttribute('type') != 'image' && element.getAttribute('type') != 'file') {
                this.addElement(element, element.type);
            }
        }

        for (var i = 0; i < aRadioGroups.length; i++) {
            this.addElement($(aRadioGroups[i]), 'radio');
        }

        //this.form.addEvent('submit', this.submitForm.bind(this));
    },
    addElement: function(element, type) {
        if (element === null) { return; }
        var initial_state = Object.extend({
            validated: this.params.form_validated || false
        }, this.params.initial_state[element.id] ||
		{});
        this.elements.push(new Validator_Element(element, this, type, initial_state));
    },
    submitForm: function(event) {
        this.error_fields = [];
        this.pass_fields = [];
        this.validateForm();

        //If an ajax request has taken place, submit the form without waiting
        if (this.valid == true) {
            //alert('Pass:'+this.pass_fields);
            //return false;
            //this.form.submit();
            //event.stop();
            //
        } else {
            var errorElement = this.error_fields.pop();
            if (errorElement.element) {
                $(errorElement.element).scrollTo();
            }
            //event.stop();

            return false;
        }
    },
    validateForm: function() {
        this.valid = true;

        for (var i = 0; i < this.elements.length; i++) {
            element = this.elements[i];
            if (!element.isValid()) {
                this.error_fields.push(element);
                //this.error_fields.push(element.element.type);
                this.valid = false;
            } else {
                this.pass_fields.push(element);
            }
        }
    }
});

