/**
 * FOX Tree
 *
 * @package FOX
 * @subpackage JavaScript Library
 */

if (!Fox.Control) {
	console.log('Include Fox.Control please');
}
if (!Fox.Api) {
	throw 'Include Fox.Api please';
}

Fox.Control.Form = Class.create(Fox.Control, {
	initialize: function(options) {
		this.options = options;
		if (options.handlers) {
			this.handlers = options.handlers;
		} else {
			this.handlers = {};
		}
		if (options.object) {
			this.object = $H(options.object);
		} else {
			this.object = $H({});
		}
		this.preDefinedValues = $H({});
		
		if (!this.options.handlers) {
			this.options.handlers = {};
		}
	},
	
	setValue: function(name, value) {
		this.preDefinedValues.set(name, value);
	},

	getValue: function(name) {
		this.preDefinedValues.get(name);
	},
	
	redraw: function(data) {
		$H(data.elements).each(function(elt) {
			var name = elt[0];
			var elt = elt[1];
			if (typeof this.form[elt.name] == 'undefined') {
				return;
			}
			//console.log(this.form[elt.name].type);			
			switch (this.form[elt.name].type) {
				case 'select-one':
					
					$A($(this.form[elt.name]).descendants()).invoke('remove');
					if (Object.isArray(elt.options)) {
						$A(elt.options).each(function (text, value) {
							var oOption = document.createElement("option");
							oOption.appendChild(document.createTextNode(text));
							oOption.setAttribute("value", value);
							if (elt.value == value) {
								oOption.setAttribute('selected', 'selected');
							}
	  						this.form[elt.name].appendChild(oOption);
						}.bind(this));
					} else {
						$H(elt.options).each(function (option) {
							var oOption = document.createElement("option");
							oOption.appendChild(document.createTextNode(option[1]));
							oOption.setAttribute("value", option[0]);
							if (elt.value == option[0]) {
								oOption.setAttribute('selected', 'selected');
							}
	  						this.form[elt.name].appendChild(oOption);
						}.bind(this));
					}
				break;
				case 'textarea':
					$(this.form[elt.name]).update(elt.value);
				break;
				default:
					this.form[elt.name].value = elt.value;
				break;
			}
		}.bind(this));
	},
	
	init: function(subject, value) {
		this.form = $(this.options.container);
		this.load();
		if (this.form.addEventListener) {	
			this.form.addEventListener("submit", this._responder.apply(this), false);
		} else {
			this.form.attachEvent("onsubmit", this._responder.apply(this));
		}
	},
	
	_responder: function(e) {
		var _this = this;
		return function(e) {
			_this.formEvent = e;
			_this.stopSubmit();
			return _this._run();
		}
	},
	
	onSubmit: function(data, action) {
		this._sendData(data);
	},
	
	onErrorSubmit: function(data, action, errors) {
		if (typeof (showMessage) == 'function') {
			showMessage('Validation error', $A(errors).join('<br />'), 'red', 0);
		} else if (console.log) {
			console.log('Validation error', $A(errors).join('<br />'), 'red', 0);
		}
	},

	_run: function(submit) {
		var data = $H({});
		if (typeof submit != 'undefined') {
			data.set(submit, submit);
		}
		var validationErrors = [];
		$H(this.loadData.elements).each(function(elt) {
			var name = elt[0];
			var elt = elt[1];
			var formElt = this.form.elements[this.options.name + '_' + name];
			if (!formElt) {
				var formElt = this.preDefinedValues.get(name);
				if (formElt) {
					if (elt.validation) {
						validationErrors = validationErrors.concat(this.validate(formElt, elt.validation));
					}
					data.set(name, formElt);
				} else if (elt.mandatory) {
					validationErrors = validationErrors.concat([name + ' is required field']);
				}
				return;
			}
			switch (elt.type) {
				case 'submit':
					if (typeof submit == 'undefined') {
						submit = elt[0];
						data.set(submit, submit);
					}
				break;
				default:
					if (elt.mandatory && !formElt.value) {
						validationErrors = validationErrors.concat([name + ' is required field']);
					} else if (elt.validation) {
						validationErrors = validationErrors.concat(this.validate(formElt.value, elt.validation));
					}
					data.set(name, formElt.value);
				break;	
			}
		}.bind(this));
		
				
		if (this.dispatchEvent('preSubmit', {data: data, validationErrors: validationErrors}) !== false) {
			if (validationErrors.length) {
				this.onErrorSubmit(data, submit, validationErrors);
			} else {
				this.onSubmit(data);
			}
		}
	},
	
	validate: function(value, params) {
		var messages = [];
		$A(params).each(function(param) {
			var valid = true;
			switch (param.type) {
				case 'regex':
					eval('var regex = ' + param['regex']);
					valid = Object.isString(value) ? value.match(regex) : false;
				break;
				
				case 'lessThan':
					valid = value < param['number'];
				break;
				
				case 'greaterThan':
					valid = value > param['number'];
				break;
				
				case 'shorterThan':
					valid = Object.isString(value) ? value.length  < param['length'] : false;
				break;
				
				case 'longerThan':
					valid = Object.isString(value) ? value.length  > param['length'] : false;
				break;
				
				case 'length':
					valid = Object.isString(value) ? value.length == param['length'] : false;
				break;

				case '_alias':
					valid = Object.isString(value) ? value.match(/^[a-z][a-z\d_]*$/) : false;
				break;
				
				case '_email':
					valid = Object.isString(value) ? value.match(/^([0-9a-zA-Z]+[-._+&;])*[0-9a-zA-Z]+@([-0-9a-zA-Z]+[.])+[a-zA-Z]{2,6}$/) : false;
				break;
				
				case '_domain':
					valid = Object.isString(value) ? value.match(/^([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}$/) : false;
				break;	
				
				case '_url':
					valid = Object.isString(value) ? value.match(new RegEx(
						'/^(https?:\/\/)'
						+ '?(([0-9a-z_!~*\'().&=+$%-]+: )?[0-9a-z_!~*\'().&=+$%-]+@)?' // user@
						+ '(([0-9]{1,3}\.){3}[0-9]{1,3}' // IP - 111.222.333.444
						+ '|' // allows either IP or domain
						+ '([0-9a-z_!~*\'()-]+\.)*' // tertiary domain(s)- www.
						+ '([0-9a-z][0-9a-z-]{0,61})?[0-9a-z]\.' // second level domain
						+ '[a-z]{2,6})' // first level domain - .ru or .museum
						+ '(:[0-9]{1,4})?' // port number - :80 or :8080
						+ '((\/?)|' // a slash isn't required if there is no file name
						+ '(\/[0-9a-z_!~*\'().;?:@&=+$,%#-]+)+\/?)$/' // query
					)) : false;
				break;
			}
			if (!valid) {
				messages.push(param.message);
			}
		}.bind(this));
		return messages;
	},
	
	submit: function(name) {
		this._run(name);
	},
	
	stopSubmit: function() {
		try {
			this.formEvent.stopPropagation();
			this.formEvent.preventDefault();
		} catch (ex) {}
	},
	
	_sendData: function(data) {
		var sendData = $H({});
		data.each(function(item){
			sendData.set(this.options.name + '_' + item[0], item[1]);
		}.bind(this));
		var params = $H({'class': this.options['class'], 'name': this.options['name'], 'getter': this.options['getter']});

		this.object.each(function(param) {
			params.set('_' + param[0], param[1]);
		}.bind(this));
		
		new Fox.Api.call('post', 'orm', 'form', {
			params: params.merge(sendData),
			onSuccess: function(response) {
				this.dispatchEvent('postSubmit', {data: data, response: response});
				if (typeof (showMessage) == 'function') {
					if (response.success) {
						showMessage('Success', response.success[0].text, 'green', 0);
					} else {
						showMessage('Info', response.info[0].text, 'gray', 0);
					}
				}
			}.bind(this),
			onFailure: function(response) {
				if (typeof (showMessage) == 'function') {
					if (response.error) {
						showMessage('Error', response.error[0].text, 'red', 0);
					} else {
						showMessage('Error', '-', 'red', 0);
					}
				}
				this.dispatchEvent('postSubmit', {data: data, errors: response.error});
			}.bind(this)
		});
		return false;
	},
	
	setObjectValue: function(name, value) {
		this.object.set(name, value);
		return this;
	},

	getObjectValue: function(name) {
		this.object.get(name);
	},
	
	deleteObjectValue: function(name) {
		this.object.unset(name);
	},
	
	load: function() {
		var params = $H({'class': this.options['class'], name: this.options['name'], getter: this.options['getter']});

		this.object.each(function(param) {
			params.set('_' + param[0], param[1]);
		}.bind(this));
		
		this.dispatchEvent('preLoad');

		new Fox.Api.call('get', 'orm', 'form', {
			params: params,
			onSuccess: function(data) {
				this.loadData = data;
				this.dispatchEvent('postLoad');
				this.redraw(this.loadData);
			}.bind(this)
		});

		return false;
	}
});

