/**
 * @class Voyeur.Tool.Cirrus A simple panel which loads the Cirrus app.
 * @namespace Voyeur.Tool
 * @extends_ext Ext.Panel
 * @extends Voyeur.Tool
 * @author Andrew MacDonald
 * @since 1.0
 */
Voyeur.Tool.Cirrus = Ext.extend(Ext.Panel, {
    initialized: false,
    getCirrusObjectId : function() {return this.id.replace(/-/g,'_')+'_cirrus';},
    cirrusApp : null, // the actual canvas/flash app
    
    initCirrus: function(params) {
        var id = this.getCirrusObjectId();
        var appVars = {
        	id: id
        };
        var keys = ['background','fade','smoothness','diagonals'];
        for (var k = 0; k < keys.length; k++) {
            appVars[keys[k]] = this.getApiParamValue(keys[k]);
        }
        
        // from http://www.kirupa.com/developer/mx/detection.htm
        var has_flash = (navigator.mimeTypes && navigator.mimeTypes["application/x-shockwave-flash"]) ? navigator.mimeTypes["application/x-shockwave-flash"].enabledPlugin : 0;
        if (Ext.isIE) {
        	try {
                var obj = new ActiveXObject("ShockwaveFlash.ShockwaveFlash");
                has_flash = true;
            } catch(err){
            }
        }

        // canvas and canvas text detection code from: http://diveintohtml5.org/detect.html#canvas
        var has_canvas = !!(!!document.createElement('canvas').getContext && typeof document.createElement('canvas').getContext('2d').fillText == 'function');
        
        var forceFlash = this.getApiParamValue('forceFlash');
        var forceHtml5 = this.getApiParamValue('forceHtml5');
        
        // for now we'll keep flash the default since the html5 version seems less efficient
        if ((has_flash && !forceHtml5) || forceFlash) {
			var scripts = '<script type="text/javascript">'+
				'function cirrusClickHandler'+id+'(word, value) {'+
				'if (window.console && console.info) console.info(word, value);'+
				'var cirrusTool = Ext.getCmp("'+this.id+'");'+
				'cirrusTool.cirrusClickHandler(word, value);'+
				'}'+
				'function cirrusLoaded'+id+'() {'+
				'if (window.console && console.info) console.info("cirrus flash loaded");'+
				'Ext.getCmp("'+this.id+'").loadInitialData();'+
				'}'+
				'</script>';
            this.body.update(scripts+'<div id="'+id+'" style="height: 100%; width: 100%;"></div>', true);
            appVars.background = '0x'+appVars.background;
            var params = {
                menu: 'false',
                scale: 'showall',
                allowScriptAccess: 'always',
                bgcolor: '#222222',
                wmode: 'opaque'
            };
            var attributes = {
                id: id,
                name: id
            };
            swfobject.embedSWF(this.getApplication().getBaseUrl()+"resources/lib/cirrus/Cirrus.swf", id,
                '100%', '100%', "10.0.0", "expressInstall.swf", appVars, params, attributes);
            this.cirrusApp = Ext.getDom(id);
            this.initialized = true;
        	
        } else if (has_canvas || forceHtml5) {
            appVars.background = '#'+appVars.background;
            var initCanvasApp = function() {
            	if (Ext.DomQuery.selectNode('canvas', Ext.get(id).dom) == null) {
	                try {
	                    appVars.containerId = id;
	                    this.cirrusApp = new Cirrus(appVars);
	                    $('#'+id).bind('wordclicked', function(e, word, value) {
	                        this.cirrusClickHandler(word, value);
	                    }.createDelegate(this));
	                    this.initialized = true;
	                    this.loadInitialData();
	                } catch(e) {
	                    setTimeout(initCanvasApp.createDelegate(this), 250);
	                }
            	}
            }
            var scripts = ''+ 
            '<script type="text/javascript" src="'+this.getApplication().getBaseUrl()+'resources/lib/jquery/jquery-1.4.2.min.js"></script>'+
            '<script type="text/javascript" src="'+this.getApplication().getBaseUrl()+'resources/lib/cirrus_canvas/Cirrus.js"></script>'+
            '<script type="text/javascript" src="'+this.getApplication().getBaseUrl()+'resources/lib/cirrus_canvas/Word.js"></script>'+
            '<script type="text/javascript" src="'+this.getApplication().getBaseUrl()+'resources/lib/cirrus_canvas/WordController.js"></script>';
            this.body.update(scripts+'<div id="'+id+'" style="height: 100%; width: 100%;"></div>', true, initCanvasApp.createDelegate(this));
        } else {
        	this.body.update('Your browser does not support this tool.');
        }
    },
    
    cirrusClickHandler: function(word, value) {
        if (value == this.getCorpus().getId()) {
        	/**
			 * @event corpusTypeSelected
			 * @param {Voyeur.Tool.Cirrus} tool
			 * @param {Object} params <ul>
			 * <li><b>type</b> : String</li>
			 * </ul>
			 * @type dispatcher
			 */
            Voyeur.application.dispatchEvent('corpusTypeSelected', this, {type: word});
        } else {
            var docIdType = value + ':' + word;
            /**
			 * @event documentTypeSelected
			 * @param {Voyeur.Tool.Cirrus} tool
			 * @param {Object} params <ul>
			 * <li><b>docIdType</b> : String</li>
			 * </ul>
			 * @type dispatcher
			 */
            Voyeur.application.dispatchEvent('documentTypeSelected', this, {docIdType: docIdType});
        }
    },
    
    constructor : function(config) {
        Ext.apply(this, new Voyeur.Tool(config, this));

        Voyeur.Tool.Cirrus.superclass.constructor.apply(this, arguments);
        
        /**
		 * @event CorpusSummaryResultLoaded
		 * @type listener
		 */
        this.addListener('CorpusSummaryResultLoaded', function() {
			if (this.rendered) {this.fireEvent('afterrender', this);}
        }, this);
        
        this.addListener('afterrender', function(src, params) {
			if (this.getCorpus().getSize()==0) {this.body.update('');}
			else {
				this.initCirrus(params);
			}
        }, this);
        
//        this.addListener('corpusDocumentSelected', function(src, params) {
//        	this.setApiParams(params);
//        	this.fetch('CorpusTypeFrequencies');
//        }, this);
        
        /**
		 * @event CorpusTypeFrequenciesResultLoaded
		 * @type listener
		 */
    	this.addListener('CorpusTypeFrequenciesResultLoaded', function(src, data) {
    		if (src==this) { // only if it originates from here
	    		this.handleTypeData(this.corpusTypeReader.readRecords(data).records,'corpus');
    		}
    	}, this);
    	this.addListener('DocumentTypeFrequenciesResultLoaded', function(src, data) {
    		if (src==this) { // only if it originates from here
	    		this.handleTypeData(this.documentTypeReader.readRecords(data).records,'document');
    		}
    	}, this);
    }
    
    ,loadInitialData: function() {
    	var type = this.getApiParamValue('type');
    	// default to document mode if there's only 1 doc in the corpus
    	if (this.getCorpus().getSize() == 1) {
    		this.setApiParams({docId: this.getCorpus().getDocument(0).getId(), docIndex: 0});
    	}
		if (type != null) {
			// make sure type is included, then remove it so we can get other words
			this.fetch(this.getApiParamValue('docId') || this.getApiParamValue('docIndex') ? 'DocumentTypeFrequencies' : 'CorpusTypeFrequencies');
			this.setApiParams({type: null});
		}
    	this.fetch(this.getApiParamValue('docId') || this.getApiParamValue('docIndex') ? 'DocumentTypeFrequencies' : 'CorpusTypeFrequencies');
    }
    
    ,handleTypeData : function (records, mode) {
        var words = [];
        for (var i = 0; i < records.length; i++) {
        	var freq = records[i].get('rawFreq');
            var word = {word: records[i].get('type'), size: freq, label: freq};
            if (mode =='corpus') word.value = this.getCorpus().getId();
            else word.value = records[i].get('docId');
            words.push(word);
        }
        this.sendWords(words);
    }

    ,clearAll: function() {
        this.cirrusApp.clearAll();
    }
    
    ,sendWords: function(words) {
		var me = this;
		try {
            this.cirrusApp.addWords(words);
            this.cirrusApp.arrangeWords();
		}
		catch (e) {
			setTimeout(function(){me.sendWords.call(me,words)},750);
		}
    }
    
    /**
     * 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) {
    	var params = this.getApiParams();
    	var remove = ['background', 'diagonals', 'fade', 'smoothness'];
    	for (var i = 0; i < remove.length; i++) {
    		delete params[remove[i]];
    	}
    	this.update({tool: tool, params: params});
    }
    
	,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))
	
	
	,showOptions : function() {
		this.showOptionsWindow({
			items : [{
				xtype : 'form',
				labelWidth : 150,
				labelAlign : 'right',
				border : false,
				items : [{
					xtype: 'textfield',
					id: 'query',
					value: this.getApiParams()['query'],
					fieldLabel : '<span ext:qtip="'
						+ this.localize('queryTip') + '">'
						+ this.localize('query') + '</span>',
					width: 300
				}],
				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 query = form.findField('query');
								
								var params = {stopList: stopList.getValue(), query: query.getValue()};
								if (params.stopList == '') params.stopList = null;
								
								formPanel.findParentByType('window').destroy();
								
								this.clearAll();
								this.setApiParams(params);

								if (global) {
									this.getApplication().applyParamsGlobally({
										stopList: this.getApiParamValue('stopList')
									}, true);
								}
								else {
									this.initialized = false;
									this.initCirrus({});
								}
						    	
							},
							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('stopList').setValue(this.getApiParamDefaultValue('stopList'));
							},
							scope : this
						}
					}
	
				}]
			}]
		}, true);

	}
	
	,api: {
		/**
		 * @property query A string to search for in a document.
		 * @type String
		 * @default null
		 */
		query: {'default': null}
		/**
		 * @property type The corpus type(s) to restrict results to.
		 * @type String|Array
		 * @default null
		 */
		,type: {'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
			,'type': String
			,'required': false
			,'value': null
			,'multiple': false
			,'choices': ['stop.en.taporware.txt', 'stop.fr.veronis.txt']
			,'example': 'stop.en.taporware.txt'
		}
		/**
		 * @property docIndex The document index to restrict results to.
		 * @type Integer
		 * @default null
		 */
		,'docIndex': {
			'default': null
			,'type': Number
			,'required': false
			,'value': null
			,'multiple': true
			,'example': 0
		}
		/**
		 * @property docId The document ID to restrict results to.
		 * @type String
		 * @default null
		 */
		,'docId': {
			'default': null
			,'type': String
			,'required': false
			,'value': null
			,'multiple': true
			,'example': 'a_valid_document_id'
		}
		/**
		 * @property background The background colour (in hexadecimal) of the app.
		 * @type String
		 * @default ffffff
		 */
		,'background': {
			'default': 'ffffff'
			,'type': String
			,'required': false
			,'value': null
			,'multiple': false
		}
		/**
		 * @property fade Whether words should fade in or not.
		 * @type Boolean
		 * @default true
		 */
		,'fade': {
			'default': 'true'
			,'type': Boolean
			,'required': false
			,'value': null
			,'multiple': false
		}
		/**
		 * @property smoothness How many words to display at a time (lower is smoother).
		 * @type Integer
		 * @default 2
		 */
		,'smoothness': {
			'default': 2
			,'type': Number
			,'required': false
			,'value': null
			,'multiple': false
		}
		/**
		 * @property diagonals What words to arrange diagonally (Flash version only). Possible values: all, bigrams, none.
		 * @type String
		 * @default none
		 */
		,'diagonals': {
			'default': 'none'
			,'type': String
			,'required': false
			,'value': null
			,'multiple': false
			,'choices': ['all', 'bigrams', 'none']
		}
		/**
		 * @property limit The number of words to return in each call.
		 * @type Integer
		 * @default 75
		 */
		,limit: {
			'default': 75
			,type: Number
		}
		/**
		 * @property forceFlash True to force the use of the Flash version.
		 * @type Boolean
		 * @default null
		 */
		,forceFlash: {
			'default': null
		}
		/**
		 * @property forceHtml5 True to force the use of the Canvas version.
		 * @type Boolean
		 * @default null
		 */
		,forceHtml5: {
			'default': null
		}
		,toolType: ['Visualization']
		,listeners: ['CorpusSummaryResultLoaded', 'CorpusTypeFrequenciesResultLoaded', 'DocumentTypeFrequenciesResultLoaded']
		,dispatchers: ['corpusTypeSelected', 'documentTypeSelected']
	}
	
	,thumb: {
		large: 'Cirrus.png'
	}
	
    ,i18n : {
        title : {en: 'Cirrus'}
        ,type : {en: "Visualization"}
        ,query : {en: 'Search'}
        ,queryTip : {en: 'Enter a comma-separated list of words to search for.'}
        ,help: {en: 'This tool organizes words into a "cloud".'}
        ,adaptedFrom: {en: 'This tool is based on this <a href="http://emumarketing.uoregon.edu/paul/2008/09/28/the-new-tag-cloud/" target="_blank">Wordle Clone</a>.'}
    }
});

Ext.reg('voyeurCirrus', Voyeur.Tool.Cirrus);

