var AsyncForm = new Class({
   Implements: Options,
   options: {
      'validate':function(){},
      'validations': [],
      'onSuccess':null,
      'onError':function(){},
      'defaultError': 'There was a problem submitting this form.',
      'dispose': true,
			'async': true
   },
   
   initialize: function(formSelector, options) {
       this.setOptions(options);

       this.form = document.getElement(formSelector);
       this.onsuccess = this.options['onSuccess'];
       this.validations = this.options['validations'];
       this.onerror = this.options['onError'];
       this.dispose = this.options['dispose'];
			 this.async = this.options['async'];
       this.defaultError = this.options['defaultError'];
       this.error = [];
			 this.syncHalt = false;
       
       var that = this;
       this.form.addEvent('submit', function(event){
           that._onSubmit(function() {
							 if (that.async || that.syncHalt) {
									 event.stop();
									 that.syncHalt = false;
							 }
					 });
       });
   },
   
   _onSubmit: function(callback) {
       var fieldsHash = new Hash();
       var fields = this.form.getElements('input, textarea, select, button');
       var blankError = new Error();
       blankError.name = 'halt';
       
       fields.each(function(field) {
          fieldsHash.set(field.get('id'), field.get('value')); 
       });
       
       try {
           this.options['validate'].call(this, fieldsHash.getClean(), blankError);
       } catch(e) {
           if(e.name == "halt") {
               this.error.push(e);
           } else {
               throw(e);
           }
       }
              
       if(this.validations != []) {
            var that = this;            
            this.validations.each(function(arguments) {
                var validation = $H(arguments).getKeys()[0];
                try {
                    that.validate[validation].call(this, arguments[validation], fieldsHash.getClean());
                } catch(e) {
                    if(e.name == "halt") {
                        that.error.push(e);
                    } else {
                        throw(e);
                    }
                }
            });
       }
        
       if(this.error.length == 0) {
					 if(this.async)
							 this.form.send();
           this._afterSubmitSuccess(this.onsuccess, fieldsHash.getClean());
           if(this.dispose)
               this.form.dispose();
       } else {
           this._afterSubmitError(this.onerror, fieldsHash.getClean());
       }
			 callback();
   },
   
   validate: {
       'null': function() {},
       
       'present': function(check, fields) {
           if(fields[check[0]] == "") {
               var e = new Error();
               e.name = 'halt';
               e.message = check[1];
               throw(e);
           }
       },
       
       'length': function(check, fields) {
           if(fields[check[1]].length < check[0]) {
               var e = new Error();
               e.name = 'halt';
               e.message = check[2];
               throw(e);
           }
       },
       
       'equal': function(check, fields) {
           if(fields[check[0]] != fields[check[1]]) {
               var e = new Error();
               e.name = 'halt';
               e.message = check[2];
               throw(e);
           }
       },
        
       'notequal': function(check, fields) {
           if(fields[check[0]] == check[1]) {
               var e = new Error();
               e.name = 'halt';
               e.message = check[2];
               throw(e);
           }
       },
       
       'size': function(check, fields) {
           if(parseInt(fields[check[1]]) < check[0]) {
               var e = new Error();
               e.name = 'halt';
               e.message = check[2];
               throw(e);
           }
       },

       'checked': function(check, fields) {
					 var checked = $(check[0]).get('checked');
           if(checked == false) {
               var e = new Error();
               e.name = 'halt';
               e.message = check[1];
               throw(e);
           }
       },
      
       'format': function(check, fields) {
           if(!fields[check[0]].match(check[1])) {
               var e = new Error();
               e.name = 'halt';
               e.message = check[2];
               throw(e);
           }
       }
   },
   
   _afterSubmitError: function(onerror, fields) {
      if(this.message)
        this.message.dispose();
      this.message = new Element('div',{'class':'async_error','text':this.error.message});
      var processedErrors = this._processErrors();
      this.message.grab(processedErrors);
      this.message.inject(this.form, 'before');
      onerror.call(this, fields)
      this.error.empty();
			this.syncHalt = true;
   },
   
   _afterSubmitSuccess: function(onsuccess, fields) {
       if(this.message)
           this.message.dispose();
       switch(typeof(onsuccess)) {
           case 'function' :
               this.onsuccess.call(this, fields);
           break;
           
           case 'string' :
               var message = new Element('p',{'text':this.onsuccess});
               message.inject(this.form, 'before');
           break;
           
           case 'object' :
               this.onsuccess.inject(this.form, 'before');
           break;
       }
   },
   
   _processErrors: function() {
       var ul = new Element('ul');
       this.error.each(function(e){
           if(e.message == "")
               e.message = this.defaultError;
           var li = new Element('li', {'text':e.message});
           ul.grab(li);
       });
       return ul;
   }
});
