Ext.ns("Terrasoft.controls.MessageBoxEnums");

/**
 * @enum {string} Terrasoft.controls.MessageBoxEnums.Buttons
 * A collection of pre-configured buttons.
 */
Terrasoft.MessageBoxButtons = {
	/**
  * YES button
  * @type {Object}
  */
	YES: {
		className: "Terrasoft.Button",
		caption: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionYes,
		markerValue: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionYes,
		returnCode: "yes"
	},
	/**
  * NO button
  * @type {Object}
  */
	NO: {
		className: "Terrasoft.Button",
		caption: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionNo,
		markerValue: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionNo,
		returnCode: "no"
	},
	/**
  * CANCEL button
  * @type {Object}
  */
	CANCEL: {
		className: "Terrasoft.Button",
		caption: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionCancel,
		markerValue: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionCancel,
		returnCode: "cancel"
	},
	/**
  * CLOSE button
  * @type {Object}
  */
	CLOSE: {
		className: "Terrasoft.Button",
		caption: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionClose,
		markerValue: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionClose,
		returnCode: "close"
	},
	/**
  * OK button
  * @type {Object}
  */
	OK: {
		className: "Terrasoft.Button",
		caption: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionOk,
		markerValue: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionOk,
		returnCode: "ok"
	},
	/**
  * Save button
  * @type {Object}
  */
	SAVE: {
		className: "Terrasoft.Button",
		caption: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionSave,
		markerValue: Terrasoft.Resources.Controls.MessageBox.ButtonCaptionSave,
		returnCode: "save"
	}
};

/**
 * Alias for {@link Terrasoft.MessageBoxButtons}
 * @inheritdoc Terrasoft.MessageBoxButtons
 */
Terrasoft.controls.MessageBoxEnums.Buttons = Terrasoft.MessageBoxButtons;

/**
 * @enum {Object} Terrasoft.controls.MessageBoxEnums.Styles
 * Preconfigured styles of dialog boxes.
 */
Terrasoft.MessageBoxStyles = {
	RED: {
		borderStyle: "ts-messagebox-border-style-red",
		buttonStyle: "red"
	},
	BLUE: {
		borderStyle: "ts-messagebox-border-style-blue",
		buttonStyle: "blue"
	}
};

/**
 * Shortening for {@link Terrasoft.controls.MessageBoxEnums#Styles}
 * @member Terrasoft.controls.MessageBoxEnums
 * @enum Styles
 * @inheritdoc Terrasoft.controls.MessageBoxEnums#Styles
 */
Terrasoft.controls.MessageBoxEnums.Styles = Terrasoft.MessageBoxStyles;

/**
 * Класс для работы с диалоговым окном.
 *
 *    Ext.create("Terrasoft.MessageBox", {
 *		buttons: ["yes", "no", {
 *			className: "Terrasoft.Button",
 *			returnCode: "customButton",
 *			style: "green",
 *			caption: "myButton"
 *		}],
 *		defaultButton: 0,
 *		caption: "Пример с двумя кнопками по умолчанию и одной настраиваемой кнопкой"
 *	});
 */
Ext.define("Terrasoft.controls.MessageBox", {
	alternateClassName: "Terrasoft.MessageBox",
	singleton: true,
	extend: "Terrasoft.Container",

	/**
  * The place where the control will be displayed
  * @type {Object}
  */
	renderTo: null,

	/**
  * The default configuration used when updating the control.
  * @protected
  * @type {Object}
  */
	defaultConfig: {
		caption: "",
		buttons: null,
		defaultButton: null,
		callback: null,
		scope: null,
		style: Terrasoft.MessageBoxStyles.BLUE,
		visible: false,
		controlConfig: null,
		customWrapClass: null,
		handler: null
	},

	/**
  * Configuration classes for the control template.
  * @protected
  * @type {Object}
  */
	configClasses: {
		leftPositionClass: "ts-messagebox-center-left-position",
		centralPositionClass: "ts-messagebox-center-position",
		defaultButtonStyle: "t-btn-style-default",
		buttonFocus: "t-btn-focus"
	},

	/**
  * The method is called when the button or ESC key is pressed.
  * @protected
  * @param {String} returnCode The code of the pressed button
  */
	handler: null,

	/**
  * The context in which the {@link #handler handler} is executed.
  * @protected
  */
	scope: null,

	/**
  * @inheritdoc Terrasoft.Component#visible
  * @override
  */
	visible: false,

	/**
  * The title that will be shown in the control.
  * @type {String}
  */
	caption: "",

	/**
  * The default button number from the {@link Terrasoft.AbstractContainer.items items} array.
  * Numbering starts from zero.
  * @type {Number}
  */
	defaultButton: null,

	/**
  * The array of buttons for the control. For an example, see {@link Terrasoft.MessageBoxButtons}
  * @type {Array}
  */
	buttons: null,

	/**
  * Control Style
  * @type {Terrasoft.controls.MessageBoxEnums.Styles}
  */
	style: Terrasoft.MessageBoxStyles.BLUE,

	/**
  * Configuring custom controls
  * @type {Object}
  * Example:
  {
   link: {
    dataValueType: Terrasoft.DataValueType.TEXT,
    caption: "Text",
    value: "Text",
    renderTo: "custom-container"
   },
   checkbox: {
    dataValueType: Terrasoft.DataValueType.BOOLEAN,
    caption: "Logical",
    value: true,
    renderTo: "custom-container"
   },
   date: {
    dataValueType: Terrasoft.DataValueType.DATE,
    caption: "Date",
    value: new Date(Date.now()),
    renderTo: "custom-container"
   },
   memo: {
    dataValueType: Terrasoft.DataValueType.TEXT,
    caption: "Text",
    value: "Text",
    renderTo: "custom-container",
    customConfig: {
 	className: "Terrasoft.MemoEdit"
    }
   }
  }
  */
	controlConfig: null,

	/**
  * An array of custom controls
  * @type {Array}
  */
	controlArray: null,

	/**
  * Name of the container of custom contols
  * @type {String}
  */
	controlContainer: "-custom-control",

	/**
  * Name of custom wrap class.
  * @type {String}
  */
	customWrapClass: null,

	/**
  * Use Html content flag.
  * @type {Boolean}
  */
	useHtmlContent: false,

	/**
  * @inheritdoc Terrasoft.Component#init
  * @protected
  * @override
  */
	init: function () {
		this.callParent(arguments);
		this.addEvents(
		/**
   * @event
   * Event of changing the status of the dialog box.
   * Called when the button is pressed inside the control or the ESC key.
   * @param {String} returnCode keystroke code.
   * @param {Terrasoft.MessageBox} self link to the current control.
   * @param {Terrasoft.Button} this link to the button control.
   */
		"stateChanged");
		this.on("stateChanged", this.onStateChanged, this);
	},

	/**
  * The {@link #stateChanged stateChanged} event handler. If {@link #handler handler} is specified, it calls it.
  * @protected
  * @param  {String} returnCode The code of the pressed button
  */
	onStateChanged: function (returnCode) {
		var handler = this.handler;
		var scope = this.scope || this;
		this.updateCustomControl();
		if (handler) {
			var controlState = Terrasoft.deepClone(this.controlConfig);
			if (this.controlConfig) {
				this.controlConfig = null;
			}
			handler.call(scope, returnCode, controlState);
		}
	},

	/**
  * @inheritdoc Terrasoft.AbstractContainer#initItems
  * @override
  */
	initItems: function () {
		this.items = this.getMessageBoxItems();
		this.callParent(arguments);
		this.attachButtonsHandlers();
	},

	/**
  * @inheritdoc Terrasoft.AbstractContainer#add
  * Adds handlers to buttons.
  * @override
  */
	add: function () {
		this.callParent(arguments);
		this.attachButtonsHandlers();
	},

	/**
  * @inheritdoc Terrasoft.AbstractContainer#getTpl
  * @override
  */
	getTpl: function () {
		return [
		/*jshint quotmark:true */
		'<div id="{id}-cover" class="{coverClass}"></div>', '<div id="{id}-wrap" class="{boxClass}">', '<tpl if="!inputbox">', '<div id="{id}-caption" class="{captionClass}">', '{caption}', '</div>', '</tpl>', '<tpl if="inputbox">', '<tpl if="caption">', '<div id="{id}-caption" class="{inputboxCaptionClass}">', '{caption}', '</div>', '</tpl>', '<tpl if="!caption">', '<div id="{id}-caption" class="{inputboxNoCaptionClass}">', '</div>', '</tpl>', '<div id="{id}-inputbox">', '<div id="{id}-custom-control" class="{customControl}">', '</div>', '</div>', '</tpl>', '<tpl for="items">', '<@item>', '</tpl>', '</div>'
		/*jshint quotmark:double */
		];
	},

	/**
  * Returns an array of buttons.
  * If the string is encountered in the {@link Terrasoft.AbstractContainer # items items} array,
  * the string method looks for the button by name in the default settings.
  * Adds the default style for the button.
  * @protected
  * @return {Array}
  */
	getMessageBoxItems: function () {
		var items = this.buttons;
		if (!items) {
			items = [Terrasoft.MessageBoxButtons.CLOSE];
		}
		var messageBoxItems = [];
		var button;
		for (var i = 0, length = items.length; i < length; i++) {
			var item = items[i];
			if (Ext.isString(item)) {
				item = item.toUpperCase();
				button = Terrasoft.MessageBoxButtons[item];
				if (button !== undefined) {
					messageBoxItems.push(Ext.apply({}, button));
				}
			}
			if (Ext.isObject(item)) {
				messageBoxItems.push(item);
			}
		}
		var defaultButton = this.defaultButton;
		if (defaultButton !== null) {
			button = messageBoxItems[defaultButton];
			if (button !== undefined) {
				button.style = this.style.buttonStyle;
			}
		}
		var messageBoxItemsWithDefaultStyle = messageBoxItems.filter(function (value) {
			return !value.style || value.style === Terrasoft.controls.ButtonEnums.style.DEFAULT;
		});
		if (messageBoxItems.length > 0 && messageBoxItemsWithDefaultStyle.length === messageBoxItems.length) {
			messageBoxItems[0].style = Terrasoft.controls.ButtonEnums.style.BLUE;
		}
		return messageBoxItems;
	},

	/**
  * Initializes user controls
  * @protected
  */
	initCustomControl: function () {
		this.destroyCustomControl();
		this.wrapEl.on("click", function (event) {
			event.stopEvent();
		});
		var controlsConfig = Terrasoft.deepClone(this.controlConfig);
		if (!controlsConfig) {
			return null;
		}
		this.controlArray = [];
		Terrasoft.each(controlsConfig, this._initCustomControlItem, this);
	},

	/**
  * @private
  */
	_initCustomControlItem: function (controlConfigItem, key) {
		var controlLabel = {
			className: "Terrasoft.Label",
			caption: controlConfigItem.caption || "",
			classes: {
				labelClass: ["controlCaption"]
			},
			isRequired: !!controlConfigItem.isRequired
		};
		var descriptionLabel = {
			className: "Terrasoft.Label",
			caption: controlConfigItem.description || "",
			classes: {
				labelClass: ["controlDescription"]
			},
			visible: Boolean(controlConfigItem.description)
		};
		var controlConfig = Terrasoft.getControlConfigByDataValueType(controlConfigItem.dataValueType);
		controlConfig.markerValue = controlConfigItem.caption || "";
		if (controlConfigItem.customConfig) {
			Ext.apply(controlConfig, controlConfigItem.customConfig);
		}
		if (controlConfigItem.dataValueType !== Terrasoft.DataValueType.BOOLEAN) {
			controlConfig.value = controlConfigItem.value;
		} else {
			controlConfig.checked = controlConfigItem.value;
		}
		var renderTo = Ext.get(this.id + this.controlContainer);
		controlLabel.renderTo = controlConfig.renderTo = descriptionLabel.renderTo = renderTo;
		var item = {
			name: key,
			label: Ext.create(controlLabel.className, controlLabel),
			control: Ext.create(controlConfig.className, controlConfig),
			description: Ext.create(descriptionLabel.className, descriptionLabel)
		};
		var controlArray = this.controlArray;
		controlArray.push(item);
	},

	/**
  * @inheritdoc Terrasoft.Component#initDomEvents
  * @override
  */
	initDomEvents: function () {
		this.callParent(arguments);
		var document = Ext.getDoc();
		document.on("keydown", this.onKeyDown, this);
	},

	/**
  * Key press event handler.
  * @protected
  * @param  {Event} e DOM event
  */
	onKeyDown: function (e) {
		if (this.visible) {
			var key = e.getKey();
			if (key === e.ESC) {
				this.setVisible(false);
				var returnCode = Terrasoft.MessageBoxButtons.CANCEL.returnCode;
				this.fireEvent("stateChanged", returnCode, this);
				return;
			}
			if (!e.within(this.wrapEl)) {
				e.stopEvent();
			}
		}
	},

	/**
  * Adds the event handler {@link #onButtonClick onButtonClick} to the buttons in the control.
  * @protected
  */
	attachButtonsHandlers: function () {
		var items = this.items.items;
		for (var i = 0, length = items.length; i < length; i++) {
			items[i].on("click", this.onButtonClick(this));
		}
	},

	/**
  * The event handler {@link Terrasoft.Button # click click}.
  * Hides the control, calls the {@link #stateChanged stateChanged} event.
  * @protected
  * @param {Object} self link to the control {@link Terrasoft.MessageBox # dialog box}
  */
	onButtonClick: function (self) {
		return function () {
			var returnCode = this.returnCode;
			self.setVisible(false);
			self.fireEvent("stateChanged", returnCode, self, this);
		};
	},

	/**
  * Performs a re-initialization of buttons in the control
  */
	reConfigurateButtonItems: function () {
		var items = this.items;
		items.each(function (item) {
			item.destroy();
		}, this);
		items.clear();
		var buttons = this.getMessageBoxItems(this.buttons);
		this.add(buttons);
	},

	/**
  * Applies the configuration to the {@link #defaultConfig} control by default.
  */
	applyDefaultConfig: function () {
		Ext.apply(this, this.defaultConfig);
	},

	/**
  * @inheritdoc Terrasoft.Component#reRender
  * @override
  */
	reRender: function () {
		this.renderTo = Ext.getBody();
		this._removeCoverEl();
		this.callParent(arguments);
	},

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

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

	/**
  * Sets the css class to the control and passes the focus to the default button.
  * @protected
  */
	applyAfterRenderClasses: function () {
		var wrapEl = this.wrapEl;
		var height = wrapEl.getHeight();
		var configClasses = this.configClasses;
		wrapEl.setHeight(height);
		wrapEl.removeCls(configClasses.leftPositionClass);
		wrapEl.addCls(configClasses.centralPositionClass);
		var defaultButton = this.defaultButton;
		if (defaultButton !== null) {
			var item = this.items.items[defaultButton];
			if (item) {
				var defaultButtonWrapEl = item.selectors.wrapEl;
				var domWrapEl = Ext.query(defaultButtonWrapEl)[0];
				var buttonWrapEl = Ext.get(domWrapEl);
				buttonWrapEl.addCls(this.configClasses.buttonFocus);
				buttonWrapEl.focus();
			}
		}
	},

	/**
  * The method returns the parameter object of the control's rendering template.
  * @protected
  * @override
  * @return {Object}
  */
	getTplData: function () {
		var tplData = this.callParent(arguments);
		var messageBoxTplData = {
			caption: this.getMessageCaption()
		};
		this.selectors = this.getSelectors();
		Ext.apply(tplData, this.getClasses());
		tplData.inputbox = !!this.controlConfig;
		tplData.caption = !!this.caption;
		return Ext.apply(tplData, messageBoxTplData, {});
	},

	/**
  * Gets message caption.
  * @protected
  * @return {Object}
  */
	getMessageCaption: function () {
		return this.useHtmlContent ? this.caption : Terrasoft.encodeHtml(this.caption);
	},

	/**
  * Returns an array containing the css classes for the control template.
  * @protected
  * @return {Array}
  */
	getClasses: function () {
		var classes = {
			coverClass: ["ts-messagebox-cover"],
			captionClass: ["ts-messagebox-caption"],
			inputboxCaptionClass: ["ts-inputbox-caption"],
			inputboxNoCaptionClass: ["ts-inputbox-nocaption"],
			customControl: ["ts-inpupbox-control"]
		};
		var boxClass = classes.boxClass = ["ts-messagebox-box", "ts-messagebox-center-left-position"];
		boxClass.push(this.style.borderStyle);
		if (this.customWrapClass) {
			boxClass.push(this.customWrapClass);
		}
		return classes;
	},

	/**
  * Returns the selector object of the control.
  * @protected
  * @return {Object}
  */
	getSelectors: function () {
		return {
			wrapEl: "#" + this.id + "-wrap",
			captionEl: "#" + this.id + "-caption"
		};
	},

	/**
  * Sets the information label of the control
  * @param {String} caption new caption
  */
	setCaption: function (caption) {
		if (this.caption === caption) {
			return;
		}
		this.caption = caption;
		this.safeRerender();
	},

	/**
  * @inheritdoc Terrasoft.Component#setVisible
  * @override
  */
	setVisible: function (visible) {
		if (this.visible === visible) {
			return;
		}
		if (!this.renderTo) {
			this.renderTo = Ext.getBody();
		}
		this.callParent(arguments);
		if (visible === true) {
			this._initCoverEl();
		}
		if (visible === false) {
			this._hideCoverEl();
		}
	},

	/**
  * Updates the configuration values of user controls.
  * @protected
  */
	updateCustomControl: function () {
		if (!this.controlConfig) {
			return null;
		}
		var controlConfig = this.controlConfig;
		var controlArray = this.controlArray;
		for (var i = 0; i < controlArray.length; i++) {
			var key = controlArray[i].name;
			var objectItemConfig = controlConfig[key];
			var control = controlArray[i].control;
			objectItemConfig.value = control.getValue();
		}
	},

	/**
  * Deletes user controls
  * @protected
  */
	destroyCustomControl: function () {
		var controlArray = this.controlArray;
		if (controlArray) {
			for (var i = 0; i < controlArray.length; i++) {
				var customControlItemLabel = controlArray[i].label;
				customControlItemLabel.destroy();
				var customControlItem = controlArray[i].control;
				customControlItem.destroy();
				var customControlItemDescription = controlArray[i].description;
				customControlItemDescription.destroy();
			}
		}
		this.controlArray = null;
		this.wrapEl.un("click", function (event) {
			event.stopEvent();
		});
	},

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

	/**
  * Init message box cover element.
  * @private
  */
	_initCoverEl: function () {
		this.coverEl = Ext.get(this.id + "-cover");
		this.coverEl.on("wheel", function (event) {
			event.preventDefault();
		});
		this.coverEl.on("click", function (event) {
			event.stopEvent();
		});
	},

	/**
  * Hides message box cover element.
  * @private
  */
	_hideCoverEl: function () {
		this.coverEl.dom.style.display = "none";
	},

	/**
  * Safe removes message box cover element.
  * @private
  */
	_removeCoverEl: function () {
		if (this.coverEl) {
			this.coverEl.destroy();
			this.coverEl = null;
		}
	},

	//region Methods: Public

	/**
  * Prepares message box to show.
  */
	prepare: function (config) {
		this.applyDefaultConfig();
		Ext.apply(this, config);
		this.reConfigurateButtonItems();
	},

	/**
  * Shows message box.
  */
	show: function () {
		this.setVisible(true);
	}

	//endregion

});