Ext.ns('Zarafa.common.plugins');

/**
 * @class Zarafa.common.plugins.splitFieldLabeler
 * @extends Ext.util.Observable
 * @ptype zarafa.splitfieldlabeler
 *
 * This plugin functions similarly as the {@link Zarafa.common.plugins.FieldLabeler FieldLabeler}.
 * This plugin should be attached to an {@link Ext.Container container} or anything which
 * has an 'items' field containing components. And is therefor primarily intended for
 * splitting a label for more then one component.
 * On the {@link Ext.Container container} the fieldLabel must be set consisting
 * of a string which can be split into multiple substrings.
 * Each component should have the {@link Zarafa.common.plugins.FieldLabeler.labelSplitter labelSplitter}
 * field set to indicate which variable it represents in the fieldLabel string of the container.
 *
 * Before rendering this plugin will breakup the fieldLabel on the {@link Ext.Container container}
 * and adds {@link Ext.form.Label Label} components and the original {@link Ext.form.Field Fields}
 * into the items array of the container.
 */
Zarafa.common.plugins.SplitFieldLabeler = Ext.extend(Ext.util.Observable, {
	/**
	 * @cfg {Object} labelCfg The labelConfiguration which must be
	 * applied to all generated labels.
	 */
	labelCfg : {
		style: 'text-align: left; padding: 3px 3px 3px 3px'
	},
	/**
	 * @cfg {Object} firstLabelCfg The labelConfiguration which must be
	 * applied to the first generated label (if this is also the first
	 * displayed item inside the field!). This configuration applied after
	 * the {@link #labelCfg}.
	 */
	firstLabelCfg : {
		style: 'text-align: left; padding: 3px 3px 3px 0px'
	},
	/**
	 * @cfg {Object} lastLabelCfg The labelConfiguration which must be
	 * applied to the last generated label (if this is also the last
	 * displayed item inside the field!). This configuration applied after
	 * the {@link #labelCfg}.
	 */
	lastLabelCfg : {
		style: 'text-align: left; padding: 3px 3px 3px 3px'
	},

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config)
	{
		Ext.apply(this, config);
	},

	/**
	 * Plugin initializer for the SplitFieldLabeler.
	 * @param {Ext.Component} field The field for which the fieldlabeler is initialized
	 */
	init : function(field)
	{
		// This is not a container, or no label has been assigned.
		if (!Ext.isDefined(field.items) || !Ext.isDefined(field.fieldLabel) || field.hideLabel === true) {
			return;
		}

		var labelPieces = this.createLabelDistribution(field.items, field.fieldLabel);
		var labeledItems = this.applyLabelDistribution(field.items, labelPieces);

		this.applyLabelWidths(field.labelWidth, labeledItems);

		// Overwrite the items, be careful not to change the type
		// of the items object (it could be an array or MixedCollection).
		if (Array.isArray(field.items)) {
			field.items = labeledItems;
		} else {
			field.items.clear();
			field.items.addAll(labeledItems);
			// The innerCt has already been created at this point so the items 
			// need to be added to that Component as well
			field.innerCt.items.clear();
			field.innerCt.items.addAll(labeledItems);
		}

		// The main field must not display any labels
		field.hideLabel = true;
		delete field.fieldLabel;
	},

	/**
	 * Create a list of all sub-labels which can be constructed from the given label.
	 * This will return a list of all sub-labels, including the seperator string which
	 * indicates the correct position of an item.
	 *
	 * @param {Array/MixedCollection} items The items for which the label distribution
	 * is required.
	 * @param {String} label The label which must be distributed over the items.
	 * @return {Array} The array of labelstrings which must be applied to the components.
	 */
	createLabelDistribution : function(items, label)
	{
		var splitters = [];
		var generateSplitters = function(item) {   
			if (Ext.isDefined(item.labelSplitter)) {
				splitters.push(item.labelSplitter);
			}
		};

		// Items can be an Array or MixedCollection, depending
		// on the current state of the field for which we are generating
		// the labels.
		if (Array.isArray(items)) {
			Ext.each(items, generateSplitters, this);
		} else {
			items.each(generateSplitters, this);
		}

		return Zarafa.util.Translations.MultiSplitTranslation(label, splitters);
	},

	/**
	 * Apply the label distribution which was generated with the
	 * {@link #createLabelDistribution createLabelDistribution}
	 * function. This will construct an array of {@link Ext.Component Components} with the
	 * labels and components in the order of which they should be displayed.
	 *
	 * @param {Array/MixedCollection} items The items for which the labels must be applied.
	 * @param {Array} labels The array of labels which must be applied to the items.
	 * @return {Array} items The array of items with the labels applied, and in the
	 * order in which they must be shown.
	 */
	applyLabelDistribution : function(items, labels)
	{
		var labeledItems = [];

		for (var i = 0; i < labels.length; i++) {
			var foundItem = undefined;
			var label = labels[i];

			var findItem = function(item) {
				if (item.labelSplitter === labels[i]) {
					foundItem = item;
					return false;
				}
			};

			// Items can be an Array or MixedCollection, depending
			// on the current state of the field for which we are generating
			// the labels.
			if (Array.isArray(items)) {
				Ext.each(items, findItem, this);
			} else {
				items.each(findItem, this);
			}

			// If the label was recognized as labelSplitter,
			// we must insert the component itself, otherwise
			// generate the label.
			if (Ext.isDefined(foundItem)) {
				labeledItems.push(foundItem);
			} else {
				labeledItems.push(Ext.apply({}, this.labelCfg, {
					xtype: 'displayfield',
					value: label
				}));
			}
		}

		// Apply special configurations to the first and last items.
		if (labeledItems[0].xtype == 'displayfield') {
			Ext.apply(labeledItems[0], this.firstLabelCfg);
		}
		if (labeledItems[labeledItems.length - 1].xtype == 'displayfield') {
			Ext.apply(labeledItems[labeledItems.length - 1], this.lastLabelCfg);
		}

		for (var i = 0; i < labeledItems.length; i++) {
			labeledItems[i] = Ext.create(labeledItems[i]);
		}

		return labeledItems;
	},

	/**
	 * Calculate the {@link Ext.form.Label.width labelWidths} which
	 * must be applied to each individual component which doesn't have the
	 * {@link Ext.form.Label.width labelWidth} set explicitely.
	 *
	 * @param {Number} totalWidth The total width which can be used to
	 * for all the labels combined.
	 * @param {Ext.Component} labeledItems The components which contain the
	 * fieldLabels for which the lengths must be calculated.
	 */
	applyLabelWidths : function(totalWidth, labeledItems)
	{
		// No width configured, all labels have autoWidth
		if (!Ext.isNumber(totalWidth)) {
			return;
		}

		// The combined length of all labels
		var labelLength = 0;

		// Loop over all items to see what labels have been
		// applied, and if a width has already been specified. 
		for (var i = 0; i < labeledItems.length; i++) {
			var item = labeledItems[i];

			if (item.xtype == 'displayfield') {
				// If no width is configured, but a label is provided,
				// increase the labelLength to have this label be part
				// of the labelWidth calculations.
				// If a width was provided, we reduce the totalWidth
				// because we must only calculate the labelWidth for
				// the non-fixed labels.
				if ((!Ext.isDefined(item.width) || item.width === 0) && !Ext.isEmpty(item.text)) {
					labelLength += item.text.length;
				} else {
					totalWidth -= item.width;
				}
			}
		}

		// Calculate the labelWidth 
		for (var i = 0; i < labeledItems.length; i++) {
			var item = labeledItems[i];

			if (item.xtype == 'displayfield') {
				if ((!Ext.isDefined(item.width) || item.width === 0) && !Ext.isEmpty(item.text)) {
					item.width = Math.round(totalWidth * (item.text.length / labelLength));
				}
			}
		}
	}
});

Ext.preg('zarafa.splitfieldlabeler', Zarafa.common.plugins.SplitFieldLabeler);
