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

/**
 * @class Zarafa.plugins.facebook.facebookEventsPlugin
 * @extends Zarafa.core.Plugin
 *
 * This class integrates facebook events into the Zarafa calendar.
 */
Zarafa.plugins.facebook.FacebookEventsPlugin = Ext.extend(Zarafa.core.Plugin, {

	/**
	 * adds the cross-server script and registers insertion point.
	 * @protected
	 */
	initPlugin : function()
	{
		Zarafa.plugins.facebook.FacebookEventsPlugin.superclass.initPlugin.apply(this, arguments);

		// Init the SDK
		FB.init({
			appId      : container.getSettingsModel().get(this.getSettingsBase() + '/appId'), // App ID
			channelUrl : 'https://'+container.getSettingsModel().get(this.getSettingsBase() + '/home_domain')+'/channel', // Path to your Channel File
			status     : true, // check login status
			cookie     : true, // enable cookies to allow the server to access the session
			xfbml      : true  // parse XFBML
		});
		this.registerInsertionPoint('navigation.south', this.createFacebookButton, this);
		Zarafa.core.data.SharedComponentType.addProperty('plugins.facebook.dialogs.importevents');
	},

	/**
	 * Creates the button by clicking on which the Facebook
	 * will be imported to the Zarafa calendar.
	 *
	 * @return {Object} Configuration object for a {@link Ext.Button button}
	 * @private
	 */
	createFacebookButton:function()
	{
		var button=
		{
			xtype				: 'button',
			text				: _('Import Facebook events'),
			iconCls				: 'icon_facebook_button',
			navigationContext	: container.getContextByName('calendar'),
			handler				: this.onImportFacebookEvents,
			scope				: this
		}
		return  button;
	},

	/**
	 * create a dialog for import facebook events
	 * @private
	 */
	onImportFacebookEvents: function()
	{
		Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.core.data.SharedComponentType['plugins.facebook.dialogs.importevents'], undefined, {
			manager : Ext.WindowMgr
		});
	},

	/**
	 * Bid for the type of shared component
	 * and the given record.
	 * This will bid on calendar.dialogs.importevents
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @return {Number} The bid for the shared component
	 */
	bidSharedComponent : function(type, record)
	{
		var bid = -1;
		
		switch(type)
		{
			case Zarafa.core.data.SharedComponentType['plugins.facebook.dialogs.importevents']:
				bid = 2;
				break;
		}
		return bid;
	},

	/**
	 * Will return the reference to the shared component.
	 * Based on the type of component requested a component is returned.
	 * @param {Zarafa.core.data.SharedComponentType} type Type of component a context can bid for.
	 * @param {Ext.data.Record} record Optionally passed record.
	 * @return {Ext.Component} Component
	 */
	getSharedComponent : function(type, record)
	{
		var component;

		switch(type)
		{
			case Zarafa.core.data.SharedComponentType['plugins.facebook.dialogs.importevents']:
				component = Zarafa.plugins.facebook.FbIntegrationContentPanel;
				break;
		}

		return component;
	}
});

Zarafa.onReady(function() {
	container.registerPlugin(new Zarafa.core.PluginMetaData({
		name : 'facebook',
		displayName : _('Facebook Events'),
		pluginConstructor : Zarafa.plugins.facebook.FacebookEventsPlugin
	}));
});
Ext.namespace('Zarafa.plugins.facebook');

/**
 * @class Zarafa.plugins.facebook.FbEventSelectionContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * A contentpanel with grid for selection events from facebook
 */
Zarafa.plugins.facebook.FbEventSelectionContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {


	/**
	 * @cfg {Zarafa.plugins.facebook.FbEventStore} store - for facebook events
	 */
	store: undefined,

	/**
	 * @constructor
	 * @param {object} config
	 */
	constructor: function (config) {

		config = config || {};

		this.store = config.store || new Zarafa.plugins.facebook.data.FbEventStore();

		Ext.apply(this, {
			recordComponentPluginConfig : Ext.applyIf(config.recordComponentPluginConfig || {}, {
				useShadowStore : false
			}),
			closeOnSave             : true,
			items                   : [
				{
					xtype               : 'facebook.fbeventselectiongrid',
					store               : this.store,
					ref					: 'selectionGrid',
					viewConfig: {
						style	:	{ overflow: 'auto', overflowX: 'hidden' }
					}
				}
		]
		});

		Zarafa.plugins.facebook.FbEventSelectionContentPanel.superclass.constructor.call(this, config);

	}
});

Ext.reg('facebook.eventsselectioncontentpanel', Zarafa.plugins.facebook.FbEventSelectionContentPanel);
Ext.namespace('Zarafa.plugins.facebook');

/**
 * @class Zarafa.plugins.facebook.FbEventSelectionGrid
 * @extends Ext.grid.GridPanel
 * grid with events from facebook
 */
Zarafa.plugins.facebook.FbEventSelectionGrid = Ext.extend(Ext.grid.GridPanel, {

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

		Ext.apply(config, {
			colModel: new Ext.grid.ColumnModel({
				defaults: {
					width: 120,
					sortable: false
					},
				columns: [
					{header: _('Name'), name: 'name', dataIndex: 'name'},
					{header: _('Status'), name:  'rsvp_status',dataIndex: 'rsvp_status'},
					{header: _('Start Time'), name:  'start_time',dataIndex: 'start_time'},
					{header: _('End Time'), name: 'end_time',dataIndex: 'end_time'},
					{header: _('Location'), name: 'location',dataIndex: 'location'},
					{header: _('Description'), name: 'description', dataIndex: 'description'}
				]
			}),
			frame : true,
			buttons: [
				{
					xtype: 'button',
					width: 'auto',
					text: _('Save'),
					handler: this.onSave,
					scope: this
				}
			],
			viewConfig: {
				forceFit: true
			},
			scroll: true,
			sm: new Ext.grid.RowSelectionModel({multiSelect:true}),
			enableRowBody: true
		});
		Zarafa.plugins.facebook.FbEventSelectionGrid.superclass.constructor.call(this, config);
	},

	/**
	 * function for save facebook events in zarafa calendar
	 * @private
	 */
	onSave: function() {
		this.saveEvents();
	},

	/**
	 * create an appointment record from
	 * facebook event and save it in calendar
	 */
	saveEvents: function() {
		//receive existing calendar store
		var calendarStore = new Zarafa.calendar.AppointmentStore();
		var calendarFolder =  container.getHierarchyStore().getDefaultFolder('calendar');
		//receive Records from grid rows
		var newRecords = this.selModel.getSelections();
		Ext.each(newRecords, function(newRecord) {
			var record = newRecord.convertToAppointmentRecord(calendarFolder);
			calendarStore.add(record);
			calendarStore.save();
		}, this);
		this.dialog.close();
	}
});

Ext.reg('facebook.fbeventselectiongrid', Zarafa.plugins.facebook.FbEventSelectionGrid);Ext.namespace('Zarafa.plugins.facebook');

/**
 * @class Zarafa.plugins.facebook.FbIntegrationContentPanel
 * @extends Zarafa.core.ui.ContentPanel
 * contentpanel for logging in and out from facebook
 */
Zarafa.plugins.facebook.FbIntegrationContentPanel = Ext.extend(Zarafa.core.ui.ContentPanel, {

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

		Ext.applyIf(config, {
			layout					: 'fit',
			title				: _('Facebook events integration'),
			recordComponentPluginConfig : Ext.applyIf(config.recordComponentPluginConfig || {}, {
				useShadowStore : false
			}),
			closeOnSave				: true,
			items					: [
				{
					xtype				: 'facebook.fbintegrationpanel'
				}
			]
		});

		Zarafa.plugins.facebook.FbIntegrationContentPanel.superclass.constructor.call(this, config);
	}

});

Ext.reg('facebook.fbintegrationcontentpanel', Zarafa.plugins.facebook.FbIntegrationContentPanel);
Ext.namespace('Zarafa.plugins.facebook');

/**
 * @class Zarafa.plugins.facebook.FbIntegrationPanel
 * @extends Ext.form.FormPanel
 */
Zarafa.plugins.facebook.FbIntegrationPanel = Ext.extend(Ext.form.FormPanel, {

	/**
	 * @constructor
	 * @param {object} config
	 */
	constructor : function(config)
	{
		config = config || {};
		var self = this;
		Ext.apply(config, {
			xtype     : 'facebook.fbintegrationpanel',
			layout    : {
				type  : 'form',
				align : 'stretch'
			},
			anchor      : '100%',
			bodyStyle : 'background-color: inherit;',
			defaults  : {
				border      : true,
				bodyStyle   : 'background-color: inherit; padding: 3px 0px 3px 0px; border-style: none none solid none;'
			},
			buttons: [
				this.createImportButton(),
				this.createAuthButton()
			]
		});

		FB.Event.subscribe('auth.authResponseChange', function(authCheck) {
			if (authCheck.status == 'connected') {
				self.fbAuthButton.setText(_('Logout from Facebook'));
				self.fbAuthButton.setHandler(self.fbLogout);
			} else {
				self.fbAuthButton.setText(_('Login to Facebook'));
				self.fbAuthButton.setHandler(self.fbLogin);
			}
		});

		Zarafa.plugins.facebook.FbIntegrationPanel.superclass.constructor.call(this, config);
	},

	/**
	 * creates a button for authorization, checks if we are authorized to facebook
	 * @private
	 */
	createAuthButton: function() {
		if (!FB.getAuthResponse()) {
			return this.createFbLoginButton();
		} else {
			return this.createFbLogoutButton();
		}
	},

	/**
	 * creates a button for login to facebook
	 * @private
	 */
	createFbLoginButton: function()
	{
		return  {
			xtype       : 'button',
			border      : false,
			text        : _('Login to Facebook'),
			handler     : this.fbLogin,
			scope       : this,
			ref         : '/fbAuthButton',
			name        : 'fbAuthButton'
		};
	},


	/**
	 * creates a button for logout
	 * @private
	 */
	createFbLogoutButton: function() {
		return {
			xtype       : 'button',
			border      : false,
			text        : _('Logout from Facebook'),
			handler     : this.fbLogout,
			scope       : this,
			ref         : '/fbAuthButton',
			name        : 'fbAuthButton'
		};
	},

	/**
	 * login to facebook
	 * @private
	 */
	fbLogin: function()
	{
		FB.login(function(){}, {
			scope: 'user_events, user_birthday'
		});
	},

	/**
	 * logout from facebook
	 * @private
	 */
	fbLogout: function()
	{
		FB.logout();
	},

	/**
	 * button for request to facebook for events
	 * @private
	 */
	createImportButton : function()
	{
		return {
			xtype       : 'button',
			width       : 250,
			border      : false,
			text        : _('List events for selected period from Facebook'),
			handler     : this.importAllEvents,
			scope       : this
		}
	},

	/**
	 * facebook event selection dialog creation
	 * @private
	 */
	importAllEvents: function()
	{
		var self = this;
		if (FB.getAuthResponse()) {
			Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.plugins.facebook.FbEventSelectionContentPanel);
		} else {
			FB.login(function(response){
				// if we are successfully logged in, then authResponse property would be not empty
				if (response.authResponse)  {
					Zarafa.core.data.UIFactory.openLayerComponent(Zarafa.plugins.facebook.FbEventSelectionContentPanel);
				}
			}, {
				scope: 'user_events, user_birthday'
			});
		}
	}
});

Ext.reg('facebook.fbintegrationpanel', Zarafa.plugins.facebook.FbIntegrationPanel);
Ext.namespace('Zarafa.plugins.facebook.data');

/**
 * @class Zarafa.plugins.facebook.data.FbEventDataReader
 * @extends Ext.data.DataReader
 */
Zarafa.plugins.facebook.data.FbEventDataReader = Ext.extend(Ext.data.DataReader, {

	/**
	 * @cfg {Zarafa.core.data.RecordCustomObjectType} customObjectType The custom object type
	 * which represents the {@link Ext.data.Record records} which should be created using
	 * {@link Zarafa.core.data.RecordFactory#createRecordObjectByCustomType}.
	 */
	customObjectType : Zarafa.core.data.RecordCustomObjectType.ZARAFA_FACEBOOK_EVENT,

	/**
	 * @constructor
	 * @param {Object} meta Metadata configuration options (implementation-specific).
	 * @param {Array/Object} recordType
	 * <p>Either an Array of {@link Ext.data.Field Field} definition objects (which
	 * will be passed to {@link Ext.data.Record#create}, or a {@link Ext.data.Record Record}
	 * constructor created using {@link Ext.data.Record#create}.</p>
	 */
	constructor : function(meta, recordType)
	{
		meta = Ext.applyIf(meta || {}, {
			id            : 'id',
			idProperty    : 'id'
		});

		// If no recordType is provided, force the type to be a zarafa facebook event type
		if (!Ext.isDefined(recordType)) {
			recordType = Zarafa.core.data.RecordFactory.getRecordClassByCustomType(meta.customObjectType || this.customObjectType);
		}

		Zarafa.plugins.facebook.data.FbEventDataReader.superclass.constructor.call(this, meta, recordType);
	},

	/**
	 * start reading data from facebook through proxy
	 * @param {object} eventsData Received data from facebook
	 * @return {object} records : data
	 */
	readRecords: function (eventsData) {
		var root = eventsData.data;
		var success = true, v, totalRecords = root.length;
		if(this.meta.totalProperty){
			v = parseInt(this.getTotal(eventsData), 10);
			if(!isNaN(v)){
				totalRecords = v;
			}
		}
		if(this.meta.successProperty){
			v = this.getSuccess(eventsData);
			if(v === false || v === 'false'){
				success = false;
			}
		}
		var rs = this.extractData(eventsData.data, true);
		return {
			success: success,
			records: rs,
			totalRecords: totalRecords
		};
	},
	/**
	 * get an id from object with record data
	 * @param {object} recordData
	 * @return {number} id from the record's dataset
	 */
	getId : function(recordData) {
		return recordData.id;
	},

	/**
	 * extract data from the object
	 * received from the server
	 * @param {object} root
	 * @param {Boolean} returnRecords [false] Set true to return instances of Ext.data.Record
	 * @return {array}
	 */
	extractData: function (root, returnRecords) {
		var newRoot = [];
		var today = new Date();
		var max = today.add(Date.MONTH, 1);
		Ext.each(root, function(data){
			var newDate = new Date(data.start_time);
			if (newDate>=today && newDate<max) {
				newRoot.push(data);
			}
		});
		root = newRoot;
		//below is exact function from the superclass but changed according to ExtJs recommendations - record is created not through constructor, but using create function.
		// A bit ugly this, too bad the Record's raw data couldn't be saved in a common property named "raw" or something.
		var rawName = (this instanceof Ext.data.JsonReader) ? 'json' : 'node';

		// Had to add Check for XmlReader, #isData returns true if root is an Xml-object.  Want to check in order to re-factor
		// #extractData into DataReader base, since the implementations are almost identical for JsonReader, XmlReader
		if (this.isData(root) && !(this instanceof Ext.data.XmlReader)) {
			root = [root];
		}
		var f       = this.recordType.prototype.fields,
			fi      = f.items,
			fl      = f.length,
			rs      = [];
		if (returnRecords === true) {
			var Record = this.recordType;
			for (var i = 0; i < root.length; i++) {
				var n = root[i];
				var record = Zarafa.core.data.RecordFactory.createRecordObjectByCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FACEBOOK_EVENT, this.extractValues(n, fi, fl), this.getId(n));
				record[rawName] = n;    // <-- There's implementation of ugly bit, setting the raw record-data.
				rs.push(record);
			}
		}
		else {
			for (var i = 0; i < root.length; i++) {
				var data = this.extractValues(root[i], fi, fl);
				data[this.meta.idProperty] = this.getId(root[i]);
				rs.push(data);
			}
		}
		return rs;
		//end of superclass's function
	},

	/**
	 * type-casts a single row of raw-data from server
	 * done because of emptyFn in superclass, so we need to create this function to avoid errors
	 * @param {Object} data
	 * @return Zarafa.plugins.facebook.dataFbEventRecord
	 */
	extractValues: function(data) {
		return data;
	}

});
Ext.namespace('Zarafa.plugins.facebook.data');

/**
 * @class  Zarafa.plugins.facebook.data.FbEventProxy
 * @extends Ext.data.DataProxy
 * A proxy for import from facebook
 */
Zarafa.plugins.facebook.data.FbEventProxy = Ext.extend(Ext.data.DataProxy, {

	/**
	 * @constructor
	 * @param {object} config
	 */
	constructor: function (config) {
		var api = {};
		config = config || {};
		api[Ext.data.Api.actions.read] = true;
		api[Ext.data.Api.actions.create] = true;
		Ext.apply(config, {
			api: api
		});
		Zarafa.plugins.facebook.data.FbEventProxy.superclass.constructor.call(this, config);
	},

	/**
	 * does a request to server to FB api
	 * @param {string} action
	 * @param {object} reader
	 * @param {object} callback
	 * @param {object} scope, here == store
	 */
	doRequest: function(action, rs, params, reader, callback, scope, options) {
		if (action == 'read') {
			FB.api('/me/events', function(data) {
				Ext.each(data.data, function(generalData) {
					FB.api('/'+generalData.id, function(detailedData){
						generalData.description = detailedData.description;
						var resultData = {data: generalData};
						callback.call(scope, reader.readRecords(resultData), {}, true);
					})
				}, this);
			});
		}
	}

});Ext.namespace('Zarafa.plugins.facebook.data');

/**
 * @class Zarafa.plugins.facebook.data.FbEventRecordFields
 * Array of fields for the {@link Zarafa.plugins.facebook.data.FbEventRecord} object.
 */
Zarafa.plugins.facebook.data.FbEventRecordFields = [
	{name:'location', type:'string', defaultValue: ''},
	{name:'name', type:'string', defaultValue:''},
	{name: 'rsvp_status', type:'string', defaultValue: ''},
	{name: 'start_time', type: 'date', dateFormat: 'timestamp', defaultValue: null},
	{name: 'end_time', type:'date', dateFormat: 'timestamp', defaultValue:null},
	{name: 'description', type: 'string', defaultValue:''}
];

// Register a custom type to be used by the Record Factory
Zarafa.core.data.RecordCustomObjectType.addProperty('ZARAFA_FACEBOOK_EVENT');
Zarafa.core.data.RecordFactory.addFieldToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FACEBOOK_EVENT, Zarafa.plugins.facebook.data.FbEventRecordFields);

/**
 * @class Zarafa.plugins.facebook.data.FbEventRecord
 * @extends Zarafa.core.data.IPMRecord
 * added an 'id' param to baseIdParameters
 */
Zarafa.plugins.facebook.data.FbEventRecord = Ext.extend(Zarafa.core.data.IPMRecord, {
	baseIdProperties: ['id'],

	/**
	 * convert this record to appointment Record
	 * @param {string} parent_entry_id
	 * @param {string} store_entry_id
	 * @return {Zarafa.calendar.AppointmentRecord}
	 */
	convertToAppointmentRecord: function (calendarFolder) {
		var newRecord = Zarafa.core.data.RecordFactory.createRecordObjectByMessageClass('IPM.Appointment', {
			startdate: new Date(this.get('start_time')),
			duedate: (this.get('end_time')) ?
				new Date(this.get('end_time')) :
				new Date(this.get('start_time')).add(Date.HOUR, 1),
			location: this.get('location'),
			subject: this.get('name'),
			body: this.get('description'),
			commonstart: new Date(this.get('start_time')),
			commonend: (this.get('end_time')) ?
				new Date(this.get('end_time')) :
				new Date(this.get('start_time')).add(Date.HOUR, 1),
			parent_entryid: calendarFolder.get('entryid'),
			store_entryid: calendarFolder.get('store_entryid')
		});
		return newRecord;
	}
});

Zarafa.core.data.RecordFactory.setBaseClassToCustomType(Zarafa.core.data.RecordCustomObjectType.ZARAFA_FACEBOOK_EVENT, Zarafa.plugins.facebook.data.FbEventRecord);

Ext.namespace('Zarafa.plugins.facebook.data');

/**
 * @class Zarafa.plugins.facebook.data.FbEventStore
 * @extends Ext.data.Store
 *
 * This class extends Ext Store to configure the
 * proxy and reader in custom way.
 * Instead of defining the records dynamically, reader will
 * create {@link Zarafa.plugins.facebook.data.fbEventRecord} instance.
 *
 */
Zarafa.plugins.facebook.data.FbEventStore = Ext.extend(Ext.data.Store, {

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

		Ext.applyIf(config, {
			reader : new Zarafa.plugins.facebook.data.FbEventDataReader({
				id : 'id',
				idProperty : 'id',
				dynamicRecord : false
			}),
			writer : new Zarafa.core.data.JsonWriter(),
			proxy  : new Zarafa.plugins.facebook.data.FbEventProxy(),
			autoLoad: true
		});

		Zarafa.plugins.facebook.data.FbEventStore.superclass.constructor.call(this, config);
	}
});

Ext.reg('facebook.fbeventstore', Zarafa.plugins.facebook.data.FbEventStore);