/**
 * @class Ext.ux.util.DMDUiType
 * Utility class for handling UI Configurations used by the DMD and XPD
 */
Ext.define("Ext.ux.util.DMDUiType", {

	statics: {
		preprocess: function (o_config, s_context, o_rulesJs, o_rulesXpd, o_xpd) {

			var i_self = this;
			var s_type = o_config.type;

			// Do the default preprocess for all uitypes
			var f_defaultUiPreprocess = o_rulesJs.uitype.preprocess;

			if (f_defaultUiPreprocess) {
				o_config = f_defaultUiPreprocess(o_config);
			}

			var o_uiTypeRule = i_self.getUiTypeRule(s_type, s_context, o_rulesJs);
			var o_uiRule = i_self.getUiRule(s_type, o_rulesXpd);

			// Do the ui specific preprocess
			if (!o_uiTypeRule) {

				// No uirule found for this - throw a message about it and
				// just return o_config
				console.warn("No uirule found for: \"", s_type, "\" in uirules.jsconfig");

				return o_config;
			}

			var f_uiPreprocess = o_uiTypeRule ? o_uiTypeRule.preprocess : null;
			if (f_uiPreprocess) {
				o_config = f_uiPreprocess(o_config, o_uiRule);
			}

			// Do the property specific preprocesses
			Ext.Object.each(o_config, function (s_key, value) {

				if (value !== undefined) {
					var f_defaultPropertyPreprocess = o_rulesJs.property.preprocess;
					var o_uiPropertyTypeRule = i_self.getUiPropertyTypeRule(s_key, s_context, o_rulesJs);

					// Do the default preprocess for all properties
					if (f_defaultPropertyPreprocess) {
						f_defaultPropertyPreprocess(o_config, s_key, value, o_xpd);

						// read value again:
						value = o_config[s_key];
					}

					// Do the property specific preprocesses
					if (o_uiPropertyTypeRule) {

						var f_uiPropertyPreprocess = o_uiPropertyTypeRule.preprocess;
						if (f_uiPropertyPreprocess) {
							f_uiPropertyPreprocess(o_config, s_key, value, o_xpd);
						}
					}
				}
			});

			return o_config;
		},
		postprocess: function (o_config, s_context, o_rulesJs, o_rulesXpd, o_xpd) {

			var i_self = this;
			var s_type = o_config.type;
			var a_postprocess = [];

			// Find and add common postprocess for all uitypes
			var f_defaultUiTypePostprocess = o_rulesJs.uitype.postprocess;
			if (f_defaultUiTypePostprocess) {
				a_postprocess.push(function (o_fields) {
					var i_self = this;
					f_defaultUiTypePostprocess.call(i_self, i_self, o_fields);
				});
			}

			var o_uiTypeRule = i_self.getUiTypeRule(s_type, s_context, o_rulesJs);
			var o_uiRule = i_self.getUiRule(s_type, o_rulesXpd);

			// Do the ui specific preprocess
			var f_uiPostprocess = o_uiTypeRule ? o_uiTypeRule.postprocess : null;
			if (f_uiPostprocess) {
				a_postprocess.push(function (o_fields) {
					var i_self = this;
					var a_arguments = Array.prototype.slice.call(arguments);
					a_arguments.unshift(i_self);
					f_uiPostprocess.apply(i_self, a_arguments);
				});
			}

			// Do the property specific preprocesses
			Ext.Object.each(o_config, function (s_key, value) {
				if (value !== undefined) {
					var o_rule = o_uiRule.properties[s_key];

					// Do the default preprocess for all properties
					var f_defaultPropertyPostprocess = o_rulesJs.property.postprocess;
					if (f_defaultPropertyPostprocess) {
						a_postprocess.push(function (o_fields) {
							var i_self = this;
							f_defaultPropertyPostprocess.call(i_self, i_self, i_self[s_key], o_fields, o_rule, o_xpd);
						});
					}

					var o_uiPropertyTypeRule = i_self.getUiPropertyTypeRule(s_key, s_context, o_rulesJs);

					if (o_uiPropertyTypeRule) {

						// Do the property specific preprocesses
						var f_uiPropertyPostprocess = o_uiPropertyTypeRule.postprocess;
						if (f_uiPropertyPostprocess) {
							a_postprocess.push(function (o_fields) {
								var i_self = this;
								f_uiPropertyPostprocess.call(i_self, i_self, i_self[s_key], o_fields, o_rule, o_xpd);
							});
						}
					}
				}
			});

			o_config.postprocess = a_postprocess;

			return o_config;
		},
		getPropertyConfig: function(name, o_xpdCfg, o_component, o_collection) {

			/*
			xpd_cfg = {
				type: "display",
				qwerty: 1,
				name: "John"
			}
			coll = {
				// recursive
				"apply": { "type": "varchar", "readonly": true }
			}
			property = {
				"alias": ["<% !readonly %>", "disallowcreate", "disallowCreate"],
				"apply": { "type": "varchar", "readonly": true }
			}
			*/

			if (!o_collection) {
				console.warn("no property config found for the property: \"", name, "\"", "in the  /portfolio/app/ux/UIRules.js file");
				return false;
			}

			var o_property = o_collection.properties[name];

			var o_apply = o_property.apply;

			if (o_apply) {
				ip2.extend(o_component, ip2.map(o_apply, function(val) {
					if (ip2.isEvalTpl(val)) {
						return ip2.evalTpl(val, o_xpdCfg);
					}
					else {
						return val;
					}
				}));
			}

			if (o_component[name] == null) {

				o_component[name] = o_xpdCfg[name];

				var a_alias = o_property.alias;

				if (a_alias && o_component[name] == null) {

					o_component[name] = ip2.first(a_alias, function(val) {

						if (ip2.isEvalTpl(val)) {
							return ip2.evalTpl(val, o_xpdCfg);
						}
						else {
							return o_xpdCfg[val];
						}
					});
				}

				var s_dftValue = o_property.value;

				if (o_component[name] == null) {
					o_component[name] = ip2.isEvalTpl(s_dftValue) ? ip2.evalTpl(s_dftValue) : s_dftValue;
				}
			}

			o_apply = o_collection.apply;

			if (o_apply) {
				ip2.extend(o_component, ip2.map(o_apply, function(val) {
					if (ip2.isEvalTpl(val)) {
						return ip2.evalTpl(val, o_xpdCfg);
					}
					else {
						return val;
					}
				}));
			}

			if (o_component[name] == null) {
				delete o_component[name];
			}

			return true;
		},
		getUiRule: function (s_type, o_rulesXpd) {

			var o_found = null;

			Ext.Object.each(o_rulesXpd.collections, function (s_key, o_rule) {
				if (o_rule.name == s_type) {
					o_found = o_rule;
					return false;
				}
			});

			return o_found;
		},
		getUiTypeRule: function (s_type, s_context, o_rulesJs) {

			var o_found = null;

			Ext.Object.each(o_rulesJs.uitype.collections, function (s_key, o_rule) {
				if (s_key == s_type) {
					o_found = o_rule;
					return;
				}
			});

			return o_found ? o_found[s_context] : null;
		},
		getUiPropertyTypeRule: function (s_type, s_context, o_rulesJs) {

			var o_found = null;

			Ext.Object.each(o_rulesJs.property.collections, function (s_key, o_rule) {
				if (s_key == s_type) {
					o_found = o_rule;
					return;
				}
			});

			return o_found ? o_found[s_context] : null;
		}
	},

	createUiComponent: function (o_settings) {

		var i_cmp = this;

		var o_rulesXpd = o_settings.rulesXpd;
		var o_rulesJs = o_settings.rulesJs;
		var s_context = o_settings.context;
		var o_config = o_settings.config;
		var initialValue = o_settings.initialValue;
		var o_defaults = o_settings.defaults;
		var o_components = {};
		var o_added = [];

		if (o_config.private) {
			return;
		}

		// Create component config against uirules
		var o_component = i_cmp.createUiConfig(o_config, o_rulesXpd, initialValue, o_defaults);

		// maybe our name has changed?
		var s_name = o_component.name;

		var o_cmpRules = i_cmp.getUiRule(o_component.type, o_rulesXpd);

		if (o_cmpRules.addFields) {
			ip2.each(o_cmpRules.addFields, function(o_field) {

				var o_fieldConfig = ip2.map(o_field, function(value, key) {

					return ip2.isEvalTpl(value) ? ip2.evalTpl(value, o_config) : value;
				});

				var o_value = i_cmp.createUiComponent({
					config: o_fieldConfig,
					rulesXpd: o_rulesXpd,
					context: s_context,
					rulesJs: o_rulesJs
				});
				Ext.Object.each(o_value, function(s_fieldname) {
					o_added[o_fieldConfig.name] = o_value[s_fieldname];
				});
			});
		}

		o_component._xpdConfig = o_config;

		// Process pre processes for component and properties
		o_component = Ext.ux.util.DMDUiType.preprocess(o_component, "form", o_rulesJs, o_rulesXpd);

		// Add post processes for component and properties
		o_component = Ext.ux.util.DMDUiType.postprocess(o_component, "form", o_rulesJs, o_rulesXpd);

		o_components[s_name] = o_component;

		Ext.Object.each(o_added, function(s_value) {
			o_components[s_value] = o_added[s_value];
		});

		return o_components;
	},

	createUiCollection: function(o_settings) {

		var i_cmp = this;
		var o_uiCollection = o_settings.collection;

		// TODO: This is only here to the preprocesser can do some language translation!!
		var o_xpd = o_settings.xpd;
		var o_rulesXpd = o_settings.rulesXpd;
		var o_rulesJs = o_settings.rulesJs;
		var s_context = o_settings.context;
		var o_value = o_settings.values || {};
		var o_defaults = o_settings.defaults;
		var s_environment = o_settings.environment;

		var o_uiComponentConfig = {};
		var o_uiComponents = {};

		if (!o_uiCollection) {
			var s_message = ip2.i18n({
				da: "Ingen \"collection\" ID",
				en: "No \"collection\" ID"
			});
			var s_description = ip2.i18n({
				da: "Data mangler en \"collection\" ID",
				en: "Data is missing a \"collection\" ID"
			});

			Ext.Error.raise({
				description: s_description,
				message: s_message,
				ignore: true
			});

			return false;
		}

		Ext.Object.each(o_uiCollection.properties, function itProps(s_name, o_uiConfig) {

			if (o_uiConfig.private) {
				return;
			}

			// Place name property if not present
			o_uiConfig.name = o_uiConfig.name || s_name;

			var initialValue = o_value[o_uiConfig.name];

			if (!Ext.isEmpty(initialValue) && Ext.isEmpty(o_uiConfig.value)) {
				initialValue = o_uiConfig.value;
			}

			// Find a possible environment layout object and apply as defaults only
			if (o_uiConfig.layout && o_uiConfig.layout[s_environment]) {
				ip2.defaults(o_uiConfig, o_uiConfig.layout[s_environment] || {});
			}

			// Create component config against uirules
			var o_component = i_cmp.createUiConfig(o_uiConfig, o_rulesXpd, initialValue, o_defaults);

			// maybe our name has changed?
			s_name = o_component.name;

			// and we need to look for an initial value again because maybe name has changed

			if (!o_uiConfig.extended) {
				if (o_value[s_name]) {
					o_component.value = o_value[s_name];
				}
			}
			else {
				var a_split = s_name.split("-");
				if (a_split.length > 0) {
					var value = o_value;
					Ext.Array.each(a_split, function(s_key) {
						if (Ext.isObject(value)) {
							value = value[s_key];
							if (ip2.isStringedObject(value)) {
								value = JSON.parse(value);
							}
						}
					});
					o_component.value = value;
				}
				else {
					o_component.value = o_value[s_name];
				}
			}

			var o_cmpRules = i_cmp.getUiRule(o_component.type, o_rulesXpd);

			if (o_cmpRules.addFields) {
				ip2.each(o_cmpRules.addFields, function(o_field) {
					if (o_component.i18n != undefined) {
						o_field.i18n = o_component.i18n;
					}
					itProps(null, ip2.map(o_field, function(value, key) {

						return ip2.isEvalTpl(value) ? ip2.evalTpl(value, o_uiConfig) : value;
					}));
				});
			}

			o_component._xpdConfig = o_uiConfig;

			// Process pre processes for component and properties
			o_component = Ext.ux.util.DMDUiType.preprocess(o_component, "form", o_rulesJs, o_rulesXpd, o_xpd);

			// Add post processes for component and properties
			o_component = Ext.ux.util.DMDUiType.postprocess(o_component, "form", o_rulesJs, o_rulesXpd, o_xpd);

			// Add isFormField if we have it in the config,
			if (o_uiConfig.hasOwnProperty("isFormField")) {
				o_component.isFormField = o_uiConfig.isFormField;
			}

			// Add this config to the preparation object
			o_uiComponentConfig[s_name] = o_component;
		});

		// Run through the compiled o_uiComponentConfig object and instanciate the components
		Ext.Object.each(o_uiComponentConfig, function (s_name, o_config) {
			o_uiComponents[s_name] = Ext.create(o_config.uiclass, o_config);
		});

		// Now run post gathered post processes
		Ext.Object.each(o_uiComponents, function (s_key, i_component) {

			var a_postprocess = i_component.postprocess;

			Ext.Array.each(a_postprocess, function (f_process) {
				f_process.call(i_component, o_uiComponents);
			});
		});

		return o_uiComponents;
	},

	createUiConfig: function (o_xpdConfig, o_rulesXpd, initialValue, o_defaults) {

		o_defaults = o_defaults || {};

		var i_cmp = this;
		var o_component = ip2.extend({}, o_defaults);

		var s_oldType = null;
		var s_newType = o_xpdConfig.type;
		var o_rules = null;

		// Hardcode the value on the component
		if (Ext.isEmpty(initialValue)) {
			o_component.value = initialValue;
		}

		while (s_oldType != s_newType) {

			o_rules = this.getUiRule(s_newType, o_rulesXpd);

			Ext.ux.util.DMDUiType.getPropertyConfig(
				"type",
				o_xpdConfig,
				o_component,
				o_rules
			);

			s_oldType = s_newType;
			s_newType = o_component.type;
		}

		if (!o_rules) {
			Ext.Error.raise({
				description: "Type not found",
				message: {
					message: "The type \"" + o_xpdConfig.type + "\" does not exist in /portfolio/app/ux/UIRules.js",
					src: JSON.stringify(o_xpdConfig, null, "\t")
				},
				debug: "js",
				ignore: true
			});
		}

		Ext.Object.each(o_rules.properties, function(key, value) {

			if (key == "type") {
				return;
			}

			Ext.ux.util.DMDUiType.getPropertyConfig(
				key,
				o_xpdConfig,
				o_component,
				o_rules
			);
		});

		return o_component;
	},

	getUiRule: function(type, o_rulesXpd) {

		return Ext.ux.util.DMDUiType.getUiRule(type, o_rulesXpd);
	}
});
