/**
 * @class Portfolio.entity.util.Handler
 * Utility class for handling UI Configurations used by the DMD and XPD
 */

Ext.define("Portfolio.entity.util.Handler", {

	alternateClassName: [
		"Ext.ux.util.DMDUiType"
	],

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

			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.call(i_self, 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.call(i_self, 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.call(i_self, o_config, s_key, value, o_xpd, o_data);

						// 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.call(i_self, o_config, s_key, value, o_xpd, o_data);
						}
					}
				}
			});

			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.hasOwnProperty(name)) {

				o_component[name] = o_xpdCfg[name];

				var a_alias = o_property.alias;

				if (a_alias && Ext.isEmpty(o_component[name])) {

					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 (Ext.isEmpty(o_component[name])) {
					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 (Ext.isEmpty(o_component[name])) {
				delete o_component[name];
			}

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

			var o_found = null;

			var o_collections = o_rulesXpd.collections;

			var a_values = Ext.Object.getValues(o_collections);

			o_found = Ext.Array.findBy(a_values, function(o_value) {
				return o_value.name == s_type;
			});

			if (!o_found) {

				o_found = Ext.Array.findBy(a_values, function (o_value) {
					return o_value.name == "varchar";
				});
			}

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

			var o_found = o_rulesJs.uitype.collections[s_type];

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

			var o_found = o_rulesJs.property.collections[s_type];

			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_data = o_settings.data || {};

		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);

		Ext.Object.each(o_component, function(s_key, value) {

			if (Ext.isEmpty(value)) {
				delete o_component[s_key];
			}
		});

		// 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_key, o_field) {
					o_added[s_key] = o_field;
				});
			});
		}

		o_component._xpdConfig = o_config;

		// Process pre processes for component and properties
		o_component = Portfolio.entity.util.Handler.preprocess(o_component, "form", o_rulesJs, o_rulesXpd, null, o_data);

		// Add post processes for component and properties
		o_component = Portfolio.entity.util.Handler.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"
			});

			Ext.Logger.info(s_message);

			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 = Portfolio.entity.util.Handler.preprocess(o_component, "form", o_rulesJs, o_rulesXpd, o_xpd, o_value);

			// Add post processes for component and properties
			o_component = Portfolio.entity.util.Handler.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);

			Portfolio.entity.util.Handler.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;
			}

			Portfolio.entity.util.Handler.getPropertyConfig(
				key,
				o_xpdConfig,
				o_component,
				o_rules
			);
		});

		return o_component;
	},

	getUiRule: function(type, o_rulesXpd) {

		return Portfolio.entity.util.Handler.getUiRule(type, o_rulesXpd);
	},

	getFormFields: function(o_config) {

		var i_self = this;
		var a_fields = [];
		var a_xpdFields = [];
		var a_output = [];
		var a_items = [];

		var o_data = o_config.data || {};

		var s_dmd = o_config.dmd;
		var s_entity = o_config.entity;
		var s_view = o_config.view;

		var o_dmd = ip2.resource.get(s_dmd);

		var o_uirules = ip2.uirules;
		var o_uiTypeRules = ip2.loadUiTypeRules();
		var o_uiTypes = ip2.getUiTypes();

		var o_model = o_dmd.model;

		var a_entities = o_model.entities;

		var o_entity = Ext.Array.findBy(a_entities, function(o_entity) {
			return o_entity.name == s_entity;
		});

		if (o_entity) {

			var o_ui = o_entity.ui;
			var o_view = o_ui.forms[s_view] || o_ui.forms.portfolio;

			var a_columns = o_entity.columns || [];

			if (o_view.items) {
				a_items = Ext.Array.slice(o_view.items);
			}

			var a_components = [];

			Ext.Array.each(a_items, function(o_field) {

				var o_column = Ext.Array.findBy(a_columns, function(o_config) {
					return o_config.name == o_field.name;
				});

				var o_fieldClone = Ext.clone(o_field);
				var o_columnClone = o_column ? Ext.clone(o_column) : {};

				var o_result = Ext.apply(o_columnClone, o_fieldClone);

				if (o_result.uiType) {
					o_result.uitype = o_result.uiType;
					delete o_result.uiType;
				}
				if (o_result.uitype) {
					var o_uiType = o_uiTypes[o_result.uitype];
					if (o_uiType.form && o_uiType.form.type) {
						var o_type = o_uiTypes[o_result.type];
						if (o_type) {
							o_result.type = o_uiType.form.type;
						}
					}
					delete o_result.uitype;
				}

				if (!o_result.type) {
					o_result.type = "varchar";
				}

				if (o_result.hasOwnProperty("title")) {
					if (!o_result.hasOwnProperty("fieldLabel")) {
						o_result.fieldLabel = o_result.title;
					}
					else {
						o_result.title = o_result.fieldLabel;
					}
				}

				var o_collection;
				var b_found = false;
				var s_type = o_result.type;

				while (!b_found) {

					var o_current = o_uirules.collections[s_type] || {
						apply: {
							type: "varchar"
						}
					};

					if (o_current.apply && o_current.apply.type) {
						s_type = o_current.apply.type;
					}
					else if (o_current.apply && !o_current.apply.type) {
						b_found = true;
					}
					else {
						b_found = true;
					}
					if (b_found) {
						o_collection = o_current;
					}
				}

				o_result.type = s_type;

				var a_type = ip2.getFieldTypeDefinition(o_result.type);

				if (a_type.length > 0) {
					Ext.apply(o_result, {
						type: a_type[0]
					});
				}

				if (o_result.uiType) {
					delete o_result.uiType;
				}
				if (o_result.uitype) {
					delete o_result.uitype;
				}

				var o_components = i_self.createUiComponent({
					rulesXpd: o_uirules.flatten,
					rulesJs: o_uiTypeRules,
					context: "form",
					config: o_result
				});

				a_components.push(o_components);
			});

			Ext.Array.each(a_components, function(o_components) {

				Ext.Object.each(o_components, function(s_key) {

					var o_component = o_components[s_key];

					var o_xpdConfig = o_component._xpdConfig;

					if (o_component.hasOwnProperty("dmd")) {
						if (!o_component.dmd) {
							o_component = Ext.apply(Ext.clone(o_component), {
								dmd: s_dmd
							});
						}
					}

					Ext.Object.each(o_component, function(s_key, value) {

						if (Ext.isEmpty(value)) {
							delete o_component[s_key];
						}
					});

					if (o_component.hasOwnProperty("entity")) {
						if (!o_component.entity) {
							o_component = Ext.apply(Ext.clone(o_component), {
								entity: o_entity.name
							});
						}
					}

					if (o_xpdConfig.xpd) {

						var a_split = o_xpdConfig.xpd.split("/");

						var s_xpd = "";

						if (a_split.length == 1) {
							s_xpd = "xpd/" + a_split.join("") + s_xpd;
						}

						if (s_xpd.indexOf(".") < 0) {
							s_xpd = s_xpd + ".xpd";
						}

						var o_xpd = ip2.resource.get(s_xpd, true);

						var o_temp = {
							xpd: o_xpd,
							data: o_data
						};

						var a_result = i_self.getXpdDefinitions(o_temp);

						a_xpdFields = a_xpdFields.concat(a_result);

						o_component.value = Ext.encode({});

						Ext.apply(o_component, {
							type: "varchar",
							uiclass: "Ext.form.field.Text",
							xtype: "textfield",
							hidden: true,
							isDirty: function() {

								var i_field = this;

								var b_dirty = false;
								var s_value = i_field.getValue() || "{}";
								var s_originalValue = i_field.originalValue || "{}";
								var o_value = Ext.decode(s_value, true) || {};
								var o_original = Ext.decode(s_originalValue, true) || {};

								var n_value = Ext.Object.getKeys(o_value).length;
								var n_original = Ext.Object.getKeys(o_original).length;

								var b_sameCount = n_value == n_original;

								if (b_sameCount) {

									Ext.Object.each(o_value, function(s_key, value) {

										if (!o_original.hasOwnProperty(s_key)) {
											b_dirty = true;
											return false;
										}

										var originalvalue = o_original[s_key];

										var s_value = Ext.encode(value);
										var s_originalValue = Ext.encode(originalvalue);

										b_dirty = s_value !== s_originalValue;

										if (b_dirty) {
											return false;
										}
									});
								}

								return b_dirty;
							}
						});
					}

					try {
						var i_field = Ext.create(o_component.uiclass, o_component);
						a_fields.push(i_field);
					}
					catch (o_err) {
					}
				});
			});

			Ext.Array.each(a_xpdFields, function(i_field) {

				if (i_field.from) {

					var i_found = Ext.Array.findBy(a_fields, function(i_dmdField) {
						return i_dmdField.name == i_field.from.field;
					});

					if (i_found) {
						if (i_found._xpdConfig.groups) {
							i_field._xpdConfig.groups = i_field._xpdConfig.groups || [];
							i_found._xpdConfig.groups = i_found._xpdConfig.groups || [];
							i_field._xpdConfig.groups = i_field._xpdConfig.groups.concat(i_found.groups);
						}
					}
				}
			});

			var a_filtered = Ext.Array.filter(a_fields, function(i_field) {

				var i_found = Ext.Array.findBy(a_xpdFields, function(i_xpdfield) {
					return i_xpdfield.name == i_field.name;
				});

				return !i_found;
			});

			a_output = a_output.concat(a_xpdFields).concat(a_filtered);

			return a_output;
		}
	},

	getXpdDefinitions: function(o_field) {

		var i_self = this;
		var o_xpd = o_field.xpd;

		var o_data = o_field.data || {};
		var o_collection;
		var a_fields = [];
		if (o_data) {

			if (!o_data.hasOwnProperty(o_xpd.idProperty)) {

				var n_count = Ext.Object.getKeys(o_xpd.collections).length;

				if (n_count === 1) {
					o_data[o_xpd.idProperty] = o_xpd.collections[Ext.Object.getKeys(o_xpd.collections)[0]].id;
				}
			}

			o_data = Ext.ux.xpd.Handler.convertData(o_xpd, o_data);

			if (o_xpd.hasOwnProperty("cidMap")) {
				var o_cidMap = o_xpd.cidMap;

				Ext.Object.each(o_cidMap, function(s_collection, o_map) {
					Ext.Object.each(o_map, function(s_cid, n_id) {
						if (s_cid == o_data[o_xpd.idProperty]) {
							o_data[o_xpd.idProperty] = n_id;
						}
					});
				});
			}

			o_collection = o_xpd.collections[o_data[o_xpd.idProperty]];

			if (o_collection) {

				a_fields = Ext.ux.xpd.Handler.getFieldsWithValues(o_xpd, o_collection.id, o_data);
			}
		}

		return a_fields;
	}
});
