Ext.namespace('Zarafa.plugins.desktopnotifications.js');

/**
 * @class Zarafa.plugins.desktopnotifications.js.DesktopNotifier
 * @extends Zarafa.core.ui.notifier.NotifyPlugin
 *
 * A plugin for notification plugin to show desktop notifications instead of normal in browser
 * notifications for actions like new mail, reminder etc.
 */
Zarafa.plugins.desktopnotifications.js.DesktopNotifier = Ext.extend(Zarafa.core.ui.notifier.NotifyPlugin, {
	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		Zarafa.plugins.desktopnotifications.js.DesktopNotifier.superclass.constructor.call(this, config);
	},

	/**
	 * Notify the user with a message.
	 *
	 * The category can be either  "error", "warning", "info" or "debug", or a subtype thereof (e.g. "info.newmail").
	 *
	 * @param {String} category The category which applies to the notification.
	 * @param {String} title The title which must be shown in the message.
	 * @param {String} message The message which should be displayed.
	 * @param {Object} config Configuration object which can be applied to the notifier
	 * This object can contain keys like:
	 * - autoclose: Auto close notification after sometime
	 * @return {Mixed} A reference to the message which was created, this can be used
	 * as value for 'reference' in the config argument.
	 */
	notify : function(category, title, message, config)
	{
		Zarafa.plugins.desktopnotifications.js.DesktopNotification.notify(title, {
			tag : category,
			body : message,
			icon : 'client/resources/images/favicon.ico?kv2.2.0'
		}, {
			click : function() {
				// focus window which generated this notification
				window.focus();
			}
		});

		Zarafa.plugins.desktopnotifications.js.DesktopNotifier.superclass.notify.apply(this, arguments);
	}
});

Zarafa.onReady(function() {
	container.getNotifier().registerPlugin('desktopnotifier', new Zarafa.plugins.desktopnotifications.js.DesktopNotifier());
});
Ext.namespace('Zarafa.plugins.desktopnotifications.js');

/**
 * @class Zarafa.plugins.desktopnotifications.js.DesktopNotification
 * @singleton
 *
 * Singleton class to provide a wrapper for HTML5 desktop notifications feature
 */
Zarafa.plugins.desktopnotifications.js.DesktopNotification = (function() {
	return {
		/**
		 * Check if browser supports notifications API
		 * @return {Boolean} true if browser supports desktop notifications else false
		 */
		supports : function()
		{
			return !!window.Notification;
		},

		/**
		 * Check if browser has permissions to show notifications
		 * @return {Boolean} true if permissions are granted to show desktop notifications else false
		 */
		hasPermission : function()
		{
			if (!this.supports()) {
				return false;
			}

			return Notification.permission === 'granted';
		},

		/**
		 * Ask for permissions to show notifications
		 * In chrome this function will only work when you call it based on some user action
		 * like click of a button
		 * @param {Function} callback callback function that will be called after user has
		 * granted/rejected permission request
		 */
		authorize : function(callback)
		{
			if (!this.supports()) {
				if (Ext.isFunction(callback)) {
					callback('denied');
				}
				return;
			}

			if (!Ext.isFunction(Notification.requestPermission)) {
				return;
			}

			// Guard against double invocation: some browsers call both the
			// callback parameter and resolve the returned promise.
			var called = false;
			var wrappedCallback = function(perm) {
				if (!called && Ext.isFunction(callback)) {
					called = true;
					callback(perm || 'granted');
				}
			};

			// Pass callback as parameter for Safari compatibility, which
			// historically used the callback form instead of a promise.
			var result = Notification.requestPermission(wrappedCallback);
			if (result && Ext.isFunction(result.then)) {
				result.then(wrappedCallback);
			}
		},

		/**
		 * Function will show a desktop notification
		 * @param {String} title title to use when showing desktop notifications
		 * @param {Object} options object containing below key value pairs to provide extra information
		 * for the desktop notifications
		 * 		- icon : icon to show in desktop notifications
		 * 		- body : message to display
		 *		- tag : tag to group same type of notifications so multiple notifications
		 *				will not be showed multiple times
		 * @param {Object} handlers object containing handler function that can be registered on instance of
		 * notification object
		 * 		- possible handlers are click, show, error, close
		 */
		notify : function(title, options, handlers)
		{
			if (!this.supports()) {
				return;
			}

			if (!this.hasPermission()) {
				return;
			}

			var settingsModel = container.getSettingsModel();
			var soundDisabled = settingsModel.get('zarafa/v1/plugins/desktopnotifications/disable_sound');

			var notification = new Notification(title, {
				icon : options.icon,
				body : options.body,
				requireInteraction: true,
				silent: !soundDisabled ? false : true
			});

			if (settingsModel.get('zarafa/v1/plugins/desktopnotifications/autohide_enable')) {
				var sleepTime = settingsModel.get('zarafa/v1/plugins/desktopnotifications/autohide_time') * 1000;
				setTimeout(function () {
					notification.close();
				}, sleepTime);
			}

			if (handlers) {
				for(var key in handlers) {
					notification['on' + key] = handlers[key];
				}
			}

			// Give audio feedback
			if (!soundDisabled) {
				this.playNotificationSound();
			}
		},

		/**
		 * Play notification sound, trying WebM first with MP3 fallback for Safari.
		 * @private
		 */
		playNotificationSound : function()
		{
			// Clean up previous audio if still playing
			if (this.notificationAudio) {
				this.notificationAudio.pause();
				this.notificationAudio = null;
			}

			var self = this;
			var basePath = 'plugins/desktopnotifications/resources/audio';
			var audio = new Audio();
			this.notificationAudio = audio;

			audio.addEventListener('ended', function () {
				self.notificationAudio = null;
			});

			// Try WebM first (Chrome, Firefox, Edge), fall back to MP3 (Safari)
			if (audio.canPlayType('audio/webm')) {
				audio.src = basePath + '.webm';
			} else {
				audio.src = basePath + '.mp3';
			}

			try {
				var playPromise = audio.play();
				if (playPromise && Ext.isFunction(playPromise.catch)) {
					playPromise.catch(function() {
						self.notificationAudio = null;
					});
				}
			} catch (e) {
				this.notificationAudio = null;
			}
		}
	};
})();
Ext.namespace('Zarafa.plugins.desktopnotifications');

/**
 * @class Zarafa.plugins.desktopnotifications.DesktopNotificationsPlugin
 * @extends Zarafa.core.Plugin
 * This class is used for adding files from the users's Dropbox folder
 * to his emails as attachments
 */
Zarafa.plugins.desktopnotifications.DesktopNotificationsPlugin = Ext.extend(Zarafa.core.Plugin, {
	/**
	 * initialises insertion point for plugin
	 * @protected
	 */
	initPlugin : function()
	{	
		Zarafa.plugins.desktopnotifications.DesktopNotificationsPlugin.superclass.initPlugin.apply(this, arguments);

		this.registerInsertionPoint('context.settings.categories', this.createSettingsCategory, this);
	},

	/**
	 * Return the instance of {@link Zarafa.plugins.desktopnotifications.js.settings.SettingsDesktopNotificationsCategory SettingsDesktopNotificationsCategory}.
	 *
	 * @return {Zarafa.plugins.desktopnotifications.js.settings.SettingsDesktopNotificationsCategory} An instance of the settings category
	 * @private
	 */
	createSettingsCategory : function()
	{
		return {
			xtype : 'zarafa.settingsdesktopnotificationscategory',
			plugin : this
		};
	}
});

Zarafa.onReady(function() {
	container.registerPlugin(new Zarafa.core.PluginMetaData({
		name : 'desktopnotifications',
		displayName : _('Desktop Notifications Plugin'),
		about : Zarafa.plugins.desktopnotifications.ABOUT,
		pluginConstructor : Zarafa.plugins.desktopnotifications.DesktopNotificationsPlugin
	}));
});
Ext.namespace('Zarafa.plugins.desktopnotifications.js.settings');

/**
 * @class Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsCategory
 * @extends Zarafa.settings.ui.SettingsCategory
 * @xtype zarafa.settingspasswdcategory
 *
 * The desktop notification settings category that will allow users to enable/disable desktop notifications
 */
Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsCategory = Ext.extend(Zarafa.settings.ui.SettingsCategory, {
	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			title : _('Desktop Notifications'),
			categoryIndex : 9996,
			xtype : 'zarafa.settingsdesktopnotificationscategory',
			iconCls : 'icon_desktopnotifications_settings',
			items : [{
				xtype : 'zarafa.settingsdesktopnotificationswidget',
				settingsContext : config.settingsContext
			}]
		});

		Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsCategory.superclass.constructor.call(this, config);
	}
});

Ext.reg('zarafa.settingsdesktopnotificationscategory', Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsCategory);
Ext.namespace('Zarafa.plugins.desktopnotifications.js.settings');

/**
 * @class Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsWidget
 * @extends Zarafa.settings.ui.SettingsWidget
 * @xtype zarafa.settingsdesktopnotificationswidget
 *
 * The {@link Zarafa.settings.ui.SettingsWidget widget} for enable/disable desktop notifications
 * in the {@link Zarafa.plugins.desktopnotifications.js.settings.SettingsDesktopNotificationsCategory desktop notification category}.
 */
Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsWidget = Ext.extend(Zarafa.settings.ui.SettingsWidget, {

	/**
	 * @cfg {Zarafa.plugins.desktopnotifications.js.DesktopNotificationsPlugin} plugin The plugin which has registered this
	 * settings widget.
	 */
	plugin : undefined,

	/**
	 * Settings model instance which will be used to get current settings
	 * @property
	 * @type Zarafa.settings.SettingsModel
	 */
	model : undefined,

	/**
	 * @constructor
	 * @param {Object} config Configuration object
	 */
	constructor : function(config)
	{
		config = config || {};

		Ext.applyIf(config, {
			title : _('Desktop Notifications Settings'),
			xtype : 'zarafa.settingsdesktopnotificationswidget',
			layout : {
				// override from SettingsWidget
				type : 'fit'
			},
			items : this.createPanelItems()
		});

		Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsWidget.superclass.constructor.call(this, config);
	},

	/**
	 * Function will return object which will be used to create items for this settings widget
	 * @return {Array} Array containing configuration objects of child items
	 */
	createPanelItems : function()
	{
		return [{
			xtype : 'form',
			border : false,
			autoHeight: true,
			items : [{
				xtype : 'displayfield',
				hideLabel : true,
				value : _('We need permissions to enable desktop notifications.')
			}, {
				xtype : 'button',
				width : 200,
				text : _('Request Permissions'),
				handler : this.requestPermission,
				scope : this,
				hideLabel : true,
				ref : '../requestPermissionBtn',
				style: 'margin-bottom: 6px;'
			}, {
				xtype : 'checkbox',
				boxLabel : _('Enable desktop notifications for new mail'),
				name : 'zarafa/v1/main/notifier/info/newmail/value',
				handler : this.onChangeCheckbox,
				scope : this,
				hideLabel : true,
				ref : '../newMailNotificationsCheck'
			}, {
				xtype : 'checkbox',
				boxLabel : _('Enable desktop notifications for reminders'),
				name : 'zarafa/v1/main/notifier/info/reminder/value',
				handler : this.onChangeCheckbox,
				scope : this,
				hideLabel : true,
				ref : '../reminderNotificationsCheck'
			}, {
				xtype: 'zarafa.compositefield',
				plugins: [ 'zarafa.splitfieldlabeler' ],
				fieldLabel: _('{A} Auto-hide desktop notification after second(s) {B}'),
				labelWidth: 300,
				combineErrors: false,
				items: [{
					xtype : 'checkbox',
					labelSplitter: '{A}',
					name : 'zarafa/v1/plugins/desktopnotifications/autohide_enable',
					ref : '../../autoHideBox',
					boxLabel : '',
					hideLabel : true,
					checked : true,
					style: 'margin-top: 4px',
					listeners : {
						change : this.onFieldChange,
						scope : this
					}
				}, {
					xtype: 'zarafa.spinnerfield',
					labelSplitter: '{B}',
					vtype: 'naturalInteger',
					name : 'zarafa/v1/plugins/desktopnotifications/autohide_time',
					ref : '../../autoHideTimeSpinner',
					incrementValue: 1,
					defaultValue: 1,
					minValue : 1,
					width: 55,
					allowBlank: false,
					allowDecimals: false,
					allowNegative: false,
					listeners: {
						change: this.onFieldChange,
						scope: this
					},
					plugins: ['zarafa.numberspinner']
				}]
			}]
		}];
	},

	/**
	 * Function will be used to get permission from user to show desktop notifications
	 * @FIXME move code of changing setting to some other part
	 */
	requestPermission : function()
	{
		Zarafa.plugins.desktopnotifications.js.DesktopNotification.authorize(function(perm) {
			if (perm === 'granted') {
				// update ui
				this.update(this.model);
			}
		}.createDelegate(this));
	},

	/**
	 * Function will be used to enable/disable desktop notifications for new mail and reminder functionalities.
	 */
	onChangeCheckbox : function(checkbox, checked)
	{
		if (!this.model) {
			return;
		}

		var notifier = container.getSettingsModel().get(checkbox.name, false, true) || 'popup';

		if (checked) {
			notifier = 'desktopnotifier';
		}

		// change setting for new mail/reminder notification
		if (this.model.get(checkbox.name) !== notifier) {
			this.model.set(checkbox.name, notifier);
		}

		// update ui
		this.update(this.model);
	},

	/**
	 * Update the view with the new values of the settings
	 * model. Called when opening the settings widget or when a new
	 * folder is selected.
	 *
	 * @param {Zarafa.settings.SettingsModel} settingsModel The settings to display.
	 */
	update : function(settingsModel)
	{
		this.model = settingsModel;

		// enable/disable request permission button
		var hasPermission = Zarafa.plugins.desktopnotifications.js.DesktopNotification.hasPermission();

		this.requestPermissionBtn.setDisabled(hasPermission);

		this.newMailNotificationsCheck.setDisabled(!hasPermission);
		this.reminderNotificationsCheck.setDisabled(!hasPermission);

		// check/uncheck checkboxes for newmail and reminder functionality
		var newMailChecked = (this.model.get(this.newMailNotificationsCheck.name) === 'desktopnotifier');
		this.newMailNotificationsCheck.setValue(newMailChecked);

		var reminderChecked = (this.model.get(this.reminderNotificationsCheck.name) === 'desktopnotifier');
		this.reminderNotificationsCheck.setValue(reminderChecked);

		// Set values in autoSave checkbox and textfield.
		var enabled = settingsModel.get(this.autoHideBox.name);

		this.autoHideBox.setValue(enabled);

		var spinnerValue = this.model.get(this.autoHideTimeSpinner.name);
		if (spinnerValue === 0 || !Ext.isDefined(spinnerValue))  {
			this.autoHideTimeSpinner.setValue(5); // Default 5 minutes
		} else {
			this.autoHideTimeSpinner.setValue(spinnerValue);
		}
	},

	/**
	 * Called by the {@link Zarafa.settings.ui.SettingsCategory Category} when
	 * it has been called with {@link zarafa.settings.ui.SettingsCategory#updateSettings}.
	 * This is used to update the settings from the UI into the {@link Zarafa.settings.SettingsModel settings model}.
	 * @param {Zarafa.settings.SettingsModel} settingsModel The settings to update
	 */
	updateSettings : function(settingsModel)
	{
		var spinnerValue = this.autoHideTimeSpinner.getValue();
		if (spinnerValue === 0 || !Ext.isDefined(spinnerValue))  {
			spinnerValue = 1;
		}

		settingsModel.beginEdit();
		settingsModel.set(this.autoHideTimeSpinner.name, spinnerValue);
		settingsModel.endEdit();
	},

	/**
	 * Event handler which is called when one of the textfields has been changed.
	 * This will apply the new value to the settings.
	 * @param {Ext.form.Field} field The field which has fired the event
	 * @param {String} value The new value
	 * @private
	 */
	onFieldChange : function(field, value)
	{
		if (this.model) {
			// FIXME: The settings model should be able to detect if
			// a change was applied
			if (this.model.get(field.name) !== value) {
				this.model.set(field.name, value);
			}
		}
	}
});

Ext.reg('zarafa.settingsdesktopnotificationswidget', Zarafa.plugins.desktopnotifications.js.settings.SettingsNotificationsWidget);
