/**
 * construction function for Validator class
 *
 * @param {String} id of input element to validate
 * @param {String} id of form the input element is in
 */
function Validator(inputId, formId) {
	if (inputId && formId) {
		this._input = document.getElementById(inputId);
		this.errorAdjacentLeft = this._input;
		
		// find the form... 
		this._form = document.getElementById(formId);
		
		// this means that the last validator to be instantiated (and therefore tested on submit) is the one that will be sat in the lastValidator...
		if (this._form.validatorList == undefined) {
			this._form.validatorList = new Array();
		}
		this._form.validatorList.push(this);
		this._input.validator = this;
		
		// set this every time, it doesn't take long and it's easier than writing some complicated logic to work out whether it already exists...
		this._form.userNotified = false;
		
		this._displaySuccessNotification = false;

		var self = this;
		var cb = function(e) { self.isValid(); }
		eventSubscribe(this._input, 'blur', cb);
		
		// add function to form that runs all of the validators on submit...
		if (this._form.isValid == undefined) {
			this._form.isValid = Validator.formValidator;
			eventSubscribe(this._form, 'submit', this._form.isValid);
		}

		this._ok = document.createElement('img');
		this._ok.src = 'images/ok.gif';
		this._isOk = false;

		this._error = document.createElement('div');
		this._error.className = 'errorMessage';
		this._error.appendChild(document.createElement('span'));

		this._validationTests = new Array();
	}
}

Validator.formValidator = function(e) {
	var target = (e.currentTarget == undefined) ? (e.srcElement) : (e.currentTarget);	
	if (target.overrideValidation != true) {
		var isOk = true;
		for (var x = 0; x < target.validatorList.length; x++) {
			if (!target.validatorList[x].isValid()) {
				isOk = false;
			}
		}
		
		if (!isOk) {
			alert('Please correct the errors on the form and then try again');
			eventAbortAction(e);
			if ((window.onValidationFailure != undefined) && (typeof onValidationFailure == 'function')) {
				onValidationFailure();
			}
		}
	}
}

Validator.prototype = {
	/**
	 * @var {Object} object that will appear to the left of the error message - defaults to the element being validated...
	 */
	errorAdjacentLeft: null,
	
	/**
	 * @var {Function} can be used for assigning a callback function on validation of an element: should take one argument, which is an object with two properties, one pointing to this validator ('validator')
	 * and one ('isValid') for whether the validation passed or not...
	 */
	onValidation: null,

	/**
	 * displays an error message next to the validated element should validation fail
	 * @param {String} error message
	 * @scope private
	 */
	_displayError: function(errorMessage) {
		// remove success message...
		if (isInDocument(this._ok)) {
			this._ok.parentNode.removeChild(this._ok);
		}
	
		// remove any old error message...
		this._error.firstChild.innerHTML = ''; // non-DOM property common to most browsers...
	
		// set error message...
		this._error.firstChild.appendChild( document.createTextNode(errorMessage) );
	
		// add error message to DOM if it's not already there...
		if (!isInDocument(this._error)) {
			this.errorAdjacentLeft.parentNode.insertBefore(this._error, this.errorAdjacentLeft.nextSibling); // put it next to the input it's validating, or wherever specified...
			//this._input.parentNode.insertBefore(this._error, this._input.nextSibling); 
		}
	},

	/**
	 * displays a success icon next to the validated element should validation succeed
	 * @scope private
	 */
	_displaySuccess: function() {
		// remove any error message...
		if (isInDocument(this._error)) {
			this._error.parentNode.removeChild(this._error);
		}
	
		// add success image to DOM if it's not already there...
		if (!isInDocument(this._ok) && this._displaySuccessNotification) {
			this._input.parentNode.insertBefore(this._ok, this._input.nextSibling); // put it next to the input it's validating...
		}
	},
	
	/**
	 * @scope private
	 */
	_notifyUser: function() {
		alert("Please correct the errors on the form and then try again");
	},
	
	/**
	 * add a validation test for an element
	 * @scope public
	 * @param {Object} validation test object
	 */
	addValidationTest: function(validationTest) {
		this._validationTests.push(validationTest);
	},
	
	getInputElement: function() {
		return this._input;
	},
	
	/**
	 * call to hide validation errors
	 */
	hideErrors: function() {
		this._error.className += " hide";
	},

	/**
	 * runs all validation tests against an element, displaying any errors or a success tick
	 * @return {Boolean} whether the element validates ok...
	 * @scope private
	 */
	isValid: function() {
		this._isOk = true;
		if (this._input.disabled != true) {
			for (var xx = 0; xx < this._validationTests.length; xx ++) {
				if ( !this._validationTests[xx].isValid(this._input) ) {
					this._displayError(this._validationTests[xx].errorMessage);
					this._isOk = false;
					break;
				}
			}
			if (this._isOk) {
				this._displaySuccess();
			}
			
			// callback if there is one attached...
			if (this.onValidaton != null) {
				this.onValidation({validator: this, isValid: this._isOk});
			}
		}
		return this._isOk;
	},
	
	/**
	 * sets the success / failure element, when it is output to the page by php rather than js
	 * @param {String} id of element
	 * @param {Bool} true if success image element id is provided, false if failure span id is provided
	 */
	setResult: function(resultId, result) {
		if (result) {
			this._ok = document.getElementById(resultId);
		} else {
			this._error = document.getElementById(resultId);
		}
	},
	
	/**
	 * sets whether to display success notification
	 * @param {Bool} flag
	 */
	setDisplaySucessNotification: function(flag) {
		this._displaySuccessNotification = (flag === true);
	},
	
	showErrors: function() {
		this._error.className = this._error.className.replace(' hide', '');
	}
}