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

/**
 * @class Zarafa.plugins.desktopnotifications.ABOUT
 * @extends String
 *
 * The copyright string holding the copyright notice for the Kopano Desktop Notifications Plugin.
 */
Zarafa.plugins.desktopnotifications.ABOUT = ""
	+ "<p>Copyright (C) 2013  Saket Patel &lt;silentsakky@gmail.com&gt;<br>"
	+ "Copyright (C) 2016 Kopano and its licensors</p>"

	+ "<p>This program is free software: you can redistribute it and/or modify "
	+ "it under the terms of the GNU Affero General Public License as "
	+ "published by the Free Software Foundation, either version 3 of the "
	+ "License, or (at your option) any later version.</p>"

	+ "<p>This program is distributed in the hope that it will be useful, "
	+ "but WITHOUT ANY WARRANTY; without even the implied warranty of "
	+ "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the "
	+ "GNU Affero General Public License for more details.</p>"

	+ "<p>You should have received a copy of the GNU Affero General Public License "
	+ "along with this program. If not, see <a href=\"http://www.gnu.org/licenses/\" target=\"_blank\">http://www.gnu.org/licenses/</a>.</p>";
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() {
	var PERMISSION = ['granted', 'default', 'denied'];

	return {
		/**
		 * Check if browser supports notifications API
		 * @return {Boolean} true if browser supports desktop notifications else false
		 */
		supports : function()
		{
			if (window.Notification) {
				return true;
			}

			return false;
		},

		/**
		 * 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()) {
				console.log('Browser doesn\'t support notifications');
				return;
			}

			var permission = 'default';
			if (Ext.isFunction(Notification.checkPermission)) {
				permission = PERMISSION[Notification.checkPermission()];
			} else if (Ext.isFunction(Notification.permissionLevel)) {
				permission = Notification.permissionLevel();
			} else if (Notification.permission) {
				permission = Notification.permission;
			}

			if (permission === 'granted') {
				return true;
			}

			return false;
		},

		/**
		 * 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()) {
				console.log('Browser doesn\'t support notifications');
				return;
			}

			var callbackFn = Ext.isFunction(callback) ? function(perm) {
				// chrome doesn't give us current permission level, so default to granted if permission level is passed
				var permission = 'granted';
				if (perm) {
					permission = perm;
				}

				callback.apply(this, [permission]);
			} : Ext.emptyFn;

			if (Ext.isFunction(Notification.requestPermission)) {
				Notification.requestPermission(callbackFn);
			}
		},

		/**
		 * 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()) {
				console.log('Browser doesn\'t support notifications');
				return;
			}

			if (!this.hasPermission()) {
				console.log('Permission is denied to show desktop notifications');
				return;
			}

			var notification = new Notification(title, {
				icon : options.icon,
				body : options.body,
				requireInteraction: true
			});

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

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

			// give audio feedback
			this.audioTag = Ext.getBody().createChild({
				tag : 'audio',
				type : 'audio/ogg',
				src : 'plugins/desktopnotifications/resources/audio.ogg',
				autoplay : true
			});

			// destroy audio element when playback is completed
			this.audioTag.on('ended', function() {
				Ext.destroy(this.audioTag);
				delete this.audioTag;
			}, this);
		}
	};
})();
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
		};
	}
});

// Don't load the plugin if browser is IE
if (!Ext.isIE) {
	Zarafa.onReady(function() {
		container.registerPlugin(new Zarafa.core.PluginMetaData({
			name : 'desktopnotifications',
			displayName : dgettext('plugin_desktopnotifications', 'Desktop Notifications Plugin'),
			about : Zarafa.plugins.desktopnotifications.ABOUT,
			pluginConstructor : Zarafa.plugins.desktopnotifications.DesktopNotificationsPlugin
		}));
	});
}
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 : 'plugins/desktopnotifications/resources/Kopano.ico'
		}, {
			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.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 : dgettext('plugin_desktopnotifications', '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 dekstop 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 : dgettext('plugin_desktopnotifications', '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 : dgettext('plugin_desktopnotifications', 'We need permissions to enable desktop notifications.')
			}, {
				xtype : 'button',
				width : 200,
				text : dgettext('plugin_desktopnotifications', 'Request Permissions'),
				handler : this.requestPermission,
				scope : this,
				hideLabel : true,
				ref : '../requestPermissionBtn',
				style: 'margin-bottom: 6px;'
			}, {
				xtype : 'checkbox',
				boxLabel : dgettext('plugin_desktopnotifications', '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 : dgettext('plugin_desktopnotifications', '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: dgettext('plugin_desktopnotifications', '{A} Autohide 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);
