/**
 * @class Voyeur.Tool.CorpusTypeFrequenciesGrid A panel for displaying type frequencies across the corpus in a tabular format.
 * @namespace Voyeur.Tool
 * @extends_ext Ext.grid.GridPanel
 * @extends Voyeur.Tool
 * @author Stéfan Sinclair
 * @since 0.1.1
 */
Voyeur.Tool.CorpusTypeFrequenciesGrid = Ext.extend(Ext.grid.GridPanel, {
	
	constructor : function(config) {

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

		var store = new Ext.data.JsonStore({
			root : 'corpusTypes.types',
			totalProperty : 'corpusTypes["@totalTypes"]',
			remoteSort : true,
			fields : Voyeur.data.CorpusTypes.fields,
			sortInfo : {field : this.getApiParamValue('sortBy'), direction : this.getApiParamValue('sortDirection')},
			proxy : new Ext.data.HttpProxy({
						url : this.getTromboneUrl(),
						timeout : 60000
					}),
			listeners : {
				'beforeload' : {
					fn : function(store, options) {
						Ext.applyIf(options.params, this.getApiParams());
						if (!options.params.corpus) {return false;}
						Ext.apply(options.params, {tool: 'CorpusTypeFrequencies'});
					},
					scope : this
				},
				'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) {
						this.alertError(response.responseText);
					},
					scope : this
				}
			}
		})

		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(new Ext.ux.grid.Favs({exporter: 'type', align: 'left'}))

		var sm = new Ext.grid.CheckboxSelectionModel()

		var panel = this;

		this.pagingToolBar = new Ext.PagingToolbar({
			store : store,
            enableOverflow: true,
			pageSize : this.getApiParamValue('limit'),
			displayInfo: true,
			displayMsg : "{0}-{1} of {2}",
			items: [{
                xtype: 'typeSearch',
                width: 70,
                parentTool: this,
                listeners: {
	        	    typeSelected: function(combo, type, record) {
	        	    	this.setApiParams({type: null, query: type})
	        	    	this.getStore().load({})
//						this.update({params: this.getApiParams(), tool: 'CorpusTypeFrequencies'});
	        	    },
	        	    scope: this
            	}
        	}],
			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();}
					tb.doLayout();
				}
			}
		});
				
		var panel = this;

		
		var visibleColumn =this.getApiParamValue('visibleColumn');
		
		Ext.applyIf(config, {
			viewConfig : config.viewConfig,
			iconCls : 'table',
			loadMask : true,
			stripeRows : true,
			autoExpandColumn : this.getId()+'-column-type',
			sm: new Ext.grid.CheckboxSelectionModel({
				listeners: {
					selectionchange: {
						fn: function() {this.fireSelectionChange();}
						,scope: this
					}
				}

			}),
			colModel : new Ext.grid.ColumnModel(
				{
					columns: [sm, {
						header : this.localize('type'),
						dataIndex : 'type',
						sortable : true,
						tooltip : this.localize('typeTip'),
						hidden: visibleColumn.indexOf('type')==-1,
						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'),
						hidden: visibleColumn.indexOf('rawFreq')==-1,
						renderer : Ext.util.Format.numberRenderer('0,000')
						,width: 100
					}, {
						header : this.localize('rawZscore'),
						dataIndex : 'rawZscore',
						sortable : true,
						tooltip : this.localize('rawZscoreTip'),
						hidden: visibleColumn.indexOf('rawZscore')==-1,
						renderer : Ext.util.Format.numberRenderer('0,000.00')
						,width: 100
					}, {
						header : this
								.localize('rawZscoreDifferenceCorpusComparison'),
						dataIndex : 'rawZscoreDifferenceCorpusComparison',
						sortable : true,
						tooltip : this
								.localize('rawZscoreDifferenceCorpusComparisonTip'),
						hidden: visibleColumn.indexOf('rawZscoreDifferenceCorpusComparison')==-1,
						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('relativeDistributionMean'),
						dataIndex : 'relativeDistributionMean',
						sortable : true,
						tooltip : this.localize('relativeDistributioneanTip'),
						hidden: visibleColumn.indexOf('relativeDistributionMean')==-1,
						renderer : function(val) {return Ext.util.Format.number(val*10000,'0,000.0')}
						,width: 100
					}, {
						header : this.localize('relativeDistributionStdDev'),
						dataIndex : 'relativeDistributionStdDev',
						sortable : true,
						tooltip : this.localize('relativeDistributionStdDevTip'),
						hidden: visibleColumn.indexOf('relativeDistributionStdDev')==-1,
						renderer : function(val) {
							return isNaN(val) ? '–' : Ext.util.Format.number(val,'0,000.000')
						}
						,width: 100
					}, {
						header : this.localize('relativeDistributionKurtosis'),
						dataIndex : 'relativeDistributionKurtosis',
						sortable : true,
						tooltip : this.localize('relativeDistributionKurtosisTip'),
						hidden: visibleColumn.indexOf('relativeDistributionKurtosis')==-1,
						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('relativeDistributionSkewness'),
						dataIndex : 'relativeDistributionSkewness',
						sortable : true,
						tooltip : this.localize('relativeDistributionSkewnessTip'),
						hidden: visibleColumn.indexOf('relativeDistributionSkewness')==-1,
						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'),
						hidden: visibleColumn.indexOf('relativeFreqs')==-1,
						width : 100,
						renderer : function(val, cell, record, rowIndex, colIndex) {
							return panel.getSparkLine(val, panel.getColumnModel().getColumnWidth(colIndex))
						}
					}]
					,listeners: {
						hiddenchange: {
							// FIXME this code moved from Voyeur.Tool as being too specific
							// also in DocumentTypeCollocateFrequenciesGrid
							fn: function(cm) {
								var columns = [];
								var size = cm.getColumnCount();
								var dataIndex;
								for (var i=0;i<size;i++) {
									if (!cm.isHidden(i)) {
										dataIndex = cm.getDataIndex(i);
										if (dataIndex) {columns.push(cm.getDataIndex(i));}
									}
								}
								this.setApiParams({visibleColumn: columns});
								if (this.store && this.store.groupField) {this.setApiParams({groupBy: this.store.groupField})}
							}
							,scope: this
						}
					}
				}),
			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('rowclick', function(src, grid, rowIndex, e) {
			this.fireSelectionChange();
			return true;
		}, this);
	

		/**
		 * @event CorpusTypeFrequenciesRequest
		 * @type listener
		 */
		this.addListener('CorpusTypeFrequenciesRequest', function(src, params) {
			this.setApiParams(params);
			if (params.sortBy) {
				this.getStore().setDefaultSort(params.sortBy, 'DESC');
			}
			else {this.getStore().setDefaultSort(this.getApiParamDefaultValue('sortBy','DESC'))}
			this.getStore().load({params: params});
		}, this);
		
		/**
		 * @event CorpusSummaryResultLoaded
		 * @type listener
		 */
		this.addListener('CorpusSummaryResultLoaded', function(src, data) {
//			this.getStore().baseParams.corpus = data.corpus['@id'];
			var size = this.getCorpus().getSize();
			if (size>100) {
				var cm = this.getColumnModel();
				var ind = cm.findColumnIndex('relativeFreqs');
				cm.setColumnWidth(ind, size);
			}
			if (this.rendered) {this.fireEvent('afterrender', this);}
			
		}, this);
		
		this.addListener('afterrender', function(panel) {
			if (this.getCorpus().getSize()==0) {return;}

			// make sure sort field is visible
			var cm = this.getColumnModel();
			var ind = cm.findColumnIndex(this.getApiParamValue('sortBy'));
			cm.setHidden(ind, false);
			
			// make sure query term is set
			if (this.getApiParamValue('query')) {
				this.search.field.setValue(this.getApiParamValue('query'));
			}
			
			this.getStore().load();
			/*
			if (params.query) {
				this.search.field.setValue(params.query);
			}
			*/
		}, 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) {
					/**
					 * @event corpusTypeSelected
					 * @param {Voyeur.Tool.CorpusTypeFrequenciesGrid} tool
					 * @param {Object} params <ul>
					 * <li><b>type</b> : String</li>
					 * <li><b>record</b> : Ext.data.Record</li>
					 * </ul>
					 * @type dispatcher
					 */
					Voyeur.application.dispatchEvent('corpusTypeSelected', this,  {type : types[0], record: records[0]});
				} else if (types.length > 1) {
					/**
					 * @event corpusTypesSelected
					 * @param {Voyeur.Tool.CorpusTypeFrequenciesGrid} tool
					 * @param {Object} params <ul>
					 * <li><b>type</b> : Array</li>
					 * <li><b>record</b> : Array</li>
					 * </ul>
					 * @type dispatcher
					 */
					Voyeur.application.dispatchEvent('corpusTypesSelected', this, {type : types, record: records});
				}
			}
		}, 1000);
	},

	lastSelectionTime : 0

	,showOptions : function() {
		this.showOptionsWindow({
			items : [{
				xtype : 'form',
				labelWidth : 150,
				labelAlign : 'right',
				border : false,
				items : [{
					xtype : 'combo',
					id : 'comparisonCorpus',
					value : this.getApiParamValue('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.getApiParamValue('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');
								var global = form.findField('globalStopWords').getValue();
								
								// 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 compCorpus = form.findField('comparisonCorpus').getValue();
									this.setApiParams({
										comparisonCorpus: compCorpus ? form.findField('comparisonCorpus').getValue() : null
										,extendedSortZscoreMinimum: form.findField('extendedSortZscoreMinimum').getValue()
										,stopList: stopList.getValue()
									});
									if (global) {
										this.getApplication().applyParamsGlobally({
											stopList: this.getApiParamValue('stopList')
										}, true);
									}
									else {
										this.getStore().load();
									}
								}
								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.getApiParamDefaultValue('comparisonCorpus'));
								form.findField('extendedSortZscoreMinimum').setValue(this.getApiParamDefaultValue('extendedSortZscoreMinimum'));
								form.findField('stopList').setValue(this.getApiParamDefaultValue('stopList'));
							},
							scope : this
						}
					}
	
				}]
			}]
		}, true);

	}
	
	,api: {
		/**
		 * @property stopList The stop list to use to filter results.
		 * Choose from a pre-defined list, or enter a comma separated list of words, or enter an URL to a list of stop words in plain text (one per line).
		 * @type String
		 * @default null
		 * @choices stop.en.taporware.txt, stop.fr.veronis.txt
		 */
		stopList: {
			'default': null,
			'choices': ['stop.en.taporware.txt', 'stop.fr.veronis.txt']
		}
		/**
		 * @property comparisonCorpus The ID of a corpus to compare results against.
		 * @type String
		 * @default null
		 */
		,comparisonCorpus: {'default': null}
		/**
		 * @property start The index in the results to start at.
		 * @type Integer
		 * @default 0
		 */
		,start: {'default': 0}
		/**
		 * @property limit The number of words to return in each call.
		 * @type Integer
		 * @default 50
		 */
		,limit: {'default': 50}
		/**
		 * @property sortBy The property to sort results by.
		 * @type String
		 * @default rawFreq
		 */
		,sortBy: {'default': 'rawFreq'}
		/**
		 * @property sortDirection The direction to sort results in.
		 * @type String
		 * @default DESC
		 * @choices ASC, DESC
		 */
		,sortDirection: {'default': 'DESC'}
		/**
		 * @property extendedSortZscoreMinimum The minimum extended sort Z score that each result should have.
		 * @type Number
		 * @default null
		 */
		,extendedSortZscoreMinimum: {'default': null}
		/**
		 * @property query A string to search for in the corpus.
		 * @type String
		 * @default null
		 */
		,query: {'default': null}
		/**
		 * @property visibleColumn The list of columns which are visible.
		 * @type Array
		 * @default ['type','rawFreq','relativeFreqs']
		 * @choices type, rawFreq, rawZscore, rawZscoreDifferenceCorpusComparison, relativeDistributionMean, relativeDistributionStdDev, relativeDistributionKurtosis, relativeDistributionSkewness, relativeFreqs
		 */
		,visibleColumn: {'default': ['type','rawFreq','relativeFreqs']}
		,toolType: ['Table', 'Corpus']
		,listeners: ['CorpusSummaryResultLoaded', 'CorpusTypeFrequenciesRequest']
		,dispatchers: ['corpusTypeSelected', 'corpusTypesSelected']
	}
	
	,thumb : {
		large: 'CorpusTypeFrequenciesGrid.png'
	}

	// 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 : "Frequencies"
		},
		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."
		},
		relativeDistributionMean : {
			en : "Mean"
		},
		relativeDistributionMeanTip : {
			en : "The average of the relative frequencies (per 10,000 words) for each document in the corpus."
		},
		relativeDistributionStdDev : {
			en : "Std. Dev."
		},
		relativeDistributionStdDevTip : {
			en : "The standard deviation of the relative frequencies (per 10,000 words) for each document in the corpus."
		},
		relativeDistributionKurtosis : {
			en : "Peakedness"
		},
		relativeDistributionKurtosisTip : {
			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."
		},
		relativeDistributionSkewness : {
			en : "Skew"
		},
		relativeDistributionSkewnessTip : {
			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);


