var Validation = Class.create();

Validation.prototype = {
  initialize : function(form, options){
    this.form = $(form);
    this.options = Object.extend({
      advice_style      : ((options==null||options.advice_style==null)      ? 'validation-advice' : options.advice_style),
      advice_timeout    : ((options==null||options.advice_timeout==null)    ? 0 : options.advice_timeout),
      advice_position   : ((options==null||options.advice_position==null)   ? 'right' : options.advice_position),      
      advice_override   : ((options==null||options.advice_override==null)   ? null : options.advice_override),      
      onValidationPassed: ((options==null||options.onValidationPassed==null)? null : options.onValidationPassed),
      visualEffect      : ((options==null||options.visualEffect==null)      ? true : options.visualEffect),
      onFailed_style    : ((options==null||options.onFailed_style==null)    ? 'validation-failed' : options.onFailed_style),
      onPassed_style    : ((options==null||options.onPassed_style==null)    ? '' : options.onPassed_style),
      addValidators     : ((options==null||options.addValidators==null)     ? null : $A(options.addValidators)),
      click_observe     : ((options==null||options.click_observe==null)     ? null : options.click_observe)
    });
    Validation.options = this.options;
    Event.observe(this.form,'submit',this.onSubmit.bind(this),false);    F
    
    if(options!=null&&options.click_observe!=null) Event.observe($(this.options.click_observe), 'click',this.onSubmit.bind(this),false);    
  },

  onSubmit :  function(ev){
    Event.stop(ev);
    if(this.validate()) {
      if(this.options.onValidationPassed != null) this.form.writeAttribute('action', this.options.onValidationPassed);
      this.form.submit();
    }
  },
  
  validate : function() {
    var result = false;
    this.reset();
    result = Form.getElements(this.form).collect(this.testInput.bind(this));
    return $A(result).all(function(isValid){return isValid;});
  },
  
  reset : function() {
    Form.getElements(this.form).each(Validation.reset);
  },
  
  testInput : function (elm) {
    var cn = elm.classNames();
    var advice_style = this.options.advice_style;                        
    var advice_timeout = this.options.advice_timeout;
    var visualEffect = this.options.visualEffect;
    var onFailed_style = this.options.onFailed_style;
    var onPassed_style = this.options.onPassed_style;
    var advice_position = this.options.advice_position
    var result = cn.all(
      function(className) {
        if(elm.getWidth()==0&&elm.getHeight()==0) return true;
        var result =  Validation.test(className, elm);
        if(!result) {
          var adviceDiv = Validation.getAdviceDiv(className, elm);
          if(adviceDiv == null) {
            var errMsg = Validation.getAdvice(className); 
            var adviceId = 'advice-'+className+'-'+elm.id;
            var advice = '<div id="'+adviceId+'" style="display:none"><div style="float: left; margin-top:2px;'+(advice_position.toUpperCase()!='RIGHT' ? 'display:none' : '')+'"><img src="images/validation-arrow.png" style="margin: 0; position: absolute;"/></div><div class="'+advice_style+'"" style="float:left;'+(advice_position.toUpperCase()=='RIGHT' ? 'margin-left:8px;' : '')+'">' + errMsg + '</div></div>';
            if(elm.type.toLowerCase()=='radio') {
              var p = elm.parentNode;
              if(p) {
                new Insertion.Bottom(p, advice);
              } 
              else {
                new Insertion.After(elm, advice);
              }
            }
            else {
              new Insertion.After(elm, advice); 
            }
            adviceDiv = Validation.getAdviceDiv(className, elm);
          }
          var co = elm.viewportOffset();
          if(advice_position.toUpperCase()=='BOTTOM') {
            //adviceDiv.setStyle({position: 'absolute', left: co[0]+'px', top: (co[1]+elm.getHeight()+5)+'px'});
            adviceDiv.setStyle({marginTop: '3px', marginBottom: '10px'});
          }
          else {
            adviceDiv.setStyle({'position': 'absolute', 'display': 'block', visibility: 'hidden'});
            adviceDiv.clonePosition(elm);            
            var left_co = parseInt(adviceDiv.getStyle('left').replace('px',''))+elm.getWidth()+5;
            adviceDiv.setStyle({'left': left_co+'px', visibility: 'visible', display: 'none'});            
          }
          if(visualEffect && typeof Effect != 'undefined') {
            new Effect.Appear(adviceDiv, {duration : 0.5 });
            if(advice_timeout > 0) new Effect.Fade(adviceDiv, {delay: advice_timeout, duration: 1 });
          }
          else {
            adviceDiv.show();            
            if(advice_timeout > 0) setTimeout(function(){adviceDiv.hide()}, advice_timeout * 1000);
          }
          elm.addClassName(onFailed_style);
          elm.removeClassName(onPassed_style);          
          return false;
        }
        else {
          var adviceDiv = Validation.getAdviceDiv(className, elm);
          if(adviceDiv != null && adviceDiv.visible()) { adviceDiv.hide()};
          elm.addClassName(onPassed_style);
          elm.removeClassName(onFailed_style);          
          return true;
        }
      }
    );
    return result;
  }
}

Object.extend(Validation, {  
  options : null,
  test : function(cn, elm) {
    switch(cn) {
      case "required":
        return (this.isEmpty($F(elm)) ? false : true);
      break;
      case "validate-mobile":
        return this.validateMobile(this.trim($F(elm)));
      break;      
      case "validate-phone":
        return this.validatePhone(this.trim($F(elm)));
      break;            
      case "validate-email":
        return this.validateEmail(this.trim($F(elm)));
      break;
      case "validate-numeric":
        return this.isNumeric(this.trim($F(elm)));
      break;  
      case "validate-number":
        return (isNaN($F(elm)) ? false : true);
      break; 
      case "validate-abn":
        return this.validateABN(this.trim($F(elm)));
      break;              
      case "validate-currency":
        return this.validateCurrency(this.trim($F(elm)));
      break;                    
      case "validate-alpha":
        return this.isAlpha(this.trim($F(elm)));
      break;                        
      case "validate-postcode":
        return this.validatePostcode(this.trim($F(elm)));
      break;                        
      case "validate-areaCode":
        return this.validateAreaCode(this.trim($F(elm)));
      break;  
      case "validate-date-format":
        return this.validateDateFormat(this.trim($F(elm)));
      break;      
      case "validate-date":
        return this.validateDate(this.trim($F(elm)));
      break;            
      case "validate-dob":
        return this.validateDob(this.trim($F(elm)));
      break;                  
      case "validate-selection":
        return this.validateSelection(elm);
      break;
      case "validate-multi-selection":
        return this.validateMultiSelection(elm);
      break;      
      case "validate-radio":
        return this.validateRadio(elm);
      break;                               
      case "validate-id":
      case "validate-username":
        return this.validateID(this.trim($F(elm)));
      break;                                    
      default:
        var classFound = false;
        if(cn==Validation.options.onFailed_style) {
          return true;
        }
        if(Validation.options.addValidators != null) {
          var result = this.options.addValidators.any(function(v) {
            if(v[0]==cn) {
              try{
                classFound = true;
                return v[2](Validation.trim($F(elm)));
              }
              catch(e) {
                return false;
              }
            }else
              return false;
          });
          if(classFound && !result)
            return false;
          else
            return true;
        }
        return true;
      break;
    }
  },
  

  
  reset : function(elm) {
    elm = $(elm);
    var cn = elm.classNames();
    cn.each(
      function(className) {
        var advice = Validation.getAdviceDiv(className, elm);
        if(advice != null) advice.hide();
      }
    );
    if(elm.hasClassName(Validation.options.onFailed_style)) {
      elm.removeClassName(Validation.options.onFailed_style);    
      elm.addClassName(Validation.options.onPassed_style);    
    }
  },  
  
  getAdviceDiv : function(className, elm) {
    return $('advice-'+className+'-'+elm.id);  
  },
  
  getAdvice : function(name) {
    var adviceArray = [];
    adviceArray['required'] = 'This is a required field.';
    adviceArray['validate-mobile'] = 'Please enter a valid mobile number.';
    adviceArray['validate-phone'] = 'Please enter a valid phone number.';
    adviceArray['validate-email'] = 'Please enter a valid email address. For example johnsmith@mydomain.com';
    adviceArray['validate-numeric'] = 'Please use numbers only in this field. (No special characters including dots and commas)';
    adviceArray['validate-number'] = 'Please use numbers only in this field.';
    adviceArray['validate-alpha'] = 'Please use letters only (a-z) in this field.';
    adviceArray['validate-postcode'] = 'Please enter a valid postcode.';
    adviceArray['validate-areaCode'] = 'Please enter a valid area code.';
    adviceArray['validate-abn'] = 'Please enter a valid ABN.';
    adviceArray['validate-id'] = 'Please enter a valid ID. (No special characters)';
    adviceArray['validate-username'] = 'Please enter a valid username. (No special characters)';
    adviceArray['validate-date-format'] = 'Please enter a valid date. For example 14/05/2009';
    adviceArray['validate-selection'] = 'Please select one of the options.';
    adviceArray['validate-multi-selection'] = 'Please select at least one of the options.';
    adviceArray['validate-radio'] = 'Please select one of the options.';
    adviceArray['validate-currency'] = 'Please enter a valid amount. For example $100.00 or 100';
    adviceArray['validate-date'] = 'Please enter a valid date. For example 14/05/2009';
    adviceArray['validate-dob'] = 'Please enter a valid day of birth.';
    if(Validation.options.addValidators != null) {
      this.options.addValidators.each(function(v) {
        adviceArray[v[0]] = v[1];
      });          
    }
    if(Validation.options.advice_override != null) {
      this.options.advice_override.each(function(v) {
        adviceArray[v[0]] = v[1];
      });          
    }        
    for(field in adviceArray) {
      if(name == field) { return adviceArray[field]};
    }
    return 'Validation Failed.';
  },
  
  isEmpty : function(value) {
    value = this.trim(value);
    return (value.length > 0) ? false : true;
  },
  
  trim : function (stringToTrim) {
    return (stringToTrim) ? stringToTrim.replace(/^\s+|\s+$/g,"") : false;
  },  

  validateEmail: function (email) {
    var exp1 = /^[^.@;:"\\\(\)\[\]\ ]([^@;:"\\\(\)\[\]\ ]){1,100}@[^@;:"\\\(\)\[\]\ \.]([^@;:"\\\(\)\[\]\ ]){1,100}\.((\w){2,4}|([^@;:"\\\(\)\[\]\ ]){2,100}\.((\w){2,4}|([^@;:"\\\(\)\[\]\ ]){2,100}\.(\w){2,4}))$/;
    var exp2 = /\.\./;
    
    if(email.match(exp1) && !email.match(exp2)) {
      return true;
    }
    return false;
  },

  validateMobile: function(input){
    var newPhone = (newPhone) ? input.replace(/[\(\)\ \-]+/g,"") : "";
    var re = new RegExp('^04[0-9]{8}$');
    if(newPhone.length==0){
      return true;
    }
    else if(newPhone.match(re)){
      return true;
    }
    return false;
  },
  
  validatePhone : function(input){
    if(input.length==0) return true;  
    var newPhone = input.replace(/[\(\)\ \-]+/g,"");
    var regexp = /^(([0-9]){8}|([0-9]){10})$/;
    if(newPhone.match(regexp)){
      return true;
    }
    return false;
  },
  
  validatePostcode :  function(postcode) {
    var regexp = /^[0-9]{4}$/;
    if(postcode.match(regexp)){
      return true;
    }
    return false;
  },

  validateAreaCode : function(areaCode) {
    var regexp = /^(0)(2|3|7|8)$/;
    if(areaCode.match(regexp)){
      return true;
    }
    return false;
  },
  
  validateABN : function(abn) {
    if(abn.length==0) return true;
    if(abn.length == 11) {
      weights = Array(10,1,3,5,7,9,11,13,15,17,19);
      calc = 0;
      for(i=0;i<11;i++) {
        calc = calc + (((i==0) ? -1 : 0) + (abn.charAt(i) * 1)) * weights[i];
      }
      calc = calc % 89;

      return (calc == 0) ? true : false;
    }
    else {
      return false;
    }
  },  
  
  validateID : function(input) {
    var exp = /^[0-9a-zA-Z\-_\+]+$/;
    if(input.match(exp)) {
      return true;
    }
    return false;
  },
  
  validateDateFormat : function(input) {
    var exp = /^((0|)[1-9]|[12][0-9]|3[01])[\/\-\ \.]((0|)[1-9]|1[0-2])[\/\-\ \.](19|20)\d\d$/
    if(input.length==0) return true;    
    if(input.match(exp)) {
      return true;
    }
    return false;    
  },

  /** returns true if the string only contains characters A-Z or a-z **/
  isAlpha : function(str){
    var re = /^[a-zA-Z]+$/;
    if (re.match(str)) return true;
    return false;
  },

  /** returns true if the string only contains characters 0-9 **/
  isNumeric: function(str){
    var re = /[\D]/g
    if (re.test(str)) return false;
    return true;
  },
  
  /** returns true if the string only contains characters A-Z, a-z or 0-9 **/
  isAlphaNumeric: function(str){
    var re = /[^a-zA-Z0-9]/g
    if (re.test(str)) return false;
    return true;
  },
  
  validateSelection : function(elm) {
    return ((elm.selectedIndex > 0) ? true : false);
  },
  
  validateMultiSelection : function(elm) {
    return $A(elm).any(function(child) { return child.selected; });
  },
  
  
  validateRadio : function(elm) {
    return $$("input."+elm.name).any(function(child) { return child.checked;});
  },
  
  validateCurrency : function(value) {
    var re = /^(\$|\-|\-\$)?([1-9]{1}[0-9]{0,2}(\,[0-9]{3})*(\.[0-9]{0,2})?|[1-9]{1}[0-9]{0,}(\.[0-9]{0,2})?|0(\.[0-9]{0,2})?|(\.[0-9]{1,2})?)$/
    if (re.match(value)) return true;
    return false;  
  },
  
  /*
   * Must be in this format: dd/mm/yyyy
   */
  validateDate : function(input){
    var regexp = /^\d{2}\/\d{2}\/\d{4}$/
    if (!regexp.test(input)) {
      return false;
    }
    else{
      var dayfield=input.split("/")[0];    
      var monthfield=input.split("/")[1];
      var yearfield=input.split("/")[2];
      var dayobj = new Date(yearfield, monthfield-1, dayfield);
      if ((dayobj.getMonth()+1!=monthfield)||(dayobj.getDate()!=dayfield)||(dayobj.getFullYear()!=yearfield)) {
        return false;
      }
    }
    return true;
  },
  
  validateDob : function(input) {
    if(!this.validateDate(input)) {
      return false;
    }
    var dayfield=input.split("/")[0];    
    var monthfield=input.split("/")[1];
    var yearfield=input.split("/")[2];    
    var dayobj = new Date(yearfield, monthfield-1, dayfield);    
    var today = new Date();
    if(dayobj > today) {
      return false;
    }
    return true;
  }
});




