/**
 */
Ext.define("Terrasoft.controls.InlineTextEdit", {
	alternateClassName: "Terrasoft.InlineTextEdit",

	extend: "Terrasoft.BaseEdit",

	//region Properties: Protected

	/**
  * CKEditor instance.
  * @protected
  * @type {CKEDITOR.editor}
  */
	editor: null,

	/**
  * Css-class for control when validation is failed.
  * @protected
  * @type {String}
  */
	errorClass: "inline-text-edit-error",

	/**
  * Name of css-class for placeholder.
  * @protected
  * @type {String}
  */
	placeholderClassName: "inline-text-edit-placeholder",

	/*
 * @inheritdoc Terrasoft.controls.component#styles
 * @override
 */
	styles: {
		wrapStyles: null,
		elStyles: null,
		validationStyle: null
	},

	/*
  * @inheritdoc Terrasoft.controls.component#classes
  * @override
  */
	classes: {
		wrapClasses: ["inline-text-edit-wrap"],
		elClasses: ["inline-text-edit-el"],
		validationClasses: ["inline-text-edit-validation"]
	},

	/**
  * Template of control.
  * @protected
  * @override
  * @type {String[]}
  */
	tpl: [
	/*jshint quotmark:false */
	'<div id="{id}-inline-text-edit-wrap" style="{wrapStyles}" class="{wrapClasses}"' + ' data-placeholder="{placeholder}">', '<div tabindex="0" id="{id}-inline-text-edit-el" style="{elStyles}" class="{elClasses}" ' + 'contenteditable = "{canEdit}">', '{value}', '</div>', '<span id="{id}-validation" class="{validationClasses}" style="{validationStyle}">', '{validationText}', '</span>', '</div>'
	/*jshint quotmark:double */
	],

	/**
  * Selected text.
  * @protected
  * @override
  * @type {String[]}
  */
	selectedText: null,

	/**
  * CKEditor config that enables all features (data will not be filtered).
  * @protected
  * @type {Object}
  */
	ckeditorDefaultConfig: { allowedContent: true },

	//endregion

	//region Methods: Protected

	/**
  * @inheritdoc Terrasoft.BaseEdit#init
  * @override
  */
	init: function () {
		this.callParent(arguments);
		this.addEvents(
		/**
   * @event macrobuttonclicked
   * Calls when macros button is clicked.
   */
		"macrobuttonclicked",
		/**
   * @event selectedtextchanged
   * Calls when selected text is changed.
   */
		"selectedtextchanged",
		/**
   * @event selectedtextchanged
   * @deprecated Deprecated event because event name contains Cyrillic character.
   * Calls when selected text is changed.
   */
		"selectedtextсhanged");
	},

	/**
  * @inheritdoc Terrasoft.BaseEdit#onEnterKeyPressed
  * @override
  */
	onEnterKeyPressed: Terrasoft.emptyFn,

	/**
  * Handles selected text change.
  * @protected
  */
	onSelectionChange: function () {
		var selection = this.editor.getSelection();
		var selectedText = selection.getSelectedText();
		this.updateSelectedText(selectedText);
	},

	/**
  * Ckeditor contentDom event handler. Subscribes editor click and keyup events.
  * @protected
  */
	onContentDom: function () {
		var el = this.el;
		el.on("click", this.onSelectionChange, this);
		el.on("keyup", this.onSelectionChange, this);
	},

	/**
  * Handles macros button click.
  * @protected
  * @param {Object} event Event info object.
  */
	onMacroButtonClicked: function (event) {
		this.fireEvent("macrobuttonclicked", this, event.data);
	},

	/**
  * Updates selected text.
  * @protected
  * @param {String} selectedText Selected text.
  */
	updateSelectedText: function (selectedText) {
		if (selectedText !== this.selectedText) {
			this.selectedText = selectedText;
			this.fireEvent("selectedtextсhanged", this.selectedText, this);
			this.fireEvent("selectedtextchanged", this.selectedText, this);
		}
	},

	/**
  * Initializes an instance of CKEditor.
  * @protected
  */
	initInlineEditor: function () {
		if (this.el) {
			var editor = this.editor = CKEDITOR.inline(this.el.id, this.ckeditorDefaultConfig);
			editor.on("contentDom", this.onContentDom, this);
			this.initEditor();
			this.initExtraPluginsToolbar();
		}
	},

	/**
  * Inits extra plugin toolbar items.
  * @protected
  */
	initExtraPluginsToolbar: function () {
		this.createButton("bpmonlinemacros", this.onMacroButtonClicked);
	},

	/**
  * Creates additional button for ckeditor.
  * @protected
  * @param {String} pluginName Ckeditor plugin name.
  * @param {Function} onClick New button click event handler.
  * @param {Array} [items=null] Ckeditor items.
  */
	createButton: function (pluginName, onClick, items) {
		var clickEvent = pluginName + "click";
		items = items || [pluginName];
		this.editor.on(clickEvent, onClick, this);
		var config = this.editor.config;
		config.toolbar.push({
			name: pluginName,
			items: items
		});
		config.toolbarGroups.push({
			name: pluginName
		});
		config.extraPlugins += "," + pluginName;
	},

	/**
  * Inits ckeditor.
  * @private
  */
	initEditor: function () {
		var editorConfig = this.editor.config;
		editorConfig.toolbar = [];
		editorConfig.toolbarGroups = [];
		editorConfig.extraPlugins = "";
	},

	/**
  * Destroys an instance of CKEditor.
  * @protected
  */
	destroyInlineEditor: function () {
		if (this.editor) {
			if (this.editor.loaded) {
				this.editor.destroy();
			} else {
				var editor = CKEDITOR.instances[this.editor.name];
				editor.on("loaded", function () {
					this.destroy();
				}, editor);
			}
			this.editor = null;
		}
	},

	/**
  * @inheritdoc Terrasoft.BaseEdit#getTplData
  * @protected
  * @override
  */
	getTplData: function () {
		var tplData = this.callParent(arguments);
		var placeholder = Terrasoft.encodeHtml(this.placeholder);
		Ext.apply(tplData, {
			canEdit: !this.readonly,
			value: this.value,
			placeholder: placeholder
		});
		if (Ext.isEmpty(this.value)) {
			tplData.wrapClasses = [this.placeholderClassName];
		}
		if (!this.validationInfo.isValid) {
			tplData.wrapClasses.push(this.errorClass);
		}
		return tplData;
	},

	/**
  * @inheritdoc Terrasoft.BaseEdit#combineSelectors
  * @protected
  * @override
  */
	combineSelectors: function () {
		return {
			wrapEl: "#" + this.id + "-inline-text-edit-wrap",
			el: "#" + this.id + "-inline-text-edit-el",
			validationEl: "#" + this.id + "-validation"
		};
	},

	/**
  * Initializes a subscribtion to the DOM events.
  * @override
  * @protected
  */
	initDomEvents: function () {
		this.callParent(arguments);
		if (this.el) {
			this.el.on({
				"focus": {
					fn: this.onFocus,
					scope: this
				},
				"blur": {
					fn: this.onBlur,
					scope: this
				}
			});
		}
		var validationInfo = this.validationInfo;
		if (!validationInfo.isValid) {
			this.showValidationMessage(validationInfo.invalidMessage);
		}
	},

	/**
  * @inheritdoc Terrasoft.BaseEdit#clearDomListeners
  * @override
  */
	clearDomListeners: function () {
		this.callParent(arguments);
		if (this.el) {
			this.el.un("click", this.onSelectionChange, this);
			this.el.un("keyup", this.onSelectionChange, this);
		}
	},

	/**
  * @inheritdoc Terrasoft.BaseEdit#getBindConfig
  * @protected
  * @override
  */
	getBindConfig: function () {
		var bindConfig = this.callParent(arguments);
		Ext.apply(bindConfig, {
			selectedText: {
				changeEvent: "selectedtextchanged",
				deprecatedChangeEvent: "selectedtextсhanged",
				changeMethod: "setSelectedText"
			}
		});
		return bindConfig;
	},

	/**
  * Adds CSS class for control depending on isValid flag. If isValid is setted to true, class is added,
  * otherwise class is removed.
  * @protected
  */
	setMarkOut: function () {
		if (this.rendered && this.validationEl) {
			var validationMessage = "";
			if (!this.validationInfo.isValid) {
				this.wrapEl.addCls(this.errorClass);
				validationMessage = this.validationInfo.invalidMessage;
				this.validationEl.setStyle("width", "");
				var wrapWidth = this.wrapEl.getWidth();
				var validationElWidth = this.validationEl.getWidth();
				if (validationElWidth > wrapWidth) {
					this.validationEl.setWidth(wrapWidth);
				}
			} else {
				this.wrapEl.removeCls(this.errorClass);
			}
			this.showValidationMessage(validationMessage);
			this.validationEl.setVisible(!this.validationInfo.isValid);
		}
	},

	/**
  * Updates control's value.
  * @protected
  * @param {String} value Value of control.
  */
	updateValue: function (value) {
		var result = false;
		if (this.value !== value) {
			this.value = value;
			this.fireEvent("change", this);
			result = true;
		}
		return result;
	},

	/**
  * Checks if editor content is empty.
  * @protected
  * @return {Boolean} If editor content is empty returns true, otherwise false.
  */
	isEditorDataEmpty: function () {
		var editorData = this.editor.getData();
		return Ext.isEmpty(editorData);
	},

	/**
  * Sets placeholder visibility.
  * @protected
  * @param {Boolean } visible Indicates whether placeholder is visible.
  */
	setPlaceholderVisible: function (visible) {
		if (visible) {
			this.wrapEl.addCls(this.placeholderClassName);
		} else {
			this.wrapEl.removeCls(this.placeholderClassName);
		}
	},

	/**
  * Handles focus control event.
  * @protected
  */
	onFocus: function () {
		if (this.rendered && !this.focused) {
			if (this.isEditorDataEmpty()) {
				this.setPlaceholderVisible(false);
			}
			this.focused = true;
		}
	},

	/**
  * Handles blur control event.
  * When handles blur event updates control's value and sets placeholder visibility.
  * @protected
  */
	onBlur: function () {
		if (this.rendered) {
			this.focused = false;
			if (this.isEditorDataEmpty()) {
				this.setPlaceholderVisible(true);
			}
			var editorData = this.editor.getData();
			this.updateValue(editorData);
			this.fireEvent("blur", this);
		}
	},

	/**
  * @inheritdoc Terrasoft.Component#onAfterRender
  * @override
  */
	onAfterRender: function () {
		this.callParent(arguments);
		this.destroyInlineEditor();
		this.initInlineEditor();
	},

	/**
  * @inheritdoc Terrasoft.Component#onAfterRender
  * @override
  */
	onAfterReRender: function () {
		this.callParent(arguments);
		this.destroyInlineEditor();
		this.initInlineEditor();
	},

	/**
  * @inheritdoc Terrasoft.Component#onDestroy
  * @override
  */
	onDestroy: function () {
		this.destroyInlineEditor();
		this.callParent(arguments);
	},

	//endregion

	//region Methods: Public

	/**
  * Sets control's value.
  * If control is rendered calls reRender.
  * @param {String} value
  */
	setValue: function (value) {
		if (this.updateValue(value) && this.rendered) {
			this.reRender();
		}
	},

	/**
  * Sets readonly-mode.
  * @param {Boolean} readonly If readonly is setted to true, turns on readonly-mode, otherwise turns it off.
  */
	setReadonly: function (readonly) {
		if (this.readonly !== readonly) {
			this.readonly = readonly;
			if (this.rendered) {
				this.reRender();
			}
		}
	},

	/**
  * Sets placeholder's value.
  * @param {String} placeholder Text that is visible when editor content is empty.
  */
	setPlaceholder: function (placeholder) {
		if (this.placeholder !== placeholder) {
			this.placeholder = placeholder;
			if (this.rendered) {
				this.reRender();
			}
		}
	},

	/**
  * Inserts value to content.
  * @protected
  * @param {String} value Inserting value.
  */
	insertContent: function (value) {
		this.editor.insertText(value);
	},

	/**
  * Sets value of selected text.
  * @param {String} value Selected text.
  */
	setSelectedText: function (value) {
		if (value !== this.selectedText && this.editor) {
			var editor = this.editor;
			var selection = editor.getSelection();
			var selectedText = selection && selection.getSelectedText();
			if (!Ext.isEmpty(selectedText)) {
				var range = selection.getRanges()[0];
				range.deleteContents();
			}
			this.insertContent(value);
			this.updateSelectedText(value);
			var editorData = this.editor.getData();
			this.updateValue(editorData);
			var isDataEmpty = this.isEditorDataEmpty();
			this.setPlaceholderVisible(isDataEmpty);
		}
	}

	//endregion

});