var NeoForm = {
	Version: '0.4',

	evalReq: function(req) {
		var json = null;
		// yes, don't count on prototype's JSON support: it's completely fucked up
		if (/text\/(?:x-)?json/.test(req.getResponseHeader('Content-Type'))) {
			try {
				eval('json = ' + req.responseText);
			} catch (e) {} // { alert("cannot parse server response :("); }
		}
		return json;
	},
	
	submit: function(form, handler_ajax, handler_prepare, ev) {
		if (typeof handler_prepare == 'function') {
			if (!handler_prepare(form)) {
				if (typeof ev != 'undefined') Event.stop(ev);
				return false;
			}
		}
		new Ajax.Request(
			form.getAttribute('action'),
			{
				asynchronous: 1,
				method: form.getAttribute('method'),
				parameters: Form.serialize(form),
				onComplete: function (req, json) {
					if (!json) json = NeoForm.evalReq(req);
					handler_ajax(req, json);
				}
			}
		);
		return false;
	},

	error: function(form, field) {
		var el  = form[field];
		if (!el) return false;
		var row = Element.findParent(el, 'div', 'row');
		if (!row) return false;
		Element.addClassName(row, 'row-error');
		new Effect.Pulsate(row, {duration: 1, from: 0.5});
		Field.activate(el);
		return false;
	},

	reset_errors: function(form) {
		var elements = Form.getElements(form);
		if (!elements) return false;
		for (var j=0; j < elements.length; j++) {
			var row = Element.findParent(elements[j], 'div', 'row');
			if (row) Element.removeClassName(row, 'error');
		}
		return true;
	},

	defaultOptions: function() {
		return {
			legend: 'contact form',
			method: 'post',
			elements: {
				name:    {	label: 'Your name' },
				email:   {	label: 'Your email address',
							validate: function (s) {
								return RegExp("^[a-zA-Z0-9-_.]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}$").test(s)
							}
						},
				subject: {	label: 'Subject' },
				message: {	label: 'Message', type: 'textarea' },
				submit:  {	type: 'submit', value: 'send message' }
			},
			notes: {},
			after_submit:  function (req, json) {
					Message.add('message sent.', {timeout: 2});
				},
			before_submit: this.validate
		};
	},

	validate: function (form) {
		NeoForm.reset_errors(form);
		for (el in form.neoform.options.elements) {
			var validate = form.neoform.options.elements[el].validate
							|| function (value) {
									return /\S/.test(value)
								};
			if (!validate($F('f_' + el))) {
				Message.add('invalid value for "' + el + '"', {timeout: 3, level: 'error', before: form});
				NeoForm.error(form, 'f_' + el);
				return false;
			}
		}
		return true;
	},

	create: function(base_element, options) {
		base_element = $(base_element);
		if (!base_element) {
			Message.add('Cannot create contact form! Missing "base element"!',
						{level: 'error'});
			return false;
		}
		base_element.innerHTML = '';

		if (!options.action) {
			Message.add('Cannot create contact form! Missing "action"!',
						{level: 'error', before: base_element});
			return false;
		}

		this.options = this.defaultOptions();
		Object.extend(this.options, options || {});

		var form = Element.create('form', base_element);
		form.setAttribute('action', this.options.action);
		form.setAttribute('method', this.options.method);
		form.setAttribute('id', base_element.getAttribute('id') + '_form');

		var fieldset = Element.create('fieldset', form);
		Element.create('legend', fieldset).appendChild(Element.textNode(this.options.legend));

		for (elem in this.options.elements) {
			this.createRow(elem, fieldset);
		}

		var before = this.options.after_submit;
		var after  = this.options.before_submit;
		form.onsubmit = function (ev) {
			return NeoForm.submit(form, before, after, ev);
		};

		form.neoform = this;
		Form.focusFirstElement(form);

		return form;
	},

	createRow: function(field, parent) {
		var params = {
			label: '',
			type: 'text',
			optional: false
		};
		Object.extend(params, this.options.elements[field] || {});

		var row = Element.create('div', parent);
			Element.addClassName(row, 'row');
			Element.addClassName(row, params.type);
			if (params.optional) Element.addClassName(row, 'optional');

		if (params.label) {
			var label = Element.create('label', row);
				label.setAttribute('for', 'f_' + field);

			var label_text = Element.create('span', label);
				label_text.appendChild(Element.textNode(params.label + ':'));
				Element.addClassName(label_text, 'label');

			if (this.options.notes[field]) {
				var label_notes = Element.create('span', label);
					// neah, let HTML content flow
					// label_notes.appendChild(Element.textNode(this.options.notes[field]));
					label_notes.innerHTML = this.options.notes[field];
					Element.addClassName(label_notes, 'notes');
			}
		}

		var el;

		switch (params.type) {
			case 'text' :
				// IE fucks up 'type' property
				el = Element.create('input');
				el.type = 'text';
				row.appendChild(el);
				Element.addClassName(el, 'text');
				el.setAttribute('value', params.value || '');
				break;
			case 'textarea' :
				el = Element.create('textarea', row);
				Element.addClassName(el, 'text');
				el.setAttribute('rows', params.rows || 10);
				el.setAttribute('cols', params.cols || 40);
				el.appendChild(Element.textNode(params.value || ''));
				break;
			case 'submit' :
				// IE fucks up 'type' property
				el = Element.create('input');
				el.type = 'submit';
				row.appendChild(el);
				Element.addClassName(el, 'button');
				Element.addClassName(el, 'submit');
				el.setAttribute('value', params.value || '');
				break;
			default : 
				el = Element.create('div', row);
				el.appendChild(Element.textNode(params.value || ''));
		}

		el.setAttribute('name', field);
		el.setAttribute('id', 'f_' + field);
		el.setAttribute('title', params.label);

		return row;
	}
};
