
/**
 * @class Voyeur.Tool.CorpusTypeFrequenciesGrid
 * @description A panel for displaying type frequencies across the corpus in a
 *              tabular format.
 * @extends Ext.grid.Panel
 * @inherits Voyeur.Tool
 * @author Stéfan Sinclair
 * @since 0.1.1
 */
Voyeur.Tool.CorpusTypeFrequenciesGrid = Ext.extend(Ext.grid.GridPanel, {
	
    /**
     * @cfg {String} offset
     * The number of pixels to offset the shadow from the element (defaults to <tt>4</tt>)
     */
	defaultExtendedSortZscoreMinimum : 1,
	defaultComparisonCorpus : null,
	stopList : null,
	constructor : function(config) {

		Ext.apply(this, new Voyeur.Tool(config,
						Voyeur.Tool.CorpusTypeFrequenciesGrid.prototype.i18n));

		this.setQueryValues('stopList','defaultExtendedSortZscoreMinimum','defaultComparisonCorpus');

		var store = new Ext.data.JsonStore({
			root : 'corpusTypes.types',
			totalProperty : 'corpusTypes["@totalTypes"]',
			remoteSort : true,
			fields : Voyeur.data.CorpusTypes.fields,
			proxy : new Ext.data.HttpProxy({
						url : this.getTromboneUrl(),
						timeout : 60000
					}),
			listeners : {
				'load' : {
					fn : function(store, records, options) {
						var filters = [];
						if (options.params) {
							var sortBy = options.params.sortBy;
							if (sortBy != 'type' && sortBy != 'rawFreq'
									&& sortBy != 'rawZscore') {filters.push(this.localize('extendedSortZscoreMinimum','tool'));}
							if (options.params.stopList) {filters.push(this.localize('stopList','tool'));}
							if (filters.length>0) {
								this.getApplication().growl(this.localize('title'),
										this.localize('filtersInEffect','tool')+'<ul><li>'+filters.join('</li><li>')+'</ul>',
										this.body)
							}							
						}
					},
					scope : this
				},
				'loadexception' : {
					fn : function(conn, proxy, response, error) {
						var alert = Ext.MessageBox
								.alert(
										"Error",
										"An error occurred while loading data:<pre style='overflow: auto; clear: both'>\n"
												+ response.responseText
												+ "</pre>");
						alert.setIcon(Ext.MessageBox.ERROR);
					},
					scope : this
				}
			},
			baseParams : {
				tool : 'CorpusTypeFrequencies',
				extendedSortZscoreMinimum : this.defaultExtendedSortZscoreMinimum
				,stopList: this.stopList
			}
		})

		store.setDefaultSort('rawFreq', 'desc')

		var size = 50;

		var xtypePrefix = config.xtype + '.';

		config.viewConfig = config.viewConfig ? config.viewConfig : {};
		Ext.applyIf(config.viewConfig, {
					//forceFit : true,
					emptyText : this.localize('noResults', 'tool'),
					deferEmptyText : false
				})

		if (!config.plugins) {
			config.plugins = []
		}

		this.search = new Ext.ux.grid.Search({
					iconCls : 'icon-zoom'
				});

		config.plugins.push(this.search, new Ext.ux.grid.Favs({exporter: 'type', align: 'left'}))

		var sm = new Ext.grid.CheckboxSelectionModel()

		this.pagingToolBar = new Ext.PagingToolbar({
					store : store,
					pageSize : size,
					displayInfo : false,
					displayMsg : "{0} - {1} of {2}",
					listeners : {
						'afterrender' : function(tb) {
							tb.first.hide();
							tb.last.hide();
							tb.refresh.hide();
							seps = tb.findByType('tbseparator');
							for (var i=0;i<seps.length;i++) {seps[i].hide();}
						}
					}
				});
				
		var panel = this;

		Ext.applyIf(config, {
			viewConfig : config.viewConfig,
			iconCls : 'table',
			loadMask : true,
			stripeRows : true,
			autoExpandColumn : this.getId()+'-column-type',
			sm: new Ext.grid.CheckboxSelectionModel(),
			colModel : new Ext.grid.ColumnModel([sm, {
						header : this.localize('type'),
						dataIndex : 'type',
						sortable : true,
						tooltip : this.localize('typeTip'),
						renderer : function(val) {
							return "<span class='keyword'>" + val + "</span>"
						}
						,id: this.getId()+'-column-type'
					}, {
						header : this.localize('rawFreq'),
						dataIndex : 'rawFreq',
						sortable : true,
						tooltip : this.localize('rawFreqTip'),
						renderer : Ext.util.Format.numberRenderer('0,000')
						,width: 100
					}, {
						header : this.localize('rawZscore'),
						dataIndex : 'rawZscore',
						sortable : true,
						hidden : true,
						tooltip : this.localize('rawZscoreTip'),
						renderer : Ext.util.Format.numberRenderer('0,000.00')
						,width: 100
					}, {
						header : this
								.localize('rawZscoreDifferenceCorpusComparison'),
						dataIndex : 'rawZscoreDifferenceCorpusComparison',
						sortable : true,
						hidden : true,
						tooltip : this
								.localize('rawZscoreDifferenceCorpusComparisonTip'),
						renderer : function(val) {
							return isNaN(val) ? '–' : "<span class='"
									+ (val < 0 ? 'negative' : 'positive') + "'>"
									+ Ext.util.Format.number(val,'0,000.0') + '</span>'
						}
						,width: 100
					}, {
						header : this.localize('relativeMean'),
						dataIndex : 'relativeMean',
						sortable : true,
						hidden : true,
						tooltip : this.localize('relativeMeanTip'),
						renderer : function(val) {return Ext.util.Format.number(val*10000,'0,000.0')}
						,width: 100
					}, {
						header : this.localize('relativeStdDev'),
						dataIndex : 'relativeStdDev',
						sortable : true,
						hidden : true,
						tooltip : this.localize('relativeStdDevTip'),
						renderer : function(val) {
							return isNaN(val) ? '–' : Ext.util.Format.number(val,'0,000.000')
						}
						,width: 100
					}, {
						header : this.localize('relativeKurtosis'),
						dataIndex : 'relativeKurtosis',
						sortable : true,
						hidden : true,
						tooltip : this.localize('relativeKurtosisTip'),
						renderer : function(val) {
							return isNaN(val) ? '–' : "<span class='"
									+ (val < 0 ? 'negative' : 'positive') + "'>"
									+ Ext.util.Format.number(val,'0,000.00') + '</span>'
						}
						,width: 100
					}, {
						header : this.localize('relativeSkewness'),
						dataIndex : 'relativeSkewness',
						sortable : true,
						hidden : true,
						tooltip : this.localize('relativeSkewnessTip'),
						renderer : function(val) {
							return isNaN(val) ? '–' : "<span class='"
									+ (val < 0 ? 'negative' : 'positive') + "'>"
									+ Ext.util.Format.number(val,'0,000.00') + '</span>'
						}
					}, {
						header : this.localize('relativeFreqs'),
						dataIndex : 'relativeFreqs',
						tooltip : this.localize('relativeFreqsTip'),
						width : 100,
						renderer : function(val, cell, record, rowIndex, colIndex) {
							return panel.getSparkLine(val, panel.getColumnModel().getColumnWidth(colIndex))
						}
					}]),
			store : store,
			bbar : this.pagingToolBar
//			,enableDragDrop: true
		});
		store.paramNames.sort = 'sortBy';
		store.paramNames.dir = 'sortDirection'

		Voyeur.Tool.CorpusTypeFrequenciesGrid.superclass.constructor.apply(
				this, arguments);
		this.addListener('CorpusSummaryResultLoaded', function(src, data) {
					this.getStore().baseParams.corpus = data.corpus['@id'];
					if (!data.corpusTypes) { // should come in separate event
						this.getStore().load({
									params : {
										start : 0,
										limit : this.getBottomToolbar().pageSize,
										sortBy : 'rawFreq',
										sortDirection : 'desc',
										extendedSortZscoreMinimum : this.defaultExtendedSortZscoreMinimum
										,stopList: this.stopList
									}
								})
					}
				}, this);
		this.addListener('rowclick', function(src, grid, rowIndex, e) {
					this.fireSelectionChange();
					return true;
				}, this);
		this.addListener('CorpusTypeFrequenciesResultLoaded', function(src, data) {
					this.getStore().baseParams.corpus = data.corpus['@id'];
					this.getStore().loadData(data);
					var size = this.getCorpus().getSize();
					if (size>100) {
						var cm = this.getColumnModel();
						var ind = cm.findColumnIndex('relativeFreqs');
						cm.setColumnWidth(ind, size);
					}
				}, this);
		this.addListener('CorpusTypeFrequenciesGridBootstrap',
				function(src, params) {
					if (params.query) {
						this.search.field.setValue(params.query);
					}
					if (params.sortBy && params.sortBy=='relativeKurtosis') {
						var cm = this.getColumnModel();
						var ind = cm.findColumnIndex('relativeKurtosis');
						cm.setHidden(ind, false);
					}
					Ext.applyIf(params, {
								start : 0,
								limit : this.getBottomToolbar().pageSize,
								sortBy : 'rawFreq',
								sortDirection : 'desc',
								extendedSortZscoreMinimum : this.defaultExtendedSortZscoreMinimum
							})
					this.getStore().setDefaultSort(params.sortBy,
							params.sortDirection);
					if (this.getStore().getCount()>0) {
						this.getStore().load({params: params})
					}
					else {
						this.update({
									params : params,
									tool : ['CorpusSummary','CorpusTypeFrequencies']
								});
					}
				}, this);

	}

	,
	fireSelectionChange : function() {
		var time = new Date().getMilliseconds();
		this.lastSelectionTime = time;
		var me = this;
		setTimeout(function() {
					if (me.lastSelectionTime == time) {
						records = me.getSelectionModel().getSelections();
						types = []
						for (var i = 0; i < records.length; i++) {
							types.push(records[i].get('type'));
						}
						if (types.length == 1) {
							Voyeur.application.dispatchEvent(
									'corpusTypeSelected', this,  {
										type : types[0]
									}, records[0], me.getStore(), me);
						} else if (types.length > 1) {
							Voyeur.application.dispatchEvent(
									'corpusTypesSelected', this, {
										type : types
									}, records, me.getStore(), me);
						}
					}
				}, 1000);
	},
	lastSelectionTime : 0,
	getTromboneToolName : function() {
		return 'CorpusTypeFrequencies';
	}

	,
	showOptions : function() {
		this.showOptionsWindow({
			items : [{
				xtype : 'form',
				labelWidth : 150,
				labelAlign : 'right',
				border : false,
				items : [{
					xtype : 'combo',
					id : 'stopList',
					value : this.getStore().baseParams.stopList,
					fieldLabel : '<span ext:qtip="'
							+ this.localize('stopListTip','tool') + '">'
							+ this.localize('stopList','tool') + '</span>',
					loadingText : this.localize('loading', 'tool'),
					width : 300,
					store : this.getApplication().getStopListsStore()
				    ,selectOnFocus : true
				    ,displayField: 'label'
				    ,triggerAction: 'all'
				    ,valueField: 'id'
				    ,mode: 'local'
				    ,emptyText: this.localize('none','tool')
				},{
					xtype : 'combo',
					id : 'comparisonCorpus',
					value : this.getStore().baseParams.comparisonCorpus,
					fieldLabel : '<span ext:qtip="'
							+ this.localize('comparisonCorpusTip') + '">'
							+ this.localize('comparisonCorpus') + '</span>',
					loadingText : this.localize('loading', 'tool'),
					width : 300,
					store : this.getApplication().getCorporaStore()
				    ,mode:'local'
				    ,selectOnFocus : true
				    ,displayField: 'label'
				    ,triggerAction: 'all'
				    ,valueField: 'id'
				    ,emptyText: this.localize('none','tool')
				}, {
					xtype : 'numberfield',
					id : 'extendedSortZscoreMinimum',
					value : this.getStore().baseParams.extendedSortZscoreMinimum,
					fieldLabel : '<span ext:qtip="'
							+ this.localize('extendedSortZscoreMinimumTip', 'tool')
							+ '">'
							+ this.localize('extendedSortZscoreMinimum','tool')
							+ '</span>'
				}],
				buttons : [{
					text : this.localize('ok', 'tool'),
					iconCls : 'icon-accept',
					listeners : {
						click : {
							fn : function(btn) {
								var formPanel = btn.findParentByType('form');
								var form = formPanel.getForm();
								var comparisonCorpus = form.findField('comparisonCorpus');
								var stopList = form.findField('stopList');
								
								// make sure we don't have any queries
								if (comparisonCorpus.getValue() && !comparisonCorpus.getRawValue()) {comparisonCorpus.setValue('');}
								else if (comparisonCorpus.lastQuery && comparisonCorpus.lastQuery!=comparisonCorpus.getValue()) {
									comparisonCorpus.getStore().loadData({corpora: {corpora: [{id: comparisonCorpus.lastQuery, label: comparisonCorpus.lastQuery, description: ''}]}}, true);
									comparisonCorpus.setValue(comparisonCorpus.lastQuery)
								}
								if (stopList.getValue() && !stopList.getRawValue()) {stopList.setValue('');}
								else if (stopList.lastQuery && stopList.lastQuery!=stopList.getValue()) {
									stopList.getStore().loadData({stopLists: {lists: [{id: stopList.lastQuery, label: stopList.lastQuery, description: ''}]}}, true);
									stopList.setValue(stopList.lastQuery)
								}

								if (form.isDirty()) {
									var store = this.getStore();
									var baseParams = store.baseParams;
									baseParams.comparisonCorpus = form.findField('comparisonCorpus').getValue();
									baseParams.extendedSortZscoreMinimum = form.findField('extendedSortZscoreMinimum').getValue();
									baseParams.stopList = stopList.getValue();
									if (store.lastOptions) {Ext.apply(store.lastOptions.params, baseParams)}
									store.reload();
								}
								formPanel.findParentByType('window').destroy();
							},
							scope : this
						}
					}
				}, {
					text : this.localize('cancel', 'tool'),
					handler : function(btn) {
						btn.findParentByType('window').destroy();
					}
				}, {
					text : this.localize('restore', 'tool'),
					listeners : {
						click : {
							fn : function(btn) {
								var form = btn.findParentByType('form').getForm();
								form.findField('comparisonCorpus').setRawValue(this.defaultComparisonCorpus);
								form.findField('extendedSortZscoreMinimum').setValue(this.defaultExtendedSortZscoreMinimum);
								form.findField('stopList').setValue(this.stopList);
							},
							scope : this
						}
					}
	
				}]
			}]
		})

	}

	// private localization variables
	,
	i18n : {
		title : {
			en : "Words in the Entire Corpus"
		},
		help : {
			en : "This tool shows overall word frequencies for the entire corpus as well as information about how word frequencies are spread out over documents within the corpus. Hover over column headers and buttons for more information."
		},
		type : {
			en : "Word"
		},
		typeTip : {
			en : "The normalized word found in the corpus."
		},
		rawFreq : {
			en : "Count"
		},
		rawFreqTip : {
			en : "The raw count of this word in the entire corpus."
		},
		rawZscore : {
			en : "Z-Score"
		},
		rawZscoreTip : {
			en : "This is the z-score – or <a href='http://en.wikipedia.org/wiki/Standard_score' target='_blank'>standard score</a> – of the total raw frequencies for the type in all documents, compared to other types; it is a normalized version of the raw frequency, showing the number of standard deviations a value is above or below the mean of type frequencies."
		},
		rawZscoreDifferenceCorpusComparison : {
			en : "Difference"
		},
		rawZscoreDifferenceCorpusComparisonTip : {
			en : "This is the difference between the z-score of the word in this corpus and the z-score of the word in the comparison corpus. Positive values mean the word is more frequent in the document than in the corpus as a whole). See the options for defining the comparison corpus. The z-score – <a href='http://en.wikipedia.org/wiki/Standard_score' target='_blank'>standard score</a> is a normalized version of the raw frequency, showing the number of standard deviations a value is above or below the mean of type frequencies."
		},
		relativeMean : {
			en : "Mean"
		},
		relativeMeanTip : {
			en : "The average of the relative frequencies (per 10,000 words) for each document in the corpus."
		},
		relativeStdDev : {
			en : "Std. Dev."
		},
		relativeStdDevTip : {
			en : "The standard deviation of the relative frequencies (per 10,000 words) for each document in the corpus."
		},
		relativeKurtosis : {
			en : "Peakedness"
		},
		relativeKurtosisTip : {
			en : "A measure of the <a href='http://en.wikipedia.org/wiki/Kurtosis' target='_blank'>peakedness</a> (presence of infrequency extreme deviations) of relative frequency values."
		},
		relativeSkewness : {
			en : "Skew"
		},
		relativeSkewnessTip : {
			en : "A measure of the <a href='http://en.wikipedia.org/wiki/Skewness' target='_blank'>asymmetry</a> of relative frequency values for each document in the corpus."
		},
		relativeFreqs : {
			en : "Trend"
		},
		relativeFreqsTip : {
			en : "This graph shows variation in the relative frequencies of a word for each document in a corpus.."
		},
		searchText : {
			en : 'Search'
		},
		selectRowsForResults : {
			en : "Generate results by selecting rows from the <i>Corpus Types</i> tool."
		},
		selectRowsForMoreResults : {
			en : "Select one or more rows to see more results."
		},
		extendedSortInEffect : {
			en : 'A minimum z-score threshold is in effect – see options for more details'
		},
		comparisonCorpus : {
			en : 'Comparison Corpus'
		},
		comparisonCorpusTip : {
			en : 'Define an existing corpus to use for comparison purposes.'
		}
	}
});

Ext.reg('voyeurCorpusTypeFrequenciesGrid',
		Voyeur.Tool.CorpusTypeFrequenciesGrid);

