var Form = {
	serialize: function(form) {
		var elements = Form.getElements($(form));
		var queryComponents = new Array();

		for (var i = 0; i < elements.length; i++) {
			var queryComponent = Form.Element.serialize(elements[i]);
			if (queryComponent) {
				queryComponents.push(queryComponent);
			}
		}

		return queryComponents.join('&');
	},

	getElements: function(form) {
		form = $(form);
		var elements = new Array();

		for (var tagName in Form.Element.Serializers) {
			var tagElements = form.getElementsByTagName(tagName);
			for (var j = 0; j < tagElements.length; j++) {
				elements.push(tagElements[j]);
			}
		}
		return elements;
	},

	getInputs: function(form, typeName, name) {
		form = $(form);
		var inputs = form.getElementsByTagName('input');

		if (!typeName && !name) {
			return inputs;
		}

		var matchingInputs = new Array();
		for (var i = 0; i < inputs.length; i++) {
			var input = inputs[i];
			if ((typeName && input.type != typeName) ||
			    (name && input.name != name)) {
				continue;
			}
			matchingInputs.push(input);
		}

		return matchingInputs;
	},

	disable: function(form) {
		var elements = Form.getElements(form);
		for (var i = 0; i < elements.length; i++) {
			var element = elements[i];
			element.blur();
			element.disabled = 'true';
		}
	},

	enable: function(form) {
		var elements = Form.getElements(form);
		for (var i = 0; i < elements.length; i++) {
			var element = elements[i];
			element.disabled = '';
		}
	},

	findFirstElement: function(form) {
		return Form.getElements(form).find(function(element) {
			return element.type != 'hidden' && !element.disabled &&
			       ['input', 'select', 'textarea'].include(element.tagName.toLowerCase());
		});
	},

	focusFirstElement: function(form) {
		// todo this function is not found: Field.activate
		// Field.activate(Form.findFirstElement(form));
	},

	reset: function(form) {
		$(form).reset();
	}
};

Form.Element = {
	serialize: function(element) {
		element = $(element);
		var method = element.tagName.toLowerCase();
		var parameter = Form.Element.Serializers[method](element);

		if (parameter) {
			var key = encodeURIComponent(parameter[0]);
			if (key.length == 0) {
				return;
			}

			if (parameter[1].constructor != Array) {
				parameter[1] = [parameter[1]];
			}

			return parameter[1].map(function(value) {
				return key + '=' + encodeURIComponent(value);
			}).join('&');
		}
	},

	getValue: function(element) {
		element = $(element);
		var method = element.tagName.toLowerCase();
		var parameter = Form.Element.Serializers[method](element);

		if (parameter) {
			return parameter[1];
		}
	}
};

Form.Element.Serializers = {
	input: function(element) {
		switch (element.type.toLowerCase()) {
			case 'submit':
			case 'hidden':
			case 'password':
			case 'text':
				return Form.Element.Serializers.textarea(element);
			case 'checkbox':
			case 'radio':
				return Form.Element.Serializers.inputSelector(element);
		}
		return false;
	},

	inputSelector: function(element) {
		if (element.checked) {
			return [element.name, element.value];
		}
	},

	textarea: function(element) {
		return [element.name, element.value];
	},

	select: function(element) {
		return Form.Element.Serializers[element.type == 'select-one' ?
		                                'selectOne' : 'selectMany'](element);
	},

	selectOne: function(element) {
		var value = '', opt, index = element.selectedIndex;
		if (index >= 0) {
			opt = element.options[index];
			value = opt.value;
			if (!value && !('value' in opt)) {
				value = opt.text;
			}
		}
		return [element.name, value];
	},

	selectMany: function(element) {
		var value = new Array();
		for (var i = 0; i < element.length; i++) {
			var opt = element.options[i];
			if (opt.selected) {
				var optValue = opt.value;
				if (!optValue && !('value' in opt)) {
					optValue = opt.text;
				}
				value.push(optValue);
			}
		}
		return [element.name, value];
	}
};
Element.implement({
	enableButtons: function() {
		var inputs = this.getElements("input");
		var buttons = inputs.filter(function (item, index) {
			var buttonType = item.getProperty("type");
			return buttonType == "button" || buttonType == "submit" || buttonType == "reset";
		});
		buttons.each(function (item, index) {
			item.disabled = false;
		});
	},
	disableButtons: function() {
		var inputs = this.getElements("input");
		var buttons = inputs.filter(function (item, index) {
			var buttonType = item.getProperty("type");
			return buttonType == "button" || buttonType == "submit" || buttonType == "reset";
		});
		buttons.each(function (item, index) {
			item.disabled = true;
		});
	},
	ajaxJSON: function(options) {
		if (this.tagName.toLowerCase() != "form") {
			alert("This class only applies to form tag.");
			return;
		}
		if (!this.getProperty("action")) {
			alert("This class requires the form tag has an action property.");
			return;
		}
		var enctype = this.getProperty("enctype");
		if (enctype && enctype.toLowerCase() == "multipart/form-data") {
			return new iFrameFormRequest(this, options);
		} else {
			return new FormRequestJSON(this, options);
		}
	}
});

var FormRequestJSON = new Class({

	Implements: Options,

	options: {
		onRequest: function(form) {
			return true;
		},
		onComplete: function(responseJSON, responseText) {},
		onFailure: function() {
			errorMessage("发生网络传输错误，请重试");
		}
	},

	initialize: function(formElement, options) {
		this.formElement = formElement;
		this.action = formElement.getProperty("action");
		if (!this.action) {
			errorMessage("Form 必须指定 action 属性。")
		}
		this.setOptions(options);
		this.loading = false;
		this.formElement.addEvent('submit', function(event) {
			new Event(event).stop();
			this.formElement.disableButtons();
			var valid = this.options.onRequest.run(this.formElement);
			if (!valid) {
				this.formElement.enableButtons();
				return;
			}
			var params = Form.serialize(this.formElement);
			new Request.JSON({
				url: this.action,
				method : 'post',
				data : params,
				onSuccess : function(responseJSON, responseText) {
					this.formElement.enableButtons();
					this.options.onComplete.run(responseJSON, responseText);
				}.bind(this),
				onFailure : function (xhr) {
					this.formElement.enableButtons();
					this.options.onFailure.run(xhr);
				}.bind(this)
			}).send();
		}.bind(this));
	}
});

var iFrameFormRequest = new Class({

	Implements: Options,

	options: {
		onRequest: $empty,
		onComplete: function(data) {},
		onFailure: $empty
	},

	initialize: function(formElmt, options) {
		this.formElement = formElmt;
		this.setOptions(options);
		this.frameId = 'f' + Math.floor(Math.random() * 99999);
		this.formElement.set('target', this.frameId);
		this.loading = false;
		this.formElement.addEvent('submit', function(event) {
			this.formElement.disableButtons();
			this.loading = this.options.onRequest();
			if (!this.loading) {
				new Event(event).stop();
				this.formElement.enableButtons();
			}
		}.bind(this));

		this.iframe = new IFrame({
			name: this.frameId,
			styles: {
				display: 'none'
			},
			src: 'about:blank',
			events: {
				load: function(self) {
					if (self.loading) {
						var doc = document.getElementById(self.frameId).contentWindow.document;
						if (doc) {
							if (doc.location.href == 'about:blank') {
								self.options.onFailure();
							}
							if ($type(self.options.onComplete) == 'function') {
								var body = $(doc.body);
								var html = body.get('html');
								self.options.onComplete(html, html);
							}
						} else {
							self.options.onFailure();
						}
						self.loading = false;
						self.formElement.enableButtons();
					}
				}.pass(this)
			}
		}).inject($(document.body), 'top');
	},

	toElement: function() {
		return this.iframe;
	}

});

var iFrameFormRequestJSON = new Class({

	Implements: Options,

	options: {
		onRequest: $empty,
		onComplete: function(data) {},
		onFailure: $empty
	},

	initialize: function(formElmt, options) {
		this.formElement = formElmt;
		this.setOptions(options);
		this.frameId = 'f' + Math.floor(Math.random() * 99999);
		this.formElement.set('target', this.frameId);
		this.loading = false;
		this.formElement.addEvent('submit', function(event) {
			this.formElement.disableButtons();
			this.loading = this.options.onRequest();
			if (!this.loading) {
				new Event(event).stop();
				this.formElement.enableButtons();
			}
		}.bind(this));

		this.iframe = new IFrame({
			name: this.frameId,
			styles: {
				display: 'none'
			},
			src: 'about:blank',
			events: {
				load: function(self) {
					if (self.loading) {
						var doc = document.getElementById(self.frameId).contentWindow.document;
						if (doc) {
							if (doc.location.href == 'about:blank') {
								self.options.onFailure();
							}
							if ($type(self.options.onComplete) == 'function') {
								var body = $(doc.body);
								var bodyText = body.get('text');
								self.options.onComplete(JSON.decode(bodyText), bodyText);
							}
						} else {
							self.options.onFailure();
						}
						self.loading = false;
						self.formElement.enableButtons();
					}
				}.pass(this)
			}
		}).inject($(document.body), 'top');
	},

	toElement: function() {
		return this.iframe;
	}

});

