/***************************************************************************************************************************************************************
  REQUIRES:
  - Prototype 1.6.0.2
  - Scriptaculous 1.8.0
***************************************************************************************************************************************************************/

if (Object.isUndefined(CUPH)) { var CUPH = function() { }; }

CUPH.Fields = {
	containers: [],
	fields: [],

	findContainer: function(element) {
		if (element != null && element instanceof CUPH.Fields.Container) {
			return element;
		}

		element = $(element);

		return CUPH.Fields.containers.detect(function(container) {
			return container.element == element;
		}) || null;
	},
	findContainerByField: function(element) {
		var field = CUPH.Fields.findField(element);

		return field != null ? field.container : null;
	},
	findField: function(element) {
		if (element != null && element instanceof CUPH.Fields.ContainerField) {
			return element;
		}

		element = $(element);

		return CUPH.Fields.fields.detect(function(field) {
			return field.element == element;
		}) || null;
	},
	load: function() {
		document.fire(CUPH.Fields.EventNamespace.generic("load"));
	},
	reload: function() {
		var elements = $A(arguments).flatten().compact();

		if (elements.length > 0) {
			var containers = elements.collect(function(element) {
				if (element && !Object.isElement(element)) {
					throw "An argument is not a DOM element (" + element + ").";
				}

				return CUPH.Fields.findContainer(element);
			});

			containers.each(function(container) {
				container.element.fire(CUPH.Fields.EventNamespace.container("reload"), { container: container, type: "container" });
			});
		} else {
			document.fire(CUPH.Fields.EventNamespace.generic("reload"));
		}
	}
};
CUPH.Fields.Container = Class.create({
	initialize: function(element) {
		this.element = $(element);

		if (this.element == null) {
			throw "The element is null.";
		}

		this.disabled = [];
		this.errors = [];
		this.fields = [];
		this.focused = false;
		this.indicators = [];

		this.element.fire(CUPH.Fields.EventNamespace.container("loaded"), { container: this, type: "container" });
	},
	addDisabled: function(field) {
		field = CUPH.Fields.findField(field);

		this.disabled.push(field);

		var memo = new CUPH.Fields.Memo.Base(field);
		field.element.fire(CUPH.Fields.EventNamespace.field("disable"), memo.useType("field"));
		field.container.element.fire(CUPH.Fields.EventNamespace.container("disable"), memo.useType("container"));

		(function() {
			field.element.disabled = true;
		}).defer();
	},
	addError: function(field, message) {
		field = CUPH.Fields.findField(field);

		var existingErrors = this.errors.any(function(error) {
			return error.field == field && error.message == message;
		});

		if (!existingErrors) {
			this.errors.push({ field: field, message: message });

			var memo = new CUPH.Fields.Memo.Error(field, [message]);
			field.element.fire(CUPH.Fields.EventNamespace.field("errorAdded"), memo.useType("field"));
			field.container.element.fire(CUPH.Fields.EventNamespace.container("errorAdded"), memo.useType("container"));
		}
	},
	addFields: function() {
		var fieldElements = $A(arguments).flatten();

		fieldElements.each(function(element) {
			var field = new CUPH.Fields.ContainerField(this, element);

			[this.fields, CUPH.Fields.fields].invoke("push", field);
		}.bind(this));

		return this;
	},
	addIndicator: function(field, type) {
		field = CUPH.Fields.findField(field);

		var existingIndicators = this.indicators.any(function(indicator) {
			return indicator.field == field && indicator.type == type;
		});

		if (!existingIndicators) {
			this.indicators.push({ field: field, type: type });

			var memo = new CUPH.Fields.Memo.Indicator(field, [type]);
			field.element.fire(CUPH.Fields.EventNamespace.field("indicatorAdded"), memo.useType("field"));
			field.container.element.fire(CUPH.Fields.EventNamespace.container("indicatorAdded"), memo.useType("container"));
		}
	},
	blur: function() {
		this.focused = false;

		(function() {
			this.element.fire(CUPH.Fields.EventNamespace.container("blur"), { container: this, type: "container" });
		}.bind(this)).defer();
	},
	focus: function() {
		this.focused = true;

		(function() {
			this.element.fire(CUPH.Fields.EventNamespace.container("focus"), { container: this, type: "container" });
		}.bind(this)).defer();
	},
	getIndicators: function(field) {
		if (Object.isUndefined(field)) {
			return this.indicators.collect(function(indicator) {
				return indicator.type;
			}).uniq();
		}

		field = CUPH.Fields.findField(field);

		if (field == null) {
			return [];
		}

		return this.indicators.findAll(function(indicator) { return indicator.field == field; })
			.collect(function(indicator) { return indicator.type; }).uniq();
	},
	hasAnyDisabledFields: function() {
		return this.disabled.length > 0;
	},
	hasAnyIndicators: function(field) {
		if (Object.isUndefined(field)) {
			return this.indicators.length > 0;
		}

		field = CUPH.Fields.findField(field);

		return field != null && this.indicators.any(function(indicator) {
			return indicator.field == field;
		});
	},
	hasErrors: function(field) {
		if (Object.isUndefined(field)) {
			return this.errors.length > 0;
		}

		field = CUPH.Fields.findField(field);

		return field != null && this.errors.any(function(error) {
			return error.field == field;
		});
	},
	hasIndicator: function(field, type) {
		return this.indicators.any(function(indicator) {
			return indicator.field == field && indicator.type == type;
		});
	},
	isDisabled: function(field) {
		return this.disabled.indexOf(CUPH.Fields.findField(field)) >= 0;
	},
	isFocused: function() {
		return this.focused;
	},
	removeDisabled: function(field) {
		field = CUPH.Fields.findField(field);
		field.element.disabled = false;

		this.disabled = this.disabled.without(field);

		(function() {
			var memo = new CUPH.Fields.Memo.Base(field);
			field.element.fire(CUPH.Fields.EventNamespace.field("enable"), memo.useType("field"));
			field.container.element.fire(CUPH.Fields.EventNamespace.container("enable"), memo.useType("container"));
		}).defer();
	},
	removeError: function(field, message) {
		field = CUPH.Fields.findField(field);

		var existingError = this.errors.detect(function(error) {
			return error.field == field && error.message == message;
		});

		if (existingError) {
			this.errors = this.errors.without(existingError);

			var memo = new CUPH.Fields.Memo.Error(field, [message]);
			field.element.fire(CUPH.Fields.EventNamespace.field("errorRemoved"), memo.useType("field"));
			field.container.element.fire(CUPH.Fields.EventNamespace.container("errorRemoved"), memo.useType("container"));
		}
	},
	removeErrorsByField: function(field) {
		field = CUPH.Fields.findField(field);

		var errors = this.errors.findAll(function(error) {
			return error.field == field;
		});

		if (errors.length > 0) {
			this.errors = this.errors.without.apply(this.errors, errors);

			var messages = errors.collect(function(error) { return error.message; })

			var memo = new CUPH.Fields.Memo.Error(field, messages);
			field.element.fire(CUPH.Fields.EventNamespace.field("errorRemoved"), memo.useType("field"));
			field.container.element.fire(CUPH.Fields.EventNamespace.container("errorRemoved"), memo.useType("container"));
		}
	},
	removeIndicator: function(field, type) {
		field = CUPH.Fields.findField(field);

		var existingIndicator = this.indicators.detect(function(indicator) {
			return indicator.field == field && indicator.type == type;
		});

		if (existingIndicator) {
			this.indicators = this.indicators.without(existingIndicator);

			var memo = new CUPH.Fields.Memo.Indicator(field, [type]);
			field.element.fire(CUPH.Fields.EventNamespace.field("indicatorRemoved"), memo.useType("field"));
			field.container.element.fire(CUPH.Fields.EventNamespace.container("indicatorRemoved"), memo.useType("container"));
		}
	},
	removeIndicatorsByField: function(field) {
		field = CUPH.Fields.findField(field);

		var indicators = this.indicators.findAll(function(error) {
			return error.field == field;
		});

		if (indicators.length > 0) {
			this.indicators = this.indicators.without.apply(this.indicators, indicators);

			var types = indicators.collect(function(error) { return error.type; }).uniq();

			var memo = new CUPH.Fields.Memo.Indicator(field, types);
			field.element.fire(CUPH.Fields.EventNamespace.field("indicatorRemoved"), memo.useType("field"));
			field.container.element.fire(CUPH.Fields.EventNamespace.container("indicatorRemoved"), memo.useType("container"));
		}
	}
});
CUPH.Fields.ContainerField = Class.create({
	initialize: function(container, element) {
		this.container = container;
		this.element = $(element);

		if (this.container == null) {
			throw "The container is null.";
		}
		if (this.element == null) {
			throw "The element is null.";
		}

		this.element.fire(CUPH.Fields.EventNamespace.field("loaded"), new CUPH.Fields.Memo.Base(this).useType("field"));
	},
	addError: function(message) {
		this.container.addError(this, message);
	},
	addIndicator: function(type) {
		this.container.addIndicator(this, type);
	},
	disable: function() {
		this.container.addDisabled(this);
	},
	enable: function() {
		this.container.removeDisabled(this);
	},
	getIndicators: function() {
		return this.container.getIndicators(this);
	},
	hasAnyIndicators: function() {
		return this.container.hasAnyIndicators(this);
	},
	hasErrors: function() {
		return this.container.hasErrors(this);
	},
	hasIndicator: function(type) {
		return this.container.hasIndicator(this, type);
	},
	isDisabled: function() {
		return this.container.isDisabled(this);
	},
	removeAllErrors: function() {
		this.container.removeErrorsByField(this);
	},
	removeAllIndicators: function() {
		this.container.removeIndicatorsByField(this);
	},
	removeError: function(message) {
		this.container.removeError(this, message);
	},
	removeIndicator: function(type) {
		this.container.removeIndicator(this, type);
	}
});
CUPH.Fields.Element = { };
CUPH.Fields.Element.Methods = {
	container: function(element) {
		var theContainer = CUPH.Fields.findContainerByField(element);

		if (theContainer != null) {
			return theContainer;
		} else {
			throw "A container instance of type CUPH.Fields.Container is not associated with this element (" + $(element).identify() + ").";
		}
	},
	field: function(element) {
		var theField = CUPH.Fields.findField(element);

		if (theField != null) {
			return theField;
		} else {
			throw "A field instance of type CUPH.Fields.ContainerField is not associated with this element (" + $(element).identify() + ").";
		}
	}
};
CUPH.Fields.EventNames = {
	blur: "blur",
	disable: "disable",
	enable: "enable",
	errorAdded: "errorAdded",
	errorRemoved: "errorRemoved",
	focus: "focus",
	indicatorAdded: "indicatorAdded",
	indicatorRemoved: "indicatorRemoved",
	load: "load",
	loaded: "loaded",
	reload: "reload"
};
CUPH.Fields.EventNamespace = {
	container: function(eventName) {
		return "cuph-fields-container:" + CUPH.Fields.EventNames[eventName];
	},
	field: function(eventName) {
		return "cuph-fields-field:" + CUPH.Fields.EventNames[eventName];
	},
	generic: function(eventName) {
		return "cuph-fields:" + CUPH.Fields.EventNames[eventName];
	}
};
CUPH.Fields.Memo = { };
CUPH.Fields.Memo.Base = Class.create({
	initialize: function(field) {
		this.container = field.container;
		this.field = field;
	},
	useType: function(type) {
		this.type = type == "container" ? "container" : "field";

		return this;
	}
});
CUPH.Fields.Memo.Error = Class.create(CUPH.Fields.Memo.Base, {
	initialize: function($super, field, messages) {
		$super(field);

		this.messages = messages;
	}
});
CUPH.Fields.Memo.Indicator = Class.create(CUPH.Fields.Memo.Base, {
	initialize: function($super, field, types) {
		$super(field);

		this.indicators = types;
	}
});

CUPH.Fields.Helper = {
	getElementsByName: function(name) {
		return $A(document.getElementsByName(name)).collect(function(element) {
			return $(element);
		});
	},
	getElementsByNames: function() {
		return $A(arguments).flatten().inject([], function(array, name) {
			CUPH.Fields.Helper.getElementsByName(name).each(function(element) {
				array.push(element);
			});

			return array;
		});
	}
}

if (Object.isUndefined($N)) {
	var $N = CUPH.Fields.Helper.getElementsByName;
}

Element.addMethods($w("input select textarea"), CUPH.Fields.Element.Methods);

