var kuval = new Class({

	/*** properties */
	oForm:			{},
	aFields:		[],
	bValid:			true,
	aErrorFields:	[],
	options: {
		prefix:	'kuval-',	
		mode: 'instant'		// instant, collect
	},
	/* properties ***/

	/*** methods */
	initialize: function( oForm, options )
	{
		// set default options, for options that were not passed
		this.options = $extend( this.options, options || {} );

		// store form
		this.oForm = $type( oForm ) == 'string' ? $( oForm ) : oForm;

		// get all elements which can possibly be validated
		this.get_fields();

		// validate
		this.oForm.addEvent( 'submit', this.validate.create({ 'bind': this }) );
	},

	get_fields: function( oEvent )
	{
		var aFields = [];
		// get all fields which can possibly be validated
		aFields.merge( this.oForm.getElements( 'input[type!=submit]' ) );
		aFields.merge( this.oForm.getElements( 'select' ) );
		aFields.merge( this.oForm.getElements( 'textarea' ) );

		// stuff them into the object
		this.aFields = aFields;
	},

	validate: function( oEvent )
	{
		// reset error fields array
		this.aErrorFields = [];

		// loop fields
		this.aFields.each( function( oField )
		{
			// check if field has an alt attribute
			if( sTitle = oField.getProperty( 'title' ) )
			{
				// check if a kuval tag is found in the title
				if( aMatch = sTitle.match( /kuval\[(.*?)\]/i ) )
				{
					// explode the validation string, using a regex.
					var aVal = aMatch[1].match( /[a-z]+(?:\'.*?\')?/gi );

					// check if field is required, and if field has a value ( when trimmed )
					if( aVal.contains( 'required' ) && oField.value.trim() == '' )
					{
						// throw error
						this.error( oField, 'is verplicht.' );
					}

					// has this field NOT raised an error yet, and does it have a value?
/*					if( !this.aErrorFields.contains( oField ) && oField.value.trim() != '' )
					{

						// loop all validation types that were found
						aVal.each( function( sVal )
						{

							// validation types without options
							if( sVal.test( /email/i ) )
								this.is_valid.email( oField, this );

							if( sVal.test( /url/i ) )
								this.is_valid.url( oField, this );

							if( sVal.test( /date/i ) )
								this.is_valid.date( oField, this );

							if( sVal.test( /image/i ) )
								this.is_valid.image( oField, this );

							// validation types with options
							if( aMatchVal = sVal.match( /equal\'(.*?)\'/i ) )
								this.is_valid.equal( oField, this, aMatchVal[1] );

						}, this );
					}

*/
				}
			}
			if( !this.aErrorFields.contains( oField ) )
				this.no_error( oField );

		}, this );

//		console.log( 'errors:', this.aErrorFields );

		if( this.aErrorFields.length > 0 )
			new Event( oEvent ).stop();
	},

	error: function( oField, sError )
	{
		// add fieldname to errorfields array
		this.aErrorFields.include( oField );

		if( this.options.mode == 'instant' )
		{
			this.error_instant( oField, sError );
		}
	},

	error_instant: function( oField, sError )
	{
		// create new <em>, attach error class (with prefix)
		var oError = new Element( 'em', { 'class': this.options.prefix + 'error' } );
		oError.setHTML( sError );

		// grab next element, if available
		if( oNext = oField.nextSibling )
		{
			// check if the next element has the error class, if so, replace with new error
			if( oNext.hasClass( this.options.prefix + 'error' ) )
				oNext.replaceWith( oError );
			// no error yet, just replace it
			else
				oError.injectAfter( oField );
		}

		// not available
		else
			oError.injectAfter( oField );
	},

	no_error: function( oField )
	{
		// grab next element, if available
		if( oNext = oField.nextSibling )
		{
			// check if the next element has the error class, if so, replace with new error
			if( $type( oNext ) == 'element' )
			{
				if( oNext.hasClass( this.options.prefix + 'error' ) )
					oNext.remove();
			}
		}
	},
	/* methods ***/

	/*** objects */
	is_valid:
	{

		// check for a valid email address format
		email: function( oField, oParent )
		{
			if( !oField.value.trim().test( /^[a-z0-9]+(?:[-._][a-z0-9]+)*@[a-z0-9]+(?:[-.][a-z0-9]+)*(?:[.-][a-z0-9]{2,4})$/i ) )
			{
				// throw error
				oParent.error( oField, 'is geen geldig email adres.' );
			}
		},

		// check for a valid URL format
		url: function( oField, oParent )
		{
			if( false )
			{
				// throw error
				oParent.error( oField, 'is geen geldige url.' );
			}
		},

		// check for a valid date format
		date: function( oField, oParent )
		{
			if( !oField.value.trim().test( /^(?:0?[1-9]|[12][0-9]|3[0-2]) ?[-\\\/.] ?(?:0?[1-9]|1[0-2]) ?[-\\\/.] ?[1-9][0-9]{3}$/ ) )
			{
				// throw error
				oParent.error( oField, 'is geen geldige datum.' );
			}
		},

		// check for valid image extension
		image: function( oField, oParent )
		{
			if( !oField.value.trim().test( /^.*(?:jpg|jpeg|gif|bmp|png)$/ ) )
			{
				// throw error
				oParent.error( oField, 'is geen geldige afbeelding.' );
			}
		},
		// compare values of two fields
		equal: function( oField, oParent, sMatchField )
		{
			if( $( sMatchField ) )
			{
				if( oField.value != $( sMatchField ).value )
				{
					// throw error
					oParent.error( oField, 'waarde is niet gelijk aan die van \'' + sMatchField + '\'.' );
				}
			}
			else
			{
				// throw error
				oParent.error( oField, 'element with id \'' + sMatchField + '\' was not found.' );
			}
		}
	}
	/* objects ***/

});