FormValidate = Class.create({
	
	form: null,
	elements: [],
	submitButtons: null,
	
	_validators: {},
	_filters: {},
	_required: {},
	
	errorList: { 
		'tagName'	: 'ul', 
		'className'	: 'form-error-list'
	},
	selectRule: 'ul.form-error-list',
	createRule: 'p.form-description',
	
	initialize: function(form, options) {
		
		if (options.selectRule) this.selectRule = options.selectRule;
		if (options.createRule) this.createRule = options.createRule;
		
		if (options.validators) this._validators = options.validators;
		if (options.filters) this._filters = options.filters;
		if (options.required) this._required = options.required;
				
		Event.observe(window, 'load', function() { this._setUp(form); }.bindAsEventListener(this, form) );
	},
	
	_setUp: function(form) {
		this.form = $(form);
		
		this.form.getElements().each(this._setEvents.bind(this));
		
		this.submitButtons = this.form.select('[type=submit]');
		
		console.log(this.submitButtons)
		
		var formSubmit = function(event){
			this.onSubmit(event);
		}.bindAsEventListener(this);
		
		this.validateAll(true);
		
		this.form.observe('submit', formSubmit);
		
		
	},
	
	_setEvents: function(element) {
		// Is Required?
		element.isRequired = (this._required[element.name]) ? true : false;
		
		// Has Validator?
		if (this._validators[element.name]) {
			element.validate = this.validate.curry(element).bind(this);
			element.isValid = false;
			element.errorMessages = [];
			
			var onEvent = function(e) { 
				console.log('onEvent: ' + e.type);
				Event.element(e).validate();
			}.bindAsEventListener(this);
			var onKey 	= function(e) {
				console.log('onKey: ' + e.type);
				Event.element(e).validate(true);
			}.bindAsEventListener(this);
			
			var validateOnEvent = false;
			var elementType = element.tagName.toLowerCase();
			if (elementType == 'input') elementType = element.type.toLowerCase();
			switch(elementType) {
				case 'hidden': 
					break;
				case 'text':
				case 'password':
					validateOnEvent = 'blur';
					element.observe('keyup', onKey);
					break;
				case 'select':
					validateOnEvent = 'change';
					break;
				case 'checkbox':
					validateOnEvent = 'click';
					break;
				case 'radio':
					validateOnEvent = 'click';
					break;
				default:
					validateOnEvent = 'blur';
					element.observe('keypress', onKey);
			}
			
			console.log(element.id + ', validateOnEvent: ' + validateOnEvent);
			if (validateOnEvent) element.observe(validateOnEvent, onEvent);
			
			this.elements.push(element);
		}
		
		// Has Filter?
		if (this._filters[element.name]) element.filter = this.filter.curry(element).bind(this);
	},
	
	/**
	 * Enable Submit
	 */
	enableSubmit: function() { 
		if (this.submitButtons && this.isValid()) this.submitButtons.invoke('enable');
	},
	
	/**
	 * Disable Submit
	 */
	disableSubmit: function() {
		if (this.submitButtons && !this.isValid()) this.submitButtons.invoke('disable');
	},
	
	/**
	 * Display Errors
	 */
	displayErrors: function(element) {
				
		var errorList = element.next(this.selectRule);
						
		if (element.isValid) {
			element.removeClassName('error');
			if (errorList) errorList.remove();
			return;
		}
		
		// Add class name
		element.addClassName('error');
		
		console.log(errorList);
		
		if (!errorList) {
			var insertFromElement = element.next(this.createRule);
			if (!insertFromElement) insertFromElement = element;
			errorList = new Element(this.errorList.tagName, { 'class': this.errorList.className });
			insertFromElement.insert({ after: errorList });
		}
		
		// Clear previous errors
		errorList.descendants().invoke('remove');
		
		element.errorMessages.each(function(message){
			errorList.insert('<li>' + message + '</li>');
		}.bind(this));
		
	},
	
	/**
	 * Filter
	 */
	filter: function(element) {
				
		var filteredValue = element.getValue();
		if (this._filters[name]) {
			for (var filterName in this._filters[name]) {
				filteredValue = Filter[filterName](filteredValue);
			}
		}
		return filteredValue;
	},	
	
	/**
	 * Filter All
	 */
	filterAll: function() {
		var filteredParams = {};
		for (var name in params) {
			filteredParams[name] = this.filter(name, params[name]);
		}
		return filteredParams;
	},
	
	/**
	 * Validate
	 */
	validate: function(element, supressErrors) {
		
		element.isValid = true;
		element.errorMessages = [];
		
		var value = element.value;
		var validatorResponse;
		
		// Filter Value??
		if (element.filter) value = element.filter();
				
		if (this._validators[element.name]) {
			for (var validatorName in this._validators[element.name]) {
				// Run Validator
				validatorResponse = Validate.is(validatorName, value, this._validators[element.name][validatorName]);
				// is it valid
				if (validatorResponse !== true) {
					element.isValid = false;
				 	element.errorMessages.push(validatorResponse);
					if (this._validators[element.name][validatorName].breakChainOnFailure) break;
				}
			}
		}
		
		if (!supressErrors) this.displayErrors(element);
		/*
		if (element.isValid) {
			this.enableSubmit();
		} else {
			this.disableSubmit();
		}
		*/
		return element;
	},
	
	/**
	 * Validate All
	 */
	validateAll: function(supressErrors) {
		this.form.getElements().each(function(element){
			if (element.validate) element.validate(supressErrors);
		});
	},
	
	/**
	 * Is Valid
	 */
	isValid: function() {
		return this.elements.pluck('isValid').all();
	},
	
	/**
	 * Submit Form
	 */
	onSubmit: function(event) {
		this.validateAll();
		/*
		Event.stop(event);
		this.form.request({
			method: 'post',
			onCreate: function() {
				this.form.disable();
			}.bind(this),
			onComplete: function (transport) {
				window.reload();
			}
		});
		
		return false;
		*/
	}
});