/*
	@elem: Field to validate, required
	@message: Message to display upon failure, required
	@type: Method of validation to use, defaults to 'text'
	@match: What the field should match during certain types of validation, defaults to null
	@required: Whether a value is required or not, defaults to true
	@alert_type: inline or popup, defaults to false and uses ActivaValidate alert handling
	@message_box: Element to display message in for inline alerts, defaults to false
*/
function ActivaRule(elem, message, type, match, required, alert_type, message_box)
{
	this.elem = elem;
	this.dom_elem = null;
	this.message = message;
	this.type = type;
	this.match = match;
	this.required = required;
	this.alert_type = alert_type;
	this.message_box = message_box;
	
	this.validate = function() {
		this.dom_elem = domcheck(this.elem);
		
		if ( this.dom_elem.type != 'select-multiple' ) {
			// Begin by trimming the value
			this.dom_elem.value = this.trim(this.dom_elem.value);
		}
		
		if ( !this.required ) {
			if ( !this.dom_elem.value ) {
				return true;
			}
		}
		
		switch ( this.type ) {
			// Field must have any kind of value
			case "text":
				if ( this.dom_elem.type == 'radio' ) {
					var rgroup = document.getElementsByName(this.dom_elem.name);
					for ( var i=0; i<rgroup.length; i++ ) {
						if ( rgroup[i].checked ) {
							return true;
						}
					}
					return false;
				} else if ( !this.dom_elem.value ) {
					return false;
				}
				break;
			// Field must be checked
			case "checkbox":
				if ( !this.dom_elem.checked ) {
					return false;
				}
				break;
			// Field must match email regex
			case "email":
				var reg = /^[A-Za-z0-9._%+-]+@(?:[A-Za-z0-9-]+\.)+[A-Za-z]{2,4}$/;

				if ( !reg.test(this.dom_elem.value) ) {
					return false;
				}
				break;
			// Field must match phone regex
			case "phone":
				this.dom_elem.value = this.trim(this.dom_elem.value);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /\s/);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /-/);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /\(/);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /\)/);

				var reg = /^([\d]{10})$/;

				if ( !reg.test(this.dom_elem.value) ) {
					return false;
				}
				break;
			// Field must match intl phone regex
			case "intl_phone":
				this.dom_elem.value = this.trim(this.dom_elem.value);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /\s/);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /-/);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /\(/);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /\)/);
				
				var reg = /^((\+)?[\d]{10,16})$/;

				if ( !reg.test(this.dom_elem.value) ) {
					return false;
				}
				break;
			// Field must match US zip code regex
			case 'us_zip':
				this.dom_elem.value = this.trim(this.dom_elem.value);
				this.dom_elem.value = this.trimChar(this.dom_elem.value, /\s/);

				var reg = /^((\d){5})((-(\d){4}){1}$|$)/;

				if ( !reg.test(this.dom_elem.value) ) {
					return false;
				}
				break;
			// Field must match Canadian zip code regex
			case 'can_zip':
				this.dom_elem.value = this.trim(this.dom_elem.value);

				var reg = /^([A-Za-z0-9]{3})((\s|-){1})([A-Za-z0-9]{3})$/;

				if ( !reg.test(this.dom_elem.value) ) {
					return false;
				}
				break;
			// Field must be checked
			case "checkbox":
				if ( !this.dom_elem.checked ) {
					return false;
				}
				break;
			// Field must have at least one item in selectbox
			case 'transferbox':
				if ( this.dom_elem.options.length == 0 ) {
					return false;
				}
				break;
			// Field must contain an equal or lesser amount of characters
			case "shorter":
				if ( parseInt(this.dom_elem.value.length) > parseInt(this.match) ) {
					return false;
				}
				break;
			// Field must contain an equal or greater amount of characters
			case "longer":
				if ( parseInt(this.dom_elem.value.length) < parseInt(this.match) ) {
					return false;
				}
				break;
			// Field must contain amount of characters within valid range
			case "between":
				var ranges = this.match.split('-');
				var min = parseFloat(ranges[0]);
				var max = parseFloat(ranges[1]);
				
				if ( isNaN(parseFloat(this.dom_elem.value.length)) || parseFloat(this.dom_elem.value.length) > max || parseFloat(this.dom_elem.value.length) < min ) {
					return false;
				}
				break;
			// Field must match amount of characters
			case "length":
				if ( parseInt(this.dom_elem.value.length) != parseInt(this.match) ) {
					return false;
				}
				break;
			// Field must be less than or equal to requirement
			case "less":
				var max = parseFloat(this.match);
				
				if ( isNaN(parseFloat(this.dom_elem.value)) || parseFloat(this.dom_elem.value) > max ) {
					return false;
				}
				break;
			// Field must be greater than or equal to requirement
			case "greater":
				var min = parseFloat(this.match);
				
				if ( isNaN(parseFloat(this.dom_elem.value)) || parseFloat(this.dom_elem.value) < min ) {
					return false;
				}
				break;
			// Field must contain value within valid range
			case "range":
				var ranges = this.match.split('-');
				var min = parseFloat(ranges[0]);
				var max = parseFloat(ranges[1]);
				
				if ( isNaN(parseFloat(this.dom_elem.value)) || parseFloat(this.dom_elem.value) > max || parseFloat(this.dom_elem.value) < min ) {
					return false;
				}
				break;
			// Field must match passed regex
			case "match":
				if(this.dom_elem.type == 'radio'){
					var rgroup = document.getElementsByName(this.dom_elem.name);
					for(var i = 0; i < rgroup.length; i++){
						if(rgroup[i].checked && this.match == rgroup[i].value){ return true; }
					}
					return false;
				} else {
					if ( !this.match.test(this.dom_elem.value) ) {
						return false;
					}
				}
				break;
			// Field must match exactly
			case 'equals':
				if ( this.dom_elem.value != this.match ) {
					return false;
				}
				break;
				
			case 'notequal':
				if ( this.dom_elem.value == this.match ) {
					return false;
				}
				break;
		}
		
		return true;
	}
	
	// Must pass default ActivaValidate message box, in case rule does not have its own message box
	this.alert = function(message_box) {
		switch ( this.alert_type ) {
			case "inline":
				if ( this.message_box ) {
					this.message_box.innerHTML = this.message;
					this.message_box.style.display = '';
				} else {
					message_box.innerHTML = this.message;
					message_box.style.display = '';
				}
				break;
			case "popup":
			case "alert":
				alert(this.message);
				break;
		}
	}
	
	this.ltrim = function(string)
	{
		return string.replace(/^\s*/, '');
	}
	
	this.rtrim = function(string)
	{
		return string.replace(/\s*$/, '');
	}
	
	this.trim = function(string)
	{
		if ( typeof(string) == 'undefined' ) {
			return false;
		}
		return this.rtrim(this.ltrim(string));
	}
	
	this.trimChar = function(str, reg) {
		var reg = reg;
		while (reg.test(str)) {
			str = str.replace(reg, '');
		}
		
		return str;
	}
}

/*
	@form: ActivaForm that this applies to
	@elem: Field determining conditional outcome
	@matches: Array of possible values of elem
*/
function ActivaConditional(form, elem, matches, target)
{
	this.form = form;
	this.elem = elem;
	this.dom_elem = null;
	this.matches = matches;
	
	if ( typeof(target) == 'undefined' ) {
		this.target = 'id';
	} else {
		this.target = target;
	}
	
	this.rules = new Object();
	/*
		@match: Value in matches array that you want to assign this rule to
	*/
	this.addRule = function(condition, elem, message, type, match, required, alert_type, message_box) {
		if ( typeof(type) == 'undefined' ) {
			type = 'text';
		}
			
		if ( typeof(match) == 'undefined' ) {
			match = '';
		}
		
		if ( typeof(required) == 'undefined' ) {
			required = true;
		}
		
		if ( typeof(alert_type) == 'undefined' ) {
			alert_type = false;
		}
		
		if ( typeof(message_box) == 'undefined' ) {
			message_box = false;
		}
		
		var rule = new ActivaRule(elem, message, type, match, required, alert_type, message_box);
		
		if ( !this.rules[condition] ) {
			this.rules[condition] = new Array();
		}
		this.rules[condition].push(rule);
		return rule;
	}
	
	this.validate = function(rules) {
		switch ( this.target ) {
			case 'id':
				this.dom_elem = domcheck(this.elem);
				var value = this.dom_elem.value;
				break;
			case 'radio':
				for ( var i=0; i<document.forms[this.form.form.id][this.elem].length; i++)  {
					if ( document.forms[this.form.form.id][this.elem][i].checked) {
						var value = document.forms[this.form.form.id][this.elem][i].value;
					}
				}
				break
		}
		
		if ( this.rules[value] ) {
			for ( var x=0; x<this.rules[value].length; x++ ) {
				if ( !this.rules[value][x].validate() ) {
					this.message = this.rules[value][x].message;
					this.dom_elem = this.rules[value][x].dom_elem;
					
					return false; 
				}
			}
		} else if ( this.rules['cond_default'] ) {
			for ( var x=0; x<this.rules['cond_default'].length; x++ ) {
				if ( !this.rules['cond_default'][x].validate() ) {
					this.message = this.rules['cond_default'][x].message;
					this.dom_elem = this.rules['cond_default'][x].dom_elem;
					
					return false; 
				}
			}
		}
		
		return true;
	}
	
	this.reset = function() {
		this.form.rules = new Array();
		this.form.rules = this.rules;
	}
}

/*
	@form: Form that will be validated
*/
function ActivaForm(form)
{
	this.form = form;
	this.rules = new Array();
	this.extra = new Array();
	this.active = true;
	
	// Manually add onload event so it can grab reference to DOM object, and not a string
	registerEvent(window, 'load', createDelegate(this, 'load'));

	this.load = function() {
		this.form = domcheck(this.form);
	}
	
	this.addRule = function(elem, message, type, match, required, alert_type, message_box) {
		if ( typeof(type) == 'undefined' ) {
			type = 'text';
		}
			
		if ( typeof(match) == 'undefined' ) {
			match = '';
		}
		
		if ( typeof(required) == 'undefined' ) {
			required = true;
		}
		
		if ( typeof(alert_type) == 'undefined' ) {
			alert_type = false;
		}
		
		if ( typeof(message_box) == 'undefined' ) {
			message_box = false;
		}
		
		var rule = new ActivaRule(elem, message, type, match, required, alert_type, message_box);
		this.rules.push(rule);
		return rule;
	}
	
	this.addConditional = function(elem, matches, target) {
		var conditional = new ActivaConditional(this, elem, matches, target);
		this.rules.push(conditional);
		
		return this.rules[this.rules.length-1];
	}
	
	this.addExtra = function(func) {
		this.extra.push(func);
	}
}

/*
	@form: ActivaForm, required
	@alert_type: Style of alerts, inline or popup, defaults to popup
	@message_box: Element to display message in for inline alerts, defaults to null
*/
function ActivaValidate(form, alert_type, message_box)
{
	this.form = form;
	
	if ( typeof(alert_type) == 'undefined' ) {
		alert_type = 'popup';
	}
		
	if ( typeof(message_box) == 'undefined' ) {
		message_box = false;
	} else {
		this.message_box = domcheck(message_box);
	}
	
	this.alert_type = alert_type;
	
	// Manually add onsubmit event so that form HTML does not require it
	registerEvent(this.form.form, 'submit', createDelegate(this, 'validate'));
	
	this.validate = function(e) {
		if ( !this.form.active ) {
			return true;
		}
		
		// Always reset message box to nothing
		if ( this.message_box ) {
			this.message_box.style.display = 'none';
			this.message_box.innerHTML = '';
		}
		
		// Validate rules
		for ( var i=0; i<this.form.rules.length; i++ ) {
			var rule = this.form.rules[i];
			
			if ( !rule.validate() ) {
				if ( rule.alert_type ) {
					// ActivaRule has override alert handling
					rule.alert(this.message_box);
				} else {
					// ActivaValidate is handling the alert
					switch ( this.alert_type ) {
						case "inline":
							this.message_box.innerHTML = rule.message;
							this.message_box.style.display = '';
							break;
						case "popup":
						case "alert":
							alert(rule.message);
							break;
					}
				}
				
				if ( typeof(rule.dom_elem) != 'undefined' && rule.dom_elem ) { 
					rule.dom_elem.focus();
				}
				
				// Prevents form from posting (required when you manually register onsubmit event)
				if ( e.preventDefault ) {
					e.preventDefault();
				}
				
				return false;
			}
		}
		
		// Validate extra functions
		for ( var i=0; i<this.form.extra.length; i++ ) {
			var result = this.form.extra[i].call();
			
			if ( !result ) {
				// Prevents form from posting (required when you manually register onsubmit event)
				if ( e.preventDefault ) {
					e.preventDefault();
				}
				
				return false;
			}
		}
		
		// Form has successfully validated
		return true;
	}
}