Mortice.Form = Class.create(Mortice.Component, {

	initialize: function($super, form) {
		$super($(form));
		
		
		this.element.onsubmit = function(){
			this.submit();
			return false;
		}.bind(this);
		
		this.action = this.element.getInputs("hidden", "action")[0].value;
		this.button = this.element.getInputs("submit")[0];
		this.error = $("form_error_message");

		
		var elements = this.element.getElements();
		for(var i = 0; i < elements.length; i++) {
			switch(elements[i].name) {
				case "action":
					break;
				
				case "state":
					this.add(new Mortice.Form.Field.State(elements[i]));
					break;
				
				case "street":
					this.add(new Mortice.Form.Field.Street(elements[i]));
					break;
				
				default:
					if(elements[i].type == "checkbox") {
						this.add(new Mortice.Form.Field.Checkbox(elements[i]));
						break;
					}
					this.add(new Mortice.Form.Field(elements[i]));
					
			}			
		}
	},

	submit: function() {
		try {
			
			if(!this.validate()) {
				return;
			}
			
			this.element.removeClassName("form_error");
			
			// Show the loading
			this.button.disable();
			this.element.addClassName("loading");
			
			var payload = this.serialize();
			
			new Ajax.Request("dynamic/index.php?action=" + this.action + "&json=" + payload  , {
	  			method: 'post',
	  			onComplete: function(transport) {
	   				this.onResponse(transport.responseText.evalJSON());
	  			}.bind(this)
			});
		}
		catch(exception) {
			this.button.enable();
			this.element.removeClassName("loading");
			this.element.addClassName("form_error");
			this.error.innerHTML = exception.message;
		}
	},
	
	validate: function() {
		var error = null;
		var valid = true;
		var fields = this.getChildren();
		for(var i = 0; i < fields.length; i++) {
			valid = fields[i].validate() && valid;
			if(!error && !valid) {
				// First error which will scroll to
				error = fields[i];
			}
		}
		if(error) {
			this.scrollTo(error);
		}
		
		return valid;
	},
	
	serialize: function() {
		var string = "";
		var fields = this.getChildren();
		for(var i = 0; i < fields.length; i++) {
			if(i != 0) {
				string += ",";
			}
			string += fields[i].serialize();
		}
		return "{" + string + "}";
	},
	
	scrollTo: function(field) {
		field.element.scrollTo();
	},
	
	onResponse: function(response) {
		this.button.enable();
		this.element.removeClassName("loading");
		
		if (response.result && response.result == "success") {
			this.element.onsubmit = function(){
				return true;
			}
			var success = new Element("input", {
				"type": "hidden",
				"name": "result",
				"value": "success"
			});
			this.element.appendChild(success);
			
			this.element.removeClassName("form_error");
			this.element.submit();
			return;
		}
		
		// We have some sort of error
		this.element.addClassName("form_error");
		this.error.innerHTML = response.message;
		
	}

	
});

Mortice.Form.Field = Class.create(Mortice.Component, {
	initialize: function($super, element) {
		$super(element);
	},
	
	isRequired: function() {
		return this.element.hasClassName("required");
	},
	
	getName: function() {
		return this.element.name;
	},
	
	getLabel: function() {
		
	},
	
	getValue: function() {
		var type = this.element.type;
		var value = null;
		
		if(type.indexOf("select") == 0) {
			var option = this.element.options[this.element.selectedIndex];
			value = option.value;
		}
		else {
			value = this.element.value;
			
		}
		
		if(value.blank()) {
			value = null;
		}
		return value;
	},
	
	/**
	 * Returns true if the field passes validation requirements.
	 * 
	 * @return {boolean}
	 */
	validate: function() {
		var value = this.getValue();
		
		if(value == null && this.isRequired()) {
			// Failed
			var message = "This field requires a value";
			if (this.error) {
				this.error.setMessage(message);
			}
			else {
				this.error = new Mortice.Form.Field.Error(this, message);
			}
			this.error.show();
			return false;
		}
		
		var classes = (this.className) ? this.className.split(" ") : null;
		for(var i = 0; value && classes && i < classes.length; i++) {
			var validator = Mortice.Form.Field[classes[i].toUpperCase()];
			
			if(!validator) {
				continue;
			}
			
			// Regex validation
			if(!validator.pattern.test(value)) {
				// Failed	
				message = validate.message;
				if (this.error) {
					this.error.setMessage(message);
				}
				else {
					this.error = new Mortice.Form.Field.Error(this, message);
				}
				this.error.show();
				
				return false;
			}
		}
		
		return true;
	},
	
	serialize: function() {
		var value = this.getValue();
		if(value == null) {
			return "\"" + this.getName() + "\":null"; 
		}
		return "\"" + this.getName() + "\":\"" + value + "\""; 
	}
});

Mortice.Form.Field.NUMBER = {
	"pattern": /^[-]?\d+(\.\d+)?$/,
	"message": "This field must have a numerical value"
};
	
Mortice.Form.Field.DIGITS = {
	"pattern": /^[-]?\d+(\.\d+)?$/,
	"message": "This field can only contain numbers"
};
	
Mortice.Form.Field.EMAIL = {
	"pattern": /^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/,
	"message": "This field must contain a valid email address"
};

Mortice.Form.Field.CREDIT_CARD = { 
	"pattern": /^((4\d{3})|(5[1-5]\d{2})|(6011))([- ])?\d{4}([- ])?\d{4}([- ])?\d{4}|3[4,7]\d{13}$/,
	"message": "This field must contain a valid credit card number"
};

Mortice.Form.Field.ALPHA = { 
	"pattern": /^[a-zA-z\s]+$/,
	"message": "This field must contain only letters"
};

Mortice.Form.Field.ALPHA_NUMERIC = { 
	"pattern": /^[a-zA-Z0-9]+$/,
	"message": "This field must contain only letters or numbers"
};

Mortice.Form.Field.State = Class.create(Mortice.Form.Field, {
	initialize: function($super, element) {
		$super(element);
		
		this.expansion = null;
		
		this.element.onchange = function() {
			var option = this.element.options[this.element.selectedIndex];	
			if(option.value == "Other") {
				this.expand();
				return;
			}
			this.contract();
		}.bind(this);
	},
	
	isExpanded: function() {
		return this.expansion != null;
	},
	
	getValue: function() {
		if(this.isExpanded()) {
			return this.field.getValue();
		}
		return Mortice.Form.Field.prototype.getValue.apply(this);
	},
	
	expand: function() {
		if(this.isExpanded()) {
			return;
		}
		
		this.expansion = {};
		this.expansion.label = new Element("label");
		this.expansion.label.innerHTML = "&nbsp;";
		this.expansion.field = new Element("input", {"class": "text", "type": "text"});
		
		this.element.up().insertBefore(this.expansion.field, this.element.next());
		this.element.up().insertBefore(this.expansion.label, this.element.next());
	},
	
	contract: function() {
		if(!this.isExpanded()) {
			return;
		}
		
		this.expansion.label.remove();
		this.expansion.field.remove();
		this.expansion = null;
	}
});

Mortice.Form.Field.Street = Class.create(Mortice.Form.Field, {
	initialize: function($super, element) {
		$super(element);
	
		// Now also add in the secondary element
		this.secondary = this.element.next().next();
	},
	
	getValue: function() {
		var value = Mortice.Form.Field.prototype.getValue.apply(this);
		
		var secondary = this.secondary.value;
		if(!secondary || secondary.blank()) {
			secondary = null;
		}
		
		if(value == null && secondary == null) {
			return null;
		}
		else if(secondary == null) {
			return value;
		}
		else if(value == null) {
			return secondary;
		}
		
		return value + ", " + secondary;
	}
});

Mortice.Form.Field.Checkbox = Class.create(Mortice.Form.Field, {
	initialize: function($super, element) {
		$super(element);
	},
	
	getValue: function() {
		return this.element.checked ? true : null;
	},
	
	serialize: function() {
		var value = this.getValue();
		return "\"" + this.getName() + "\":" + value; 
	}
});

/**
 * This is the floating error message which sits next to error field.
 * 
 */
Mortice.Form.Field.Error = Class.create(Mortice.Component, {
	initialize: function($super, field, message) {
		$super(new Element("div", {
			"class": "error_icon"
		}));
		this.field = field;
		this.setMessage(message);
	},
	
	setMessage: function(message) {
		if (!this.message) {
			this.message = new Element("div", {
				"class": "error_message"
			});	
		}
		this.message.innerHTML = message;
	},
	
	show: function() {
		var next = this.field.element.next();
		if(this.field.element.type == "checkbox") {
			//next = next.next();
		}
		
		this.field.element.up().insertBefore(this.message, next);
		this.field.element.up().insertBefore(this.element, this.field.element);
		
		// Attach event listener
		this.field.element.addClassName("error");
		this.field.element.onchange = this.hide.bind(this);
		this.field.element.onfocus  = this.hide.bind(this);
	},
	
	hide: function() {
		this.message.remove();
		this.message = null;
		this.element.remove();
		this.field.element.removeClassName("error");
		this.field.element.onchange = function() {}
		this.field.element.onfocus  = function() {}
		this.field.error    = null;
	}
});




Mortice.Overlay = Class.create(Mortice.Component, {
	initialize: function($super) {
		$super($("overlay"));
		this.element.setOpacity(0.7);
	},
	
	show: function() {
		var elements = $$("select");
		for(var i = 0; elements && i < elements.length; i++) {
			elements[i].hide();
		}
		this.element.show();
	},
	
	hide: function() {
		this.element.hide();
		var elements = $$("select");
		for(var i = 0; elements && i < elements.length; i++) {
			elements[i].show();
		}
	}
});

var overlay = null;
Event.observe(window, "load", function() {
	// Find all forms and wrap them up
	var forms = $$("form");
	for(var i = 0; i < forms.length; i++) {
		new Mortice.Form(forms[i]);
	}
	
	overlay = new Mortice.Overlay();
	var height = window.getScrollHeight();
	overlay.element.style.height = (height) + "px";
});

Event.observe(window, "resize", function() {
	if(overlay) {
		var height = window.getScrollHeight();
		
		overlay.element.style.height = (height) + "px";
	}
});

