/**
 * @class Voyeur.Tool.Links A simple panel which loads the Links app.
 * @extends_ext Ext.Panel
 * @extends Voyeur.Tool
 * @namespace Voyeur.Tool
 * @author Andrew MacDonald
 * @since 1.0
 */
Voyeur.Tool.Links = Ext.extend(Ext.Panel, {
    initialized: false,
    getLinksObjectId : function() {return this.id.replace(/-/g,'_')+'_links';},
    // tracks the document ids that have been added
    docIds: [],
    // configuration for the flash app
    flashvars : {
        bridgeName: 'links',
        autoFit: true,
        valueSizing: true,
        removeOrphans: true,
        groupsOpen: true
    },
    initLinks: function() {
        if (!this.initialized) {
            var id = this.getLinksObjectId();
var scripts = '<script type="text/javascript">'+
'function linksClickHandler(word, docIds, event) {'+
'if (window.console && console.info) console.info(word, event.type);'+
'var linksTool = Ext.ComponentMgr.all.find(function(object){if (object.xtype==\'voyeurLinks\') {return true;} else {return false;}});'+
'linksTool.linksClickHandler(word, docIds, event);'+
'}'+
'</script>';
            this.body.update(scripts+'<div id="'+id+'"></div>', true);
            var params = {
                menu: "false",
                scale: "noScale",
                allowFullscreen: "true",
                allowScriptAccess: "always",
                bgcolor: "#FFFFFF",
                wmode: 'opaque'
            };
            var attributes = {
                id: id,
                name: id
            };
            swfobject.embedSWF(this.getApplication().getBaseUrl()+"resources/lib/links/Links.swf", id,
                '100%', '100%', "10.0.0", "expressInstall.swf", this.flashvars, params, attributes);
            this.initialized = true;
        }
    },
    linksClickHandler: function(word, docIds, event) {
        if (event.type == 'doubleClick') {
            this.addWord(word);  // use add word so we can get frequency info first
            
            if (docIds == null) Voyeur.application.dispatchEvent('corpusTypeSelected', this, {type: word});
            else {
                var docIdTypes = [];
                for (var i = 0; i < docIds.length; i++) {
                    docIdTypes.push(this.unscrubId(docIds[i])+':'+word);
                }
                Voyeur.application.dispatchEvent('documentTypesSelected', this, {docIdType: docIdTypes});
            }
        }
    },
    constructor : function(config) {
    	if (config.flashvars) {
    		Ext.apply(this.flashvars, config.flashvars);
    		delete config.flashvars;
    	}
        Ext.apply(this, new Voyeur.Tool(config, this));
        
        // add exporter for image
        this.exporters.img = this.localize('exportImg');
        
        Ext.applyIf(config, {
            tbar: [{
                xtype: 'typeSearch',
                width: 100,
                parentTool: this,
                emptyText: this.localize('findTerm'),
                listeners: {
            	    typeSelected: function(combo, type, record) {
                		this.addWord(type);
            	    },
            	    scope: this
                }
            },'->',{
            	xtype: 'button',
            	text: this.localize('clearTerms'),
            	handler: function() {
            		var linksApp = FABridge.links.root();
            		linksApp.removeAllItems(false);
            	},
            	scope: this
            }]
        });
        
        Voyeur.Tool.Links.superclass.constructor.apply(this, arguments);

        this.addListener('afterrender', function(src, params) {
            this.initLinks();
        }, this);
        
        /**
		 * @event CorpusSummaryResultLoaded
		 * @type listener
		 */
        this.addListener('CorpusSummaryResultLoaded', function(src, params) {
            this.initLinks();
            var me = this;
            FABridge.addInitializationCallback('links', function() {
                me.getCorpus().getDocuments().each(function(doc, index) {
                    me.addDocument(doc.getId());
                }, me);
            	me.addInitialContent(params);
            });
        }, this);
        
        /**
		 * @event CorpusTypeFrequenciesResultLoaded
		 * @type listener
		 */
        this.addListener('CorpusTypeFrequenciesResultLoaded', function(src, data) {
        	if (src == this) {
        		this.handleCorpusTypeData(this.corpusTypeReader.readRecords(data).records);
        	}
        }, this);
        
        /**
		 * @event DocumentTypeFrequenciesResultLoaded
		 * @type listener
		 */
        this.addListener('DocumentTypeFrequenciesResultLoaded', function(src, data) {
        	if (src == this) {
        		this.handleDocumentTypeData(this.documentTypeReader.readRecords(data).records);
        	}
        }, this);
        
        /**
		 * @event DocumentTypeCollocateFrequenciesResultLoaded
		 * @type listener
		 */
        this.addListener('DocumentTypeCollocateFrequenciesResultLoaded', function(src, data) {
        	if (src == this) {
        		this.handleTypeCollocateData(this.documentTypeCollocatesReader.readRecords(data).records);
        	}
        }, this);
        
        /**
		 * @event corpusDocumentSelected
		 * @type listener
		 */
        this.addListener('corpusDocumentSelected', function(src, params) {
            var urlParams = Ext.urlDecode(location.search.substring(1));
            var corpus = urlParams.corpus;
//            this.addDocument(corpus, params.docId);
        }, this);
        
        /**
		 * @event corpusTypeSelected
		 * @type listener
		 */
        this.addListener('corpusTypeSelected', function(src, params) {
            if (src != this) {
            	this.addWord(params.type);
            }
        }, this);
        
        /**
		 * @event documentTypeSelected
		 * @type listener
		 */
        this.addListener('documentTypeSelected', function(src, params) {
            var wordParams = params.docIdType.split(':');
            this.addWord(wordParams[1]);
        }, this);
        this.addListener('export', function(exp) {
            if (exp=='img') {
                var img_win = window.open('', 'Charts: Export as Image');
                var linksApp = FABridge.links.root();
                var img_data = linksApp.getImageBinary();
                with(img_win.document) {
                    write('<html><head><title>'+this.localize('title')+'<\/title><\/head><body>' + 
                            "<img src='data:image/png;base64," + img_data + "' /><br />"+this.getFooterText()+"<p>"+this.localize("exportImgSrc")+"</p><tt>&lt;img src=\"data:image/png;base64,"+img_data+"\" alt=\"\" /&gt;</tt><\/body><\/html>")
                }
                img_win.document.close(); // stop the 'loading...' message
                img_win.focus();
            }
        }, this);
    }
    
    ,addInitialContent : function(params) {
    	var params = this.getApiParams();

    	var linksApp = FABridge.links.root();
    	linksApp.setGroupsName('Documents');

        if (params.docId || params.docIndex) {
        	if (params.docIndex) { // convert to docId
        		params.docId = [];
        		var corpus = this.getCorpus();
        		if (typeof params.docIndex == "string") {
        			params.docId.push(corpus.getDocument(parseInt(params.docIndex)).getId());
        		}
        		else {
        			for (var i=0;i<params.docIndex.length;i++) {
        				var id = corpus.getDocument(parseInt(params.docIndex[i])).getId();
        				params.docId.push(id);
        			}
        		}
        		delete params.docIndex;
        	}
        	
        	// convert to array
        	if (typeof params.docId == 'string') {
        		params.docId = [params.docId];
        	}
        	
//        	for (var i=0;i<params.docId.length;i++) {
//            	this.addDocument(params.docId[i]);
//        	}
        	
        	//params.sortBy = 'rawZscoreCorpusDelta';
        	params.sortDirection = 'DESC';
            params.extendedSortZscoreMinimum = 1;
            this.fetch('DocumentTypeFrequencies', params);
        } else {
        	params.corpus = this.getApiParamValue('corpus');
            params.limit = 5;
            params.sortBy = 'rawFreq';
            params.start = 0;
            params.sortDirection = 'DESC';
            params.extendedSortZscoreMinimum = 1;
            this.fetch('CorpusTypeFrequencies', params);
        }
    }
    
    ,addDocument : function(docId) {
        if (this.docIds.indexOf(docId) == -1) {
            this.docIds.push(docId);
            var doc = this.getCorpus().getDocuments().get(docId);
            var linksApp = FABridge.links.root();
            linksApp.addGroup({
                groupId: this.scrubId(docId),
                label: doc.getShortTitle()
            });
        }
    }
    
    ,addWord : function(word) {
        var params = {
            type: word,
//            sortBy: 'rawZscoreCorpusDelta',
            extendedSortZscoreMinimum: 1
        };
        this.fetch('CorpusTypeFrequencies', params);
    }
    
    ,removeWord : function(word) {
        var linksApp = FABridge.links.root();
        linksApp.removeItem(word);
        linksApp.anchorGraph();
    }
    
    ,getCollocates : function(word) {
    	var docIdTypes = [];
        for (var i = 0; i < this.docIds.length; i++) {
        	docIdTypes.push(this.docIds[i]+':'+word);
        }
        var params = {
            corpus: this.getCorpus().getId(),
            docIdType: docIdTypes,
            limit: 3 * this.docIds.length
        };
        this.fetch('DocumentTypeCollocateFrequencies', params);
    }
    
    ,handleCorpusTypeData : function(records) {
        // find document with highest relative frequency for top corpus type
    	var corpus = this.getCorpus();
    	var corpusId = corpus.getId();
    	var limit = this.getApiParamValue('limit');
    	var words = [];
    	var types = [];
    	var docIds = corpus.getDocuments().keys;
    	for (var h=0;h<records.length;h++) {
    		var record = records[h];
    		var type = record.get('type');
    		var rawFreq = record.get('rawFreq');
    		var max = record.get('relativeMax');
	        var relativeFreqs = record.get('relativeFreqs');
	        types.push(type);
//    		for (var i = 0; i < docIds.length; i++) {
//    			words.push({
//    				word: type,
//    				groupId: docIds[i],
//    				value: rawFreq
//    			});
//    		}
	        for (var i = 0; i < relativeFreqs.length; i++) {
	            if (relativeFreqs[i] == max) {
	            	var docId = corpus.getDocument(i).getId();
	            	words.push({
	    				word: type,
	    				groupId: docId,
	    				value: rawFreq
	    			});
	                this.fetch('DocumentTypeCollocateFrequencies', {
	                	corpus: corpusId,
	                	limit: limit,
	                	docIdType: docId+':'+type
	                });
	                break;
	            }
	        }
    	}
    	this.sendWords(words);
//    	for (var h=0; h<types.length; h++) {
//    		this.getCollocates(types[h]);
//    	}
    }
    
    ,handleDocumentTypeData : function (records) {
        var words = [];
        var limit = Math.max(1, Math.ceil(10 / this.getCorpus().getSize()));
        for (var i = 0; i < records.length; i++) {
            var type = records[i].get('type');
            var docId = records[i].get('docId');
            var rawFreq = records[i].get('rawFreq');
            if (rawFreq > 0) {
                words.push({
                    word: type,
                    groupId: docId,
                    value: rawFreq
                });
                
                var params = {
                    corpus: this.getCorpus().getId(),
                    docIdType: docId+':'+type,
                    limit: limit
                    
                };
                this.fetch('DocumentTypeCollocateFrequencies', params);
            }
        }
        this.sendWords(words);
    }
    
    ,handleTypeCollocateData : function (records) {
        var words = [];
        for (var i = 0; i < records.length; i++) {
        	var r = records[i];
            words.push({
                word: r.get('type'),
                groupId: r.get('docId'),
                value: r.get('rawFreq'),
                parent: r.get('keyword')
            });
        }
        this.sendWords(words);
    }
    
    ,scrubId: function(id) {
    	return id.replace(/-/g,"__dash__").replace(/\./g,"__dot__").replace(/'/g,"__apos__");
    }
    
    ,unscrubId: function(id) {
    	return id.replace(/__dash__/g,"-").replace(/__dot__/g,".").replace(/__apos__/g,"'");
    }

    ,sendWords: function(words) {
    	for (var i=0;i<words.length;i++) {
    		// handle character that are problematic
    		var word = words[i];
    		if (typeof word.groupId == 'string') {
    			word.groupId = [word.groupId];
    		}
    		for (var j=0;j<word.groupId.length;j++) {
    			word.groupId[j] = this.scrubId(word.groupId[j]);
    		}
    	}
        var linksApp = FABridge.links.root();
        linksApp.addItems(words);
        linksApp.anchorGraph(1000); // anchor graph 1 second after adding items
    }
    
    /**
     * Fetch Voyeur results using the specified tool and the provided params (and other parameters as needed).
     * @params {String} tool the tool to fetch data (CorpusTypeFrequencies or DocumentTypeFrequencies)
     * @params {Object} params parameters to be sent to the tool (others may be provided by default if not specified).
     * @private
     */
    ,fetch : function(tool,params) {
    	var apiParams = this.getApiParams();
        if (apiParams.stopList) {params.stopList = apiParams.stopList}
        this.update({tool: tool, params: params, title: tool, renderTo: this.body});
    }
    
    ,corpusTypeReader : new Ext.data.JsonReader({
        root : 'corpusTypes.types'
        ,totalProperty : 'corpusTypes["@totalTypes"]'
    }, Ext.data.Record.create(Voyeur.data.CorpusTypes.fields))
    ,documentTypeReader : new Ext.data.JsonReader({
        root : 'documentTypes.types'
        ,totalProperty : 'documentTypes["@totalTypes"]'
    }, Ext.data.Record.create(Voyeur.data.DocumentTypes.fields)) 
    ,documentTypeCollocatesReader : new Ext.data.JsonReader({
        root : 'documentTypeCollocateFrequencies.types'
        ,totalProperty : 'documentTypeCollocateFrequencies["@totalTypes"]'
    }, Ext.data.Record.create(Voyeur.data.DocumentTypeCollocates.fields))
    
    ,showOptions : function() {
        this.showOptionsWindow({
        	width: 480,
            items : [{
                xtype : 'form',
                labelWidth : 150,
                labelAlign : 'right',
                border : false,
                items : [{
                    xtype: 'radiogroup',
                    id: 'sizing',
                    fieldLabel: '<span ext:qtip="'
                            + this.localize('sizingTip') + '">'
                            + this.localize('sizing') + '</span>',
                    width : 250,
                    items: [
                        {boxLabel: this.localize('valueSizing'), name: 'sizing', inputValue: 'value', checked: this.flashvars.valueSizing == true},
                        {boxLabel: this.localize('linkSizing'), name: 'sizing', inputValue: 'link', checked: this.flashvars.valueSizing != true}
                    ]
                },{
                    xtype: 'checkbox',
                    id: 'autoFit',
                    checked: this.flashvars.autoFit,
                    fieldLabel: '<span ext:qtip="'
                            + this.localize('autoFitTip') + '">'
                            + this.localize('autoFit') + '</span>'
                },{
                    xtype: 'checkbox',
                    id: 'removeOrphans',
                    checked: this.flashvars.removeOrphans,
                    fieldLabel: '<span ext:qtip="'
                            + this.localize('removeOrphansTip') + '">'
                            + this.localize('removeOrphans') + '</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 stopList = form.findField('stopList');
                                var global = form.findField('globalStopWords').getValue();
                                var sizing = form.findField('sizing');
                                var autoFit = form.findField('autoFit');
                                var removeOrphans = form.findField('removeOrphans');
                                
                                // make sure we don't have any queries
                                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()) {
                                	this.setApiParams({
                                		stopList: stopList.getValue()
                                	});
                                    var linksApp = FABridge.links.root();
                                    this.flashvars.valueSizing = sizing.getValue().inputValue == 'value';
                                    this.flashvars.autoFit = autoFit.checked;
                                    this.flashvars.removeOrphans = removeOrphans.checked;
                                    linksApp.setValueSizing(this.flashvars.valueSizing);
                                    linksApp.setAutofit(autoFit.checked);
                                    linksApp.setRemoveOrphans(removeOrphans.checked);
                                    
                                }
                                formPanel.findParentByType('window').destroy();
                            },
                            scope : this
                        }
                    }
                }, {
                    text : this.localize('cancel', 'tool'),
                    handler : function(btn) {
                        btn.findParentByType('window').destroy();
                    }
                }]
            }]
        }, true);
    }
    
    
    ,api: {
    	/**
    	 * @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 5
	     */
    	,limit: {'default': 5}
    	/**
    	 * @property extendedSortZscoreMinimum The minimum extended sort Z score that each result should have.
    	 * @type Number
    	 * @default 1
    	 */
        ,extendedSortZscoreMinimum: {'default': 1}
        /**
         * @property docId The document ID to restrict results to.
         * @type String
         * @default null
         */
        ,docId: {'default': null}
        /**
         * @property docIndex The document index to restrict results to.
         * @type Integer
         * @default null
         */
        ,docIndex: {'default': null}
        /**
         * @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 sortBy The property to sort results by.
         * @type String
         * @default relativeCollocateRatio
         */
        ,sortBy: {'default': 'relativeCollocateRatio'}
        /**
         * @property sortDirection The direction to sort results in.
         * @type String
         * @default ASC
         * @choices ASC, DESC
         */
        ,sortDirection: {'default': 'DESC'}
        ,toolType: ['Visualization']
        ,listeners: ['CorpusSummaryResultLoaded', 'CorpusTypeFrequenciesResultLoaded', 'DocumentTypeFrequenciesResultLoaded', 'DocumentTypeCollocateFrequenciesResultLoaded',  'corpusDocumentSelected', 'corpusTypeSelected', 'documentTypeSelected']
    }
    
    ,thumb: {
    	large: 'Links.png'
    }
    
    ,i18n : {
        title : {en: "Collocate Clusters"}
        ,type : {en: "Visualization"}
        ,help: {
            en: "This tool finds collocates for words and displays links between them using a force directed graph.<br/><br/><b>Controls</b><br/>"+
            "Double-click a word to find its collocates.<br/>Right-click a word to remove it, or make it \"sticky\".<br/>Right-click an empty area to save the graph to an image file."
        }
        ,findTerm : {en: 'Find Term'}
        ,clearTerms : {en: 'Clear Terms'}
        ,adaptedFrom: {
            en: "This tool is based on the <a href=\"http://taporware.mcmaster.ca/~taporware/otherTools/viscollocator.shtml\" target=\"_blank\">Visual Collocator</a>, and uses the <a href=\"http://mark-shepherd.com/blog/springgraph-flex-component/\" target=\"_blank\">SpringGraph</a> Flex component."
        }
        ,sizing: {
            en: "Node size determined by" 
        }
        ,sizingTip: {
            en: "Select either type frequencies or node links (number of collocates) to determine the size of each node."
        }
        ,valueSizing: {
            en: "Type frequency"
        }
        ,linkSizing: {
            en: "Node links" 
        }
        ,autoFit: {
            en: "Autofit graph on screen" 
        }
        ,autoFitTip: {
            en: "If checked, the graph will scroll and zoom automatically, in order to contain all nodes within the viewing area."
        }
        ,removeOrphans: {
            en: "Remove orphans" 
        }
        ,removeOrphansTip: {
            en: "If checked, nodes with no links will be automatically removed."
        }
        ,exportImg: {en : 'a static image in a new window'}
        ,exportImgSrc: {en: 'HTML source code for this image:'}
    }
});

Ext.reg('voyeurLinks', Voyeur.Tool.Links);

