
Ext.define('Ext.ux.form.ExtendedFieldContainer', {

	alias: ['widget.extendedfieldcontainer', 'widget.ExtendedFieldContainer', 'widget.extendedFieldContainer'],

	extend: 'Ext.form.FieldContainer',

	mixins: {
		field: 'Ext.form.field.Field'
	},

	statics: {

		addNoValidation: function(tobind, preventValidation) {

			preventValidation = preventValidation || false;

			Ext.applyIf(tobind, {
				preventValidation: preventValidation,
				stopValidation: function() {

					var field = this,
						checkChange = field.suspendCheckChange;

					field.preventValidation = true;

					field.validateOnBlur = false;

					field.validateOnChange = false;

					field.suspendCheckChange = 1;

					if (field.unsetActiveError) {
						field.unsetActiveError();
					}

					if (field.clearInvalid) {
						field.clearInvalid();
					}

					field.doComponentLayout();

					field.fireEvent('validitychange', field, true);

					field.suspendCheckChange = checkChange;

					return field;
				},
				startValidation: function() {

					var field = this,
						checkChange = field.suspendCheckChange,
						errors;

					field.preventValidation = false;

					errors = field.getErrors.call(field);

					field.validateOnBlur = true;

					field.validateOnChange = true;

					field.suspendCheckChange = 0;

					field.setActiveError(errors);

					field.markInvalid(errors);

					field.doComponentLayout();

					field.fireEvent('validitychange', field, field.isValid());

					field.suspendCheckChange = checkChange;

					return field;
				}
			});

			if (tobind.isComponent) {

				if (!tobind._getErrors) {

					tobind._getErrors = tobind.getErrors;

					tobind.getErrors = function(value) {

						var field = this,
							preventValidation = field.preventValidation;

						return preventValidation === true ? [] : field._getErrors(value);
					};
				}

				if (!tobind.ownerCt) {

					tobind.on({
						'added': {
							fn: function(self) {

								if (self.preventValidation) {
									self.stopValidation();
								}
							}
						}
					});
				}
				else {

					if (tobind.preventValidation) {

						tobind.stopValidation();
					}
				}
			}
			else {

				var newFn = function(item) {

					if (!item._getErrors)
						item._getErrors = item.getErrors;

					item.getErrors = function(value) {

						var field = this,
							preventValidation = field.preventValidation;

						return preventValidation === true ? [] : field._getErrors(value);
					};

					if (item.preventValidation === true)
						item.stopValidation();
				};

				ip2.extendConfigListener(item, 'added', fn);
			}

			return tobind;
		}
	},

	config: {

		preventValidation: false
	},

	allowBlank: true,

	value: '',

	boundConfig: {
		validation: [
			'allowBlank',
			'blankText',
			'emptyText',
			'invalidCls',
			'invalidText',
			'maxLengthText',
			'minLengthText',
			'regex',
			'validateOnBlur',
			'validateOnChange',
			'validator',
			'vtype',
			'vtypeText'
		],
		events: [
			'change',
			'blur',
			'focus'
		]
	},

	blankText: 'This field is required',

	maxLengthText: 'The maximum length for this field is {0}',

	minLengthText: 'The minimum length for this field is {0}',

	boundComponents: null,

	boundValidationComponents: null,

	_isValidationBound: null,

	preventValidation: null,

	initComponent: function() {

		var field = this,
			config = field.initialConfig;

		field.boundValidationComponents = [];

		field.boundComponents = [];

		field._isValidationBound = false;

		field.fixComponentLayout();

		field.callParent();

		field.initField();

		field.setPreventValidation(config.preventValidation);

		if (field.getPreventValidation()) {

			field.on({
				'added': {
					fn: function(self) {

						console.log('I am added and will validation bind');
						self.stopValidation();
					}
				}
			});
		}

		return field;
	},

	getPreventValidation: function() {

		return this.preventValidation;
	},

	setPreventValidation: function(toSet) {

		toSet = toSet || false;

		this.preventValidation = toSet;

		return this;
	},

	getEventNames: function(component) {

		var field = this,
			events = {
				has: [],
				hasNot: []
			};

		Ext.Object.each(component.events, function(key) {

			if (Ext.Array.indexOf(field.boundConfig.events, key) > -1) {

				var toPush = component.events[key] ? events.has : events.hasNot;

				toPush.push(key);
			}
		});

		return events;
	},

	applyComponentBind: function(tobind) {

		var field = this,
			bindArray = [];

		if (Ext.typeOf(tobind) != 'array')
			bindArray.push(tobind);

		Ext.Array.each(bindArray, function(bindee) {

			// Is bindee a component already made?
			if (bindee.isComponent) {

				field.applyValidationBind(bindee)
					.applyEventsBind(bindee)
					.applyDirtyBind(bindee);

				field.boundComponents.push(bindee);
			}

			// Then we must assume a config object to create one
			else {

				var addedFn;

				addedFn = function(self) {

					field.applyValidationBind(self)
						.applyEventsBind(self)
						.applyDirtyBind(self);

					field.boundComponents.push(self);
				};

				ip2.extendConfigListener(bindee, 'added', addedFn);
			}
		});

		return tobind;
	},

	applyEventsBind: function(component) {

		var field = this,
			eventNames,
			events;

		eventNames = field.getEventNames(component);

		Ext.Array.each(eventNames.hasNot, function(name) {

			field.addEvent(name);
		});

		events = eventNames.has.concat(eventNames.hasNot);

		Ext.Array.each(events, function(name) {

			component.on(name, function() {

				field.fireEvent.call(field, name, field);
			}, component);
		});

		return this;
	},
	applyValidationBind: function(component) {

		var field = this,
			validitychange;

		validitychange = {
			fn: function(cmp, isValid, eOpts) {

				field.fireEvent('validitychange', field, isValid, eOpts);
			}
		};

		field.boundValidationComponents.push(component);

		if (!field._isValidationBound && !field._getErrors) {

			field._getErrors = field.getErrors;

			field.getErrors = function(value) {

				var field = this,
					preventValidation = field.preventValidation;

				return preventValidation === true ? [] : field._getErrors(value);
			};

			field._isValidationBound = true;
		}

		if (!component._getErrors) {

			component._getErrors = component.getErrors;

			component.getErrors = function(value) {

				var component = this,
					parent = field,
					preventValidation = field.preventValidation;

				return preventValidation === true ? [] : component._getErrors(value);
			};
		}

		component.on({
			'validitychange': validitychange
		});

		field.fireEvent('validationbind', field, [component]);

		return field;
	},
	applyDirtyBind: function(component) {

		var field = this;

		component.on({
			'dirtychange': {
				fn: function() {

					field.checkDirty();
				}
			}
		});

		return field;
	},

	// TODO: REMOVE WHEN I AM SURE WTF WENT ON HERE!
	applyValidationConfig: function(component) {

		var field = this,
			initialConfig = field.initialConfig,
			validationProps;

		validationProps = [
			'allowBlank',
			'blankText',
			'emptyText',
			'invalidCls',
			'invalidText',
			'maxLengthText',
			'minLengthText',
			'regex',
			'validateOnBlur',
			'validateOnChange',
			'validator',
			'vtype',
			'vtypeText'
		];

		Ext.Array.each(validationProps, function(property) {

			var value;

			value = initialConfig[property];

			if (typeof value !== 'undefined')
				component[property] = value;
		});

		component._isValidationBound = true;

		return component;
	},

	// bindValidationComponent - DEPRECATED - RENAME YOUR METHOD TO: applyComponentBind
	bindValidationComponent: function(tobind) {

		var field = this;

		console.log(field.xtype, 'clean up here, this component class needs to rename its method "bindValidationComponent" -> "applyComponentBind"');

		return field.applyComponentBind(tobind);
	},

	fixComponentLayout: function(items) {

		items = items || this.items;

		var field = this,
			config = field.initialConfig.componentLayout || null;

		if (config && !Ext.isEmpty(items))
			doLayout(items);

		function doLayout(items) {

			Ext.Array.each(items, function(item) {

				if (Ext.isObject(item)) {

					Ext.Object.each(config, function(key, layout) {

						if (item.name == key && !Ext.isEmpty(layout)) {

							Ext.Object.merge(item, layout);
						}
					});

					if (!Ext.isEmpty(item.items))
						doLayout(item.items);
				}
				else if (Ext.isArray(item)) {

					doLayout(item);
				}
			});
		}
	},

	isValid: function() {

		var field = this,
			value;

		value = field.getValue();

		return field.disabled || field.validateValue(value);
	},

	getErrors: function(value) {

		var field = this,
			errorsArray = [],
			boundArray = field.boundValidationComponents;

		Ext.Array.each(boundArray, function(bound) {

			var errors;

			if (Ext.isFunction(bound.getErrors)) {

				errors = bound.getErrors(value);

				if (errors.length)
					errorsArray = Ext.Array.merge(errorsArray, errors);
			}
		});

		return errorsArray;
	},

	stopValidation: function() {

		var field = this,
			boundArray = field.boundValidationComponents;

		field.preventValidation = true;

		Ext.Array.each(boundArray, function(bound) {

			var checkChange = bound.suspendCheckChange;

			bound.validateOnBlur = false;

			bound.validateOnChange = false;

			bound.suspendCheckChange = 1;

			if (bound.unsetActiveError) {
				bound.unsetActiveError();
			}

			if (bound.clearInvalid) {
				bound.clearInvalid();
			}

			bound.doComponentLayout();

			bound.fireEvent('validitychange', bound, true);

			bound.suspendCheckChange = checkChange;
		});

		//field.fireEvent('validitychange', field, true);

		return field;
	},

	startValidation: function() {

		var field = this,
			boundArray = field.boundValidationComponents;

		field.preventValidation = false;

		Ext.Array.each(boundArray, function(bound) {

			var errors = bound.getErrors(),
				checkChange = bound.suspendCheckChange;

			bound.validateOnBlur = true;

			bound.validateOnChange = true;

			bound.suspendCheckChange = 0;

			bound.setActiveError(errors);

			bound.markInvalid(errors);

			bound.doComponentLayout();

			bound.fireEvent('validitychange', bound, bound.isValid());

			bound.suspendCheckChange = checkChange;
		});

		//field.suspendCheckChange = 0;

		//field.fireEvent('validitychange', field, field.isValid());

		return field;
	},

	clearInvalid: function() {

		var field = this,
			boundArray = field.boundValidationComponents;

		Ext.Array.each(boundArray, function(bound) {

			if (Ext.isFunction(bound.clearInvalid))
				bound.clearInvalid();
		});

		return field;
	},

	markInvalid: function(errors) {

		errors = errors || [];

		var field = this,
			boundArray = field.boundValidationComponents;

		Ext.Array.each(boundArray, function(bound) {

			if (Ext.isFunction(bound.markInvalid))
				bound.markInvalid(errors);
		});

		return field;
	},

	unsetActiveError: function() {

		var field = this,
			boundArray = field.boundValidationComponents;

		Ext.Array.each(boundArray, function(bound) {

			if (Ext.isFunction(bound.unsetActiveError))
				bound.unsetActiveError.apply(bound);
		});

		return field;
	},

	setActiveError: function(errors) {

		var field = this,
			boundArray = field.boundValidationComponents;

		Ext.Array.each(boundArray, function(bound) {

			if (Ext.isFunction(bound.setActiveError))
				bound.setActiveError.apply(bound, errors);
		});

		return field;
	},

	validateValue: function(value) {

		var field = this,
			errors,
			isValid;

		errors = field.getErrors(value);

		isValid = Ext.isEmpty(errors);

		return isValid;
	}
});
