
/**
 * @class Links 
 * @description A simple panel which loads the Links app
 * @extends Ext.Panel
 * @extends Voyeur.Tool
 * @author Andrew MacDonald
 * @since 1.0
 */
Voyeur.Tool.Links = Ext.extend(Ext.Panel, {
    initialized: false,
    docIds: [],
    // base params for collocate calls
    baseParams: {
        stopList: 'stop.en.smart.txt',
        sortBy: 'relativeRatio',
        sortDirection: 'DESC',
        start: 0,
        limit: 5
    },
    getLinksObjectId : function() {return this.id.replace(/-/g,'_')+'_links';},
    flashvars : {
        bridgeName: 'links',
        autoFit: true,
        valueSizing: true,
        removeOrphans: 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.getCollocates(word);
//            this.addWord(word);
        }
/*        else if (event.type == 'doubleClick') {
            if (docIds == null) Voyeur.application.dispatchEvent('corpusTypeSelected', this, {type: word});
            else {
                var docIdTypes = [];
                for (var i = 0; i < docIds.length; i++) {
                    docIdTypes.push(docIds[i]+':'+word);
                }
                Voyeur.application.dispatchEvent('documentTypesSelected', this, {docIdType: docIdTypes});
            }
        }*/
    },
    constructor : function(config) {
        Ext.apply(this, new Voyeur.Tool(config, Voyeur.Tool.Links.prototype.i18n));
        Ext.applyIf(config, {
            tbar: [{
                xtype: 'textfield',
                emptyText: 'Type a word',
                regex: /^[^\s]+$/,
                regexText: 'You must enter exactly one word.',
                enableKeyEvents: true,
                listeners: {
                    keypress: {
                        fn: function(textfield, event) {
                            if (event.getKey() == 13) {
                                if (textfield.isValid()) {
                                    var word = textfield.getValue();
                                    this.addWord(word);
                                }
                            }
                        },
                        scope: this
                    }
                }
            },{
                xtype: 'button',
                text: 'Add Word',
                handler: function(button, event) {
                    var textfield = button.ownerCt.findByType('textfield')[0];
                    if (textfield.isValid()) {
                        var word = textfield.getValue();
                        this.addWord(word);
                    }
                },
                scope: this
            }]
        });
        
        Voyeur.Tool.Links.superclass.constructor.apply(this, arguments);

        this.addListener('afterrender', function(src, params) {
            this.initLinks();
        }, this);
        this.addListener('LinksBootstrap', function(src, params) {
            this.update({tool: 'CorpusSummary', params: params, title: 'CorpusSummary'});
            this.initLinks();
            var me = this;
            FABridge.addInitializationCallback('links', function() {
                me.addInitialContent(params);
            });
        }, this);
        this.addListener('CorpusTypeFrequenciesResultLoaded', function(src, data) {
            this.handleCorpusTypeData(this.corpusTypeReader.readRecords(data).records);
        }, this);
        this.addListener('DocumentTypeFrequenciesResultLoaded', function(src, data) {
            this.handleDocumentTypeData(this.documentTypeReader.readRecords(data).records);
        }, this);
        this.addListener('DocumentTypeCollocateFrequenciesResultLoaded', function(src, data) {
            var records = this.documentTypeCollocatesReader.readRecords(data).records;
            if (records.length == 0) {
                var type = '';
                var params = data.parameters.entries;
                for (var i = 0; i < params.length; i++) {
                    if (params[i][0] == 'docIdType') {
                        type = params[i][1][0].split(':')[1];
                        break;
                    }
                }
                this.removeWord(type);
            } else {
                this.handleTypeCollocateData(records);
            }
        }, this);
        this.addListener('corpusDocumentSelected', function(src, params) {
            var urlParams = Ext.urlDecode(location.search.substring(1));
            var corpus = urlParams.corpus;
            this.addDocument(corpus, params.docId);
        }, this);
        this.addListener('corpusTypeSelected', function(src, params) {
            if (src != this) this.addWord(params.type);
        }, this);
        this.addListener('documentTypeSelected', function(src, params) {
            var wordParams = params.docIdType.split(':');
            this.addWord(wordParams[1]);
        }, this);
    }
    
    ,addInitialContent : function(params) {
        var linksApp = FABridge.links.root();
        linksApp.setGroupsName('Documents');
        
        params.limit = 3;
        if (params.docId) {
            this.addDocument(params.docId);
            this.fetch('DocumentTypeFrequencies', params);
        } else {
            params.limit = 1;
            params.sortBy = 'relativeKurtosis';
            params.start = 0;
            params.direction = 'DESC';
            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: docId,
                label: doc.get('title')
            });
        }
    }
    
    ,addWord : function(word) {
        var params = {
            type: word,
            limit: this.docIds.length,
            bins: 10,
            sortBy: 'rawZscoreCorpusDelta',
            extendedSortZscoreMinimum: 1
        };
        this.fetch('DocumentTypeFrequencies', params);
    }
    
    ,removeWord : function(word) {
        var linksApp = FABridge.links.root();
        linksApp.removeItem(word);
        linksApp.anchorGraph();
    }
    
    ,getCollocates : function(word) {
        for (var i = 0; i < this.docIds.length; i++) {
            var params = {
                corpus: this.getCorpus().getId(),
                docIdType: this.docIds[i]+':'+word
            };
            this.fetch('DocumentTypeCollocateFrequencies', params);
        }
    }
    
    ,handleCorpusTypeData : function (records) {
        // find document with highest relative frequency for top corpus type
        var docIndex = 0;
        var max = records[0].get('relativeMax');
        var relativeFreqs = records[0].get('relativeFreqs');
        for (var i = 0; i < relativeFreqs.length; i++) {
            if (relativeFreqs[i] == max) {
                docIndex = i;
                break;
            }
        }
        this.getCorpus().getDocuments().each(function(doc, index) {
            this.addDocument(doc.get('id'));
        }, this);
        
        var docId = this.getCorpus().getDocument(docIndex).get('id');
        
        // the the top types for the document
        var params = {
            docId: docId,
            limit: 3,
            bins: 10,
            sortBy: 'rawZscoreCorpusDelta',
            extendedSortZscoreMinimum: 1
        };
        this.fetch('DocumentTypeFrequencies', params);
    }
    
    ,handleDocumentTypeData : function (records) {
        var words = [];
        //console.warn(records);
        for (var i = 0; i < records.length; i++) {
            words.push({
                word: records[i].get('type'),
                groupId: records[i].get('docId'),
                value: records[i].get('rawFreq')
            });
            
            var params = {
                corpus: this.getCorpus().getId(),
                docIdType: records[i].get('docId')+':'+records[i].get('type')
            };
            this.fetch('DocumentTypeCollocateFrequencies', params);
        }
        //console.warn(words);
        this.sendWords(words);
    }
    
    ,handleTypeCollocateData : function (records) {
        var words = [];
        //console.warn(records);
        for (var i = 0; i < records.length; i++) {
            words.push({
                word: records[i].get('type'),
                groupId: records[i].get('docId'),
                value: records[i].get('rawFreq'),
                parent: records[i].get('keyword')
            });
        }
        this.sendWords(words);
    }

    ,sendWords: function(words) {
        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).
     */
    ,fetch : function(tool,params) {
        Ext.applyIf(params, this.baseParams);
        this.update({tool: tool, params: params, title: tool})
    }
    
    ,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({
            items : [{
                xtype : 'form',
                labelWidth : 150,
                labelAlign : 'right',
                border : false,
                items : [{
                    xtype : 'combo',
                    id : 'stopList',
                    value : this.baseParams.stopList,
                    fieldLabel : '<span ext:qtip="'
                            + this.localize('stopListTip','tool') + '">'
                            + this.localize('stopList','tool') + '</span>',
                    loadingText : this.localize('loading', 'tool'),
                    width : 180,
                    store : this.getApplication().getStopListsStore()
                    ,selectOnFocus : true
                    ,displayField: 'label'
                    ,triggerAction: 'all'
                    ,valueField: 'id'
                    ,mode: 'local'
                    ,emptyText: this.localize('none','tool')
                },{
                    xtype: 'radiogroup',
                    id: 'sizing',
                    fieldLabel: '<span ext:qtip="'
                            + this.localize('sizingTip') + '">'
                            + this.localize('sizing') + '</span>',
                    width : 200,
                    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 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.baseParams.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();
                    }
                }]
            }]
        })
    }
    
    
    
    
    ,i18n : {
        title : {en: "Links"}
        ,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."
        }
        ,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."
        }
    }
});

Ext.reg('voyeurLinks', Voyeur.Tool.Links);
