/**
 * Make table cell content editable.
 *
 * The `.uiRichtable()` method makes table cells editable, which are marked with the attribute data-ep-editable="true".
 *
 * The cells content must be wrapped in a HTML-Element as `span` or `div`. The `data-ep-attribute`-value
 * represents the name of the objects attribute on which the saveAction should be executed. The innerHTML will be
 * saved.
 *
 * If the edited values should be saved, the `Input` in the first cell in the following example must be
 * an `input`-element, containing the object id of the destinated object in the `value`-attribute.
 * First cell must have the class `Checkbox` and contain an `input` node of type `checkbox`. This `checkbox` will be
 * selected, once the surrounding table row is clicked.
 *  <td class="Checkbox"><input type="checkbox" value="12345" /></td>
 *
 * ### Examples
 * Apply editable feature to a table.
 *
 * JavaScript:
 *
 *     ep('#contentTable').uiRichtable({
 *       'saveAction': 'Save',
 *       'region': jQuery.i18n.settings.region,
 *       'editIcon': true
 *     });
 *
 * HTML:
 *
 *     <table id="contentTable" class="ContentList">
 *       <tr>
 *         <td class="Checkbox">Input</td>
 *         <td class="OneQuarter" data-ep-editable="true"><span data-ep-attribute="Name">Text</span></td>
 *         <td class="OneQuarter AlignRight editable50" data-ep-editable="true"><div data-ep-attribute="Price" data-ep-additional="CurrencyID">199,95 &euro;</div></td>
 *         <td class="OneQuarter AlignRight editable40" data-ep-editable="true"><div data-ep-attribute="StockLevel">0</div></td>
 *       </tr>
 *     </table>
 *
 *
 * @class jQuery.ui.uiRichtable
 * @extends jQuery.widget
 *
 * @uses ep
 * @uses jQuery.ui.widget
 * @uses jQuery.event.special.load
 * @uses ep.fn.contextOrientation
 * @uses ep.ui.textedit
 * @uses ep.ui.validate
 * @uses ep.ui.tooltip
 * @since 6.15.0
 */

/**
 * @cfg {String} saveAction Name of the SaveAction
 */

/**
 * @cfg {String} region region-string like de-DE
 */

/**
 * @cfg {String} currency currency-string representing the current currency
 */

/**
 * @cfg {Boolean} editIcon A boolean indication whether an edit icon should be shown.
 */

/**
 * @cfg {Boolean} alterIcon A boolean indicator whether the edit icon should always be shown on the right side (false) or depending by the horizontal alignment (true)
 */

/**
 * See `jQuery.ui.uiRichtable` for details.
 *
 * @param {Object} [options] A map of additional options pass to the method.
 * @param {String} saveAction Name of the SaveAction
 * @param {String} region region-string like de-DE
 * @param {String} currency currency-string representing the current currency
 * @param {Boolean/Integer} shrink false if no shrink required, otherwise shrink up to integer
 * @param {Boolean} editIcon A boolean indication whether an edit icon should be shown.
 * @param {Boolean} alterIcon A boolean indicator whether the edit icon should always be shown on the right side (false) or depending by the horizontal alignment (true)
 *
 * @method uiRichtable
 * @member jQuery
 *
 * @since 6.15.0
 */

/*
 * @copyright		© Copyright 2006-2012, epages GmbH, All Rights Reserved.
 *
 * @module			ep.ui.richtable
 */
define("ep/ui/richtable", [
	"jquery",
	"ep",
	"util/string",
	'$dict!ep/dict',

	"jquery/ui/widget",
	"jquery/event/special/load",
	"ep/ui/validate",
	"ep/ui/textedit",
	"ep/fn/contextorientation",
	"ep/ui/tooltip",
	"$ready!"
], function($, ep, str, dict){

	/*
	 * @dictionary      ep.dict
	 *
	 * @translation     {NOT_VALID}
	 */

		// helper methods
	var getRow = function( elem ){
			return elem = elem.is(":checkbox") ? elem.closest("tr") : elem;
		},

		getCheckbox = function( elem ){
			return elem = elem.is(":checkbox") ? elem : elem.find("> td:first-child input[type=checkbox]");
		},

		toggleRow = function( elem, bool ){
			var checkbox = getCheckbox(elem),
				row = getRow(elem);

			row.toggleClass("RowSelected",  bool===undefined ? checkbox.is(":checked") : bool);
		},

		toggleCheckbox = function( elem, bool ){
			var checkbox = getCheckbox(elem);

			if (bool===undefined || bool !== checkbox.is(":checked")) {
				checkbox.trigger({
						type: "click",
						originalTarget: checkbox[0]
					});
			}
		},

		translations = {
			NOT_VALID: dict.translate('NOT_VALID')
		},	// exclude from toggle row selections

		unity = (ep.config.unity !== undefined && ep.config.unity === true) ? true : false;

	$.widget("ui.uiRichtable", {
		STR_EDIT_ICON: '<i class="ep-richtable-edit-icon ' + ((unity) ? 'fa fa-lg fa-pencil' : 'Sprite ico_s_edit') + '"></i>',

		unity: unity,

		options: {
		//	grossPrice:	"c2",						// format for net/gross prices
			editIcon: 	true,						// indicator if a edit icon should be shown
			alterIcon: 	false,						// flag if the edit icon should be shown always right (false) or depending by align (true)
			region:		$.i18n.settings.region,		// region string
			currency:	$.i18n.settings.currency,	// currency acronym
			saveAction: "Save",						// save method
			shrink: 	false,						// shrink headers with class 'ep-richtable-shrink-header' to provided min-length in this option
			noselect: [								// Selectors to prevent row selection
				"a", 									// Links and child elements
				"a *",
				":input",								// Input elements
				".ep-uiInput-custom",					// Custom UI input elements
				".epEditWrapper", 						// Editable elements and editable elements
				".epEditWrapper *"
			]
		},

		_create: function(){
			var self = this,
				o = self.options,
				$shrinkElem,
				shrinkParentWidth,
				titleString,
				shrinkVar;

			if(self.element[0]===undefined){
				return;
			}

			// get the currency format (default "c2": two decimal places)
			o.grossPrice = (o.grossPrice===undefined)? "c2" : o.grossPrice;

			self.element
				.addClass("RowSelection ep-uirichtable")
				// delegate checkbox for select all
				.on("change.uiRichtable", "thead > tr > *:first-child input[type=checkbox]", function(){
					var isChecked = $(this).is(":checked");

					self.element
						.find("> tbody > tr > td:first-child input[type=checkbox]")
						.each(function(){
							toggleCheckbox( $(this), isChecked );
						});
				})
				// delegate checkbox for row selection
				.on("change.uiRichtable", "tbody > tr > td:first-child input[type=checkbox]", function () {
					toggleRow($(this));
				})
				// delegate cell click
				.on("click.uiRichtable", "tbody > tr > td", function (event) {
					var target = $(event.originalTarget),
						cell = $(this),
						row = cell.parent();

					// prevent row selection if target is an image
					if(/\b(select)\b/.test(target[0].tagName.toLowerCase())){
						return;
					}

					// Inline editing
					if (cell.data("epEditable")) {
						self._handleEditable(row, cell, target);
					}
					// Check selectbox
					else if (target.length && !target.is(o.noselect.join(", "))) {
						toggleCheckbox(row.find("> td:first-child input[type=checkbox]"));
					}
				})
				// delegate mouseenter editable cell for initalize
				.on("mouseenter", "tbody > tr > td[data-ep-editable]:not(.RowSelectionInputCell)", function(){
					var o = self.options,
						cell = $(this).addClass("RowSelectionInputCell"),
						wrapper = cell.find("*[data-ep-attribute]");

					if( wrapper.length > 0 ){

						wrapper.addClass("epEditWrapper ClickIcon");

						// if an edit icon should be shown
						if( o.editIcon ){
							if( !o.alterIcon || cell.css("text-align") != "right" ){
								wrapper
									.append(self.STR_EDIT_ICON)
									.data("epTextAlign", "left");

								if( cell.css("text-align") == "right" ){
									cell.addClass("ep-Textedit-right");
								}
							}
							else{
								wrapper
									.prepend(self.STR_EDIT_ICON)
									.data("epTextAlign", "right");

								cell.addClass("ep-Textedit-right");
							}
						}

						switch( wrapper.data("epAttribute").toLowerCase() ){
							case "name":
								wrapper.css("display", "inline-block");
								break;
						}
					}
				});

				// register event to handle select fields in richtables (unity only)
				if (self.unity) {
					self.element
						// register handler for select fields
						.on("change.uiRichtable", "tbody > tr > td select.ep-richtable-select", function () {
							// $(this).data('epCorresponding'): array of objects
							// object: {
							//    name: attribute name of Object,
							//    selector: DOM element containing the value for the property 'name',
							//    value: method to get the value (html, text, val) or fixed value (number, string)
							// }

							var $this = $(this),
								row = $this.closest('tr'),
								selectValue = $this.val(),
								data = {},
								element,
								value;

							// if another element needs to be updated by the new select box value
							if ($this.data('epUpdate')) {
								$($this.data('epUpdate').selector).data($this.data('epUpdate').attribute, $this.val());
							}

							// prepare xhr object only if valid data exist
							if (selectValue) {

								// value from select field
								data[$this.attr('name')] = selectValue;

								// loop over corresponding elements
								$.each($this.data('epCorresponding'), function (index, item) {
									element = $(item.selector);
									if (element && element[item.value]) {
										value = (typeof element[item.value] === "function") ? element[item.value]() : item.value;

										if (value || value === 0) {
											data[item.name] = value;
										}
									}
								});

								// Ajax parameters
								data.ChangeAction = self.options.saveAction;
								data.ObjectID = getCheckbox(row).data('id') ? getCheckbox(row).data('id') : getCheckbox(row).val();
								data.ViewAction = 'JSONViewResponse';

								self._send(data);
							}
						})
				}

				// add handling to adjust column sizes to their content
				if (self.unity && !isNaN(o.shrink)) {
					$("thead > tr .ep-richtable-shrink-header").each(function(index, elem) {
						$shrinkElem = $(elem);
						$shrinkElem.hide();
						shrinkParentWidth = $shrinkElem.closest('td')[0].getBoundingClientRect().width;
						$shrinkElem.show();
						titleString = $shrinkElem.text();
						shrinkVar = titleString.length - 1;

						while ($shrinkElem.outerWidth() >= shrinkParentWidth && shrinkVar >= o.shrink) {
							$shrinkElem.text(str.shrink(titleString,shrinkVar));
							shrinkVar--;
						}

						if ($shrinkElem.attr('title') === undefined) {
							$shrinkElem.attr('title', titleString);
						}
					});
				}
		},

		_handleEditable: function( row, cell, target ){
			var self = this,
				o = self.options,
				wrapper = ep(".epEditWrapper", cell),	// element which should be editable
				oValidate,								// object/hash for the uiValidate widget
				oTextEditOptions;						// object/hash for the uiTextedit widget

			// if the element is not editable yet
			if( wrapper.data('epUiTextedit') === undefined ){

				// mark that the element is just created
				wrapper.prop("justCreated", true);

				switch( wrapper.data("epAttribute") ){
					case "Name":						// value of the data-ep-attribute correlate to the Save attribute
						oValidate = {
							maxlength: 100
						};
						break;
					case "ManufacturerPrice":
					case "Price":						// value of the data-ep-attribute correlate to the Save attribute
						oValidate = {
							type: "number",
							format: o.grossPrice,
							region: o.region,
							currency: o.currency,
							min: -2147483648,
							max: 2147483647,
							needFormat: true
						};
						break;
					case "StockLevel":					// value of the data-ep-attribute correlate to the Save attribute
						oValidate = {
							type: "number",
							format: "n2",
							region: o.region,
							min: -2147483648,
							max: 2147483647
						};
						break;
					case "Weight":
						oValidate = {
							type: "number",
							format: "n2",
							region: o.region,
							min: 0,
							max: 2147483647
						};
						break;
					default:
						oValidate = {};
						break;
				}

				oValidate.valid = false;

				// object/hash for the uiTextedit widget
				oTextEditOptions = {
					action		: self.options.saveAction, 						// SaveAction "Save"
					objectId	: getCheckbox(row).data('id') ? getCheckbox(row).data('id') : getCheckbox(row).val(),						// objectId
					name		: wrapper.data("epAttribute"),					// name/value pair (from data-ep-attribute)
					stripHTML	: true,
					validate	: oValidate,
					preventDefaultOnEnter : true,

					// callback called before element becomes editable
					onBeforeOpen: function(input){
						var icon = wrapper.find(".ep-richtable-edit-icon"),
							iconWidth;

						// remove edit icon if necessary
						if(self.options.editIcon){
							iconWidth = icon.outerWidth() +  parseInt(icon.css('margin-left'));
							ep(".ep-richtable-edit-icon", wrapper).remove();
						}
						// show element
						input.show();
						input.attr("disabled", false);

						if(self.options.editIcon && wrapper.parent().hasClass('ep-Textedit-right')){
							input.css('margin-right', iconWidth + 'px');
						}

						wrapper.css("width","98%");
						cell.find("*.hideOnEditable").hide();
					},

					onAfterOpen: function(input){
						// if element was just created
						if( wrapper.prop("justCreated") ){
							wrapper.prop("justCreated", false);
							// prevent an invalid message if input is empty
							if(input.val().length==0){
								input[0].formInvalid = false;
								input.blur();
								wrapper.trigger("click.uiTextedit");
								//input.focus();
							}
						}
					},

					// Called before data gets written to *wrapper*.
					formatOnWriteBack: function(input, evt) {
						// Format input value.
						var value = (oValidate.needFormat===true && oValidate.type=="number" && !isNaN( $.i18n.parseNumber(input.val()))) ?
								$.i18n.formatNumber($.i18n.parseNumber(input.val()), oValidate.format, {currency: self.options.currency, region: self.options.region}) :
								input.val();

						input.val(value);

						if ((typeof evt!="undefined") && evt.keyCode==27) {
						// On "ESC" *onAfterClose* will not be called.
							wrapper.hide();
							var invalid = input.is(":input:invalid");
							// short delay for webkit-engines to prevent default action
							setTimeout(function(){
								self._setContent(input, wrapper, true);
								if (!invalid) {
									wrapper.show();
									input.hide();
								}
								else {
									wrapper.hide();
									input.show();
								}
								wrapper.css("width","");
								cell.find("*.hideOnEditable").show();
							},10);
						}
					},

					// Called after element becomes uneditable and data has been saved to server.
					onAfterClose: function(input){
						self._setContent(input, wrapper, true);

						// If HasSubOwnPrices has just been set,
						// update the DOM and the corresponding server action.
						var ownpriceWasSet = wrapper.data("epOwnprice");
//#JSCOVERAGE_IF false
						if (ownpriceWasSet !== undefined) {
							// Update style and title of *wrapper* enclosing *cell*.
							cell.removeClass("InheritedValue");
							cell.attr("title", wrapper.data("epOwnpriceTitle"));

							// Update *additional* option of *uiTextedit*.
							var additional = wrapper.uiTextedit("option","additional");
							delete additional[ownpriceWasSet];
							wrapper.uiTextedit("option","additional",additional);

							// Update DOM of *wrapper*.
							wrapper
								.removeData(["epOwnprice","epOwnpriceTitle"])
								.removeAttr("data-ep-ownprice")
								.removeAttr("data-ep-ownprice-title");
						}
//#JSCOVERAGE_ENDIF
						wrapper.css("width","");
						cell.find("*.hideOnEditable").show();
					}
				};

				// if additional parameters are needed for the SaveAction
				if(wrapper.data("epAdditional")!==undefined){
					oTextEditOptions.additional = {};
					// e.g. oTextEditOptions.additional["CurrencyID"] = "EUR"
					oTextEditOptions.additional[wrapper.data("epAdditional")] = self.options.currency;
				}

				// if additional parameters are needed for the SaveAction
				if(wrapper.data("epOwnprice")!==undefined){
					oTextEditOptions.additional = (oTextEditOptions.additional===undefined)? {} : oTextEditOptions.additional;
					// e.g. oTextEditOptions.additional["HasSubOwnPrices"] = 1
					oTextEditOptions.additional[wrapper.data("epOwnprice")] = 1;
				}

				// if additional parameters are needed for the SaveAction
				if(wrapper.data("epWeightunit")!==undefined){
					if (wrapper.data("epWeightunit")) {
						oTextEditOptions.additional = (oTextEditOptions.additional===undefined)? {} : oTextEditOptions.additional;
						// e.g. oTextEditOptions.additional["HasSubOwnPrices"] = 1
						oTextEditOptions.additional['WeightUnit'] = wrapper.data("epWeightunit");
					} else {
						self._handleTooltip({
							context: wrapper,
							interactiveElement: cell,
							message: translations.NOT_VALID
						});

						return;
					}
				}

				// build the uiTextedit widget
				wrapper.uiTextedit(oTextEditOptions);
			}

			// trigger click event to show the editable element
			wrapper.uiTextedit("open");
		},

		_setContent: function( input, wrapper, disabled ){
			var self = this,
				value = input.val();

			input
				.attr("disabled", disabled)
				.prop('defaultValue', value)
				.trigger("change");

			// if an edit icons should be shown
			if( self.options.editIcon ){
				if( !self.options.alterIcon || wrapper.data("epTextAlign") != "right" ){
					wrapper.append(self.STR_EDIT_ICON);
				}
				else{
					wrapper.prepend(self.STR_EDIT_ICON);
				}
			}
		},

		// creates a tooltip
		_initTooltip: function (options) {
			var context = options.context;

			this.tooltip = $('<div>')
				.uiTooltip({
					interactive: true,
					event: 'hover',
					// offsetAdjust: [3, - 2],
					hideDelay: 1400,
					context: context,
					orientation: 'bottom right',
					hideDelay: 200,
					oldhandling: true
				});
		},

		// handle tooltips
		_handleTooltip: function (options) {
			var self = this,
				context = options.context;

			// create a tooltip if necessary
			if (self.tooltip === undefined) {
				self._initTooltip({
					context: context
				});
			}

			// update tooltip options
			self.tooltip.uiTooltip('option', 'context', context);
			self.tooltip.html(options.message);
			self.tooltip.uiTooltip('show');

			options.interactiveElement.one('mouseleave', function () {
				self.tooltip.uiTooltip('hide');
			});
		},

		_send: function (data) {
			var self = this,
				o = self.options;

			ep.ajax({
				type: 'post',
				dataType: 'json',
				data: data
			});
		}
	});

	return ep;

});