TMS.Form = Class.create({
	initialize: function(config) {
		var defaultConfig = {
			node : null,
			initfunc : Prototype.K, 
			useChangedDialog : false, 
			validateOnSubmit : true 
		}; 
		config = Object.extend(defaultConfig, config); 
		console.log(config);
		if (config.node !== null) {
			this.node = config.node;
			this.setup(config);
		} else {
			Behaviour.addLoadEvent(function() {
				if (!config.selector) {
					console.log("form selector is required!");
				}
				this.node = $$(config.selector).find(function(e) {
					return e.tagName == 'FORM';
				});
				if (!this.node) {
					console.log('Could not resolve form: ' + selector);
					return;
				}
				this.setup(config);
			});
		}
	},
	setup : function(config) {
		var form = this;
		var initfunc = config.initfunc;
		var validateOnSubmit = !(config.validateOnSubmit === false);
		var useChangedDialog = config.useChangedDialog === true;
		(initfunc.bind(this))(this.node);
		this.node.getElements().each(function(input) {
			Event.observe(input, "change", function(event) {
				form.fireChanged();
			});
		});
		if (validateOnSubmit) {
			this.node.observe("submit", function(event) {
					if (!form.skipValidationYN && !form.validate()) {
							event.stop();
					}
			});
		}
		this.node.validate = function(context) {
			var elements;
			if (!context) {
				elements = $(this).getElements();
			} else {
				elements = $(this).getElements().select(function(e) {
					return e.descendantOf(context);
				});
			}
			return elements.any(
				function(i) {
					var input = $(i);
					if (typeof(input.validate) === 'function') {
						var msg = input.validate();
						if (msg) {
							alert(msg);
							input.activate();
							return true;
						}
					} 
					return false;
				}
			);
		};
		this.applyRules(this.getRules());
		if (useChangedDialog !== false) {
			this.enableChangedDialog();
		}
	},
	register : function(rules) {
		if (typeof(this.ruleSheets) !== 'array') {
			this.ruleSheets = [ rules ];
			var form = this;
			Behaviour.addLoadEvent(function() {
				Behaviour.applySheets(this.ruleSheets, form.getNode());			
			});
		} else {
			this.ruleSheets.push(rules);
		}
	},
	applyRules : function(rules) {
		var rule;
		var form = this.getNode();
		var id = '__' + form.name;
		for (rule in rules) if (rules.hasOwnProperty(rule)) {
			Behaviour.applyFunctionToSelector(rule, form, rules[rule]);
		}
	},
	getNode : function() {
		return this.node;
	},
	changed : false,
	bypassed : false,
	skipValidationYN : false,
	bypass : function(b) {
		if (typeof(b) !== 'boolean') {
			b = true;
		}
		this.bypassed = b;
	},
	skipValidation : function(b) {
		if (typeof(b) !== 'boolean') {
			b = true;
		}
		this.skipValidationYN = b;
	},
	fireChanged : function() {
		this.changed = true;
	},
	hasChanged : function() {
		return this.changed && !this.bypassed;
	},
	addValidation : function(input, func) {
		if (typeof(input.validate) === 'function') {
			var oldfunc = input.validate;
			func = func.methodize().bind(input);
			input.validate = function() {
				var msg = oldfunc();
				if (msg) {
					return msg;
				} else {
					return func();
				}
			}.bind(input);
		} else if (typeof(input.validate) !== 'undefined') {
			throw "'validate' has already been defined on " + input +
					" as something other than a function.";
		} else {
			input.validate = func.methodize().bind(input);
		}
	},
	enableChangedDialog : function() {
		var theForm = this;
		window.onbeforeunload = function() {
			if (theForm.hasChanged()) {
				theForm.bypass(false);
				theForm.skipValidation(false);
				return "You have made changes without saving them.";
			} else {
				theForm.bypass(false);
				theForm.skipValidation(false);
			}
		};
	},
	submit : function() {
		this.bypass(true);
		this.getNode().submit();
	},
	validate : function(context) {
		return !this.getNode().validate(context);
	},
	validateRequired : function(input) {
		if (!input.disabled && !/\S/.test($F(input))) {
			var label;
			try {
				label = $(input).up("td").down("label").innerHTML.gsub(/:|^\s+|\s+$/, '');
			} catch (e) {
				label = "Input";
			}
			return label + " is required.";
		}
	},
	validateInteger : function(input) {
		var val = $F(input).strip();
		if (!val) {
			return;
		}
		if (!/^-?\d+$/.test(input)) {
			return "Please enter an integer.";
		}
	},
	validatePositiveInteger : function(input) {
		var val = $F(input).strip();
		if (!val) {
			return;
		}
		if (!/^0*[1-9]\d$/.test(val)) {
			return $(this).up("td").down("label").innerHTML.gsub(/:|^\s+|\s+$/, '') + 
				" must be a positive whole number.";
		}
	},
	validateDate : function(input) {
		var val;
		if (typeof(input) === 'string') {
			val = val.strip();
		} else {
			val = $F(input).strip();
		}
		if (!val) return null;
		var d = Date.parse(val);
		if (isNaN(d)) {
			return "Please enter a valid date.";
		}
		input.value = TMS.Helpers.formatDate(new Date(d));
		return null;
	},
	validateEmail : function(input) {
		if ($F(input)) {
			if (!/\w+([\-+.]\w+)*@\w+([\-.]\w+)*\.\w+([\-.]\w+)*/.test($F(input))) {
				return "Please enter a valid e-mail address.";
			}
		}
	},
	enableImageButton : function(button) {
		if (button.disabled) {
			button.removeClassName("obscured");
			button.enable();
		}
	},
	disableImageButton : function(button) {
		if (!button.disabled) {
			button.addClassName("obscured");
			button.disable();
		}
	},
	getRules : function() {
		var form = this;
		if (!form.__rules) 
		form.__rules = {
			'select.addRemove' : function(node) {
				var widget = new TMS.AddRemove(
					{	 'source': node, 
						'onchange' : function(event) {
							form.fireChanged();
						}
					});
				widget.setDisabled(node.disabled);
				node.addRemove = widget;
			},
			'input.dateInput' :	function(node) {
				if (node.disabled) return;
				var link =
					$(["a",
						{'href': 'javascript: void(0);',
						'title': 'Click here to choose a date from a pop-up calendar.'},
						['img', {	'src': TMS.imageBase + "icon_calendar.gif", 
									 'alt': "Calendar Icon", 
									 'align' : 'absmiddle',
									 'class' : 'calendarIcon'
								 }
						]
					].parseJsonML());
				var next = node.next();
				if (next)
					node.parentNode.insertBefore(link, next);
				else
					node.parentNode.appendChild(link);
				if (node.hasClassName("canClear")) {
					var clearButton = 
					$(["a",
						{'href': 'javascript: void(0);',
						'title': 'Click here to clear the date.'},
						['img', {	
								'src': TMS.imageBase + "icon_clear_sm.gif", 
								'alt': "Clear Date Icon", 
								'align' : 'absmiddle',
								'style' : 'margin-left: 3'
							}
						]
					].parseJsonML());
					var next = link.next();
					if (next)
						node.parentNode.insertBefore(clearButton, next);
					else
						node.parentNode.appendChild(clearButton);
					clearButton.observe("click", function() { node.clear(); });
				}
				Calendar.setup({
					inputField : node,
					ifFormat : "%m/%d/%Y",
					button: link,
					align: "Tl",
					singleClick: true,
					weekNumbers: false,
					onSelect : function(cal) {
						var p = cal.params;
						var update = (cal.dateClicked || p.electric);
						if (update && p.inputField) {
							p.inputField.value = cal.date.print(p.ifFormat);
							if (typeof p.inputField.onchange == "function")
								p.inputField.onchange();
						}
						if (update && p.displayArea)
							p.displayArea.innerHTML = cal.date.print(p.daFormat);
						if (update && typeof p.onUpdate == "function")
							p.onUpdate(cal);
						if (update && p.flat) {
							if (typeof p.flatCallback == "function")
								p.flatCallback(cal);
						}
						if (update && p.singleClick && cal.dateClicked)
							cal.callCloseHandler();
						form.fireChanged();
					}
				});
				form.addValidation(node, form.validateDate);
				//node.readOnly = true;
			},
			'input.timeInput' : function(node) {
				if (node.disabled) return;
				var name = node.name;
				var html = "<select name=\"" + node.name + "\">";
				var mins = new Array("00","15","30","45");
				var val = node.value.gsub(/^0/, '');
				for(var i = 7; i <= 23; i++) {
					for (var n = 0; n < 4; n++) {
						html += "<option value=\"";
						var hrs = (i % 12);
						if(hrs == 0) {hrs = 12}
						var ampm = (i <= 11) ? " AM" : " PM";
						var str = hrs + ":" + mins[n] + ampm;
						html += str + "\"";
						html += (str == val ? " selected>" : ">") 
									+ str + "</option>";
					}
				}
				html += "</select>";
				var parent = $(node.parentNode);
				node.replace(html);
				parent.down("select[name=\"" + name + "\"]").observe("click", 
					function() {
						form.fireChanged();
					}
				);
			},
			'input.requiredInput, select.requiredInput' : function(input) {
				form.addValidation(input, form.validateRequired);
			},
			'input.emailInput' : function(input) {
				form.addValidation(input, form.validateEmail);
				console.log("applying behaviour for emailInput", input, input.validate);
				input.observe("change", function(event) {
					var msg = this.validate();
					if (msg) {
						alert(msg);
						this.activate();
						event.stop();
					}
				});
			},
			'input.bypass' : function(input) {
				input.observe("click", function() {
						form.bypass(true);
				});
			},
			'input.skipValidation' : function(input) {
				input.observe("click", function() {
						form.skipValidation(true);
				});
			},
			'textarea.veryRichText' : function(input) {
				var editor = new FCKeditor(input.identify());
				editor.BasePath = TMS.scriptBase + "/editor/";
				editor.Config["CustomConfigurationsPath"] = TMS.scriptBase + 'editor_veryRich.js';
				editor.ReplaceTextarea();
			},
			'textarea.richText' : function(input) {
				var editor = new FCKeditor(input.identify());
				editor.BasePath = TMS.scriptBase + "/editor/";
				editor.Config["CustomConfigurationsPath"] = TMS.scriptBase + 'editor_veryRich.js';
				editor.ToolbarSet = 'Basic';
				editor.ReplaceTextarea();
			},
			'input[type=radio].enabler' : function(input) {
				console.log("enabler", input);
				var update = function(event) {
					input.up('form').getInputs("radio", input.name).each(function (el) {
						$(el.up("label").readAttribute('for'))[el.checked ? "enable" : "disable"]();
					});
				};
				input.observe("click", update);
				var label = input.up('label'); 
				label.observe("click", function(event) {
					input.checked = true;
					update();
				});
			},
			'input[type=checkbox].enabler' : function(input) {
				console.log("enabler", input);
				var update = function(event) {
					$$(input.alt).each(function(ctx) {
						var elements = ctx.getElementsBySelector("input, select, textarea");
						console.log("Elements", elements);
						elements.each(function (el) {
							if (el != input) {
								el[input.checked ? "enable" : "disable"]();
							}
						});
					});
				};
				input.observe("click", update);
			},
			'select.submitOnChange' : function(select) {
				select.observe("change", function(event) {
					select.up("form").submit();
				});
			},
			'select.searchOnChange' : function(select) {
				select.observe("change", 
					function(event) {
    					var theForm = select.up("form");
						var theFieldset = select.up("fieldset");
						var params = theForm.serialize(true);
						params['ajax'] = true;
						new Ajax.Request(theForm.action, {
							method : "get",
							parameters : params,
							onFailure: function() {
								alert(
								   "Could not update search options from" + 
								   "server. Please make another selection.");
							},
							onSuccess: function(transport) {
								theFieldset.childElements().invoke("remove");
								theFieldset.innerHTML = transport.responseText;
								form.applyRules(form.getRules());
							}
						});
					}
				);
			}
		};
		return form.__rules;
	}
});

Behaviour.register({
	'form' : function(form) {
		var config = { 
			useChangedDialog : !form.hasClassName("noChangedDialog"),
			node: form
		};
		console.log("form config:", config);
		var wrapper = new TMS.Form(config);
		form.__form = wrapper;
	}
});
