/**
 * Project      : AMDA-NG4
 * Name         : CalculatorUI.js
 * @class   amdaDesktop.ExplorerModule 
 * @extends Ext.util.Observable
 * @brief   Calculator Plugin used in SearchUI and ParameterUI
 * @author  elena
 * @version $Id: CalculatorUI.js 1399 2013-03-27 12:44:50Z elena $
 *
 ******************************************************************************
 *    FT Id     :   Date   : Name - Description
 ******************************************************************************
 *
 *	:           :06/05/2011: CDA  - Extjs 4.0.0 Migration
 *  :               :16/06/2011: elena - resolve rendering problem (sencha support)
 *  :	            :26/10/2011: BRE -autoload constants and functions from xml files
 */


/**
 * @plugin calculator
 * @extends Ext.util.Observable 
 * @ptype calculator
 */

var CalculatorData = ['1','2','3','4','5','6','7','8','9','0','(',')','[',']','+','-','*','/','^', '.','>','<', '&', '|'];

Ext.define('amdaUI.CalculatorUI', {
	extend: 'Ext.util.Observable',
	
	requires : [
	  'amdaModel.Constant',
	  'amdaModel.Function'
	],
	
	alias: 'plugin.calculator',
	
	statics : {
	  constantStore : null,
	  functionStore : null
	},
	
	win: null,
	
	constructor: function(config) { 
		Ext.apply(this, config);  
		this.callParent(arguments);
	},

	init: function(cmp) 
	{  
	    this.hostCmp = cmp;
	    this.hostCmp.on({
	        scope: this,		 	       
	        added: function(){
		    this.hostCmp.ownerCt.on({
		      render: this.onRender,
	          show: this.onShow,
	          hide : this.onHide,
		      scope: this });		   
	        }
	    }); 
	   
	},
	
	onRender: function() 
	{
		this.win = new Ext.Window({
			width: 350, 
			height: 170,
			x: 380, y: 0,
			baseCls:'x-panel',	 
			title: 'Tools For ' + this.context + ' Construction',
			layout: 'fit',
			closable: false,
			collapsible: true,
			constrain: true,
			floating: true,
			ghost: false,
			renderTo: this.hostCmp.id,
			items: this.getFormConfig(),
			listeners : {
				boxready: function (w) 
				{
					if (w.y + w.height > myDesktopApp.desktop.el.getHeight())
						w.el.setY((myDesktopApp.desktop.el.getHeight()-w.height > 0) ? myDesktopApp.desktop.el.getHeight()-w.height : 0);
        		}
			},
			getConstrainVector: function(constrainTo){
				var me = this;
		        if (me.constrain || me.constrainHeader) {
		            constrainTo = constrainTo || (me.floatParent && me.floatParent.getTargetEl()) || me.container || me.el.getScopeParent();
		            return (me.constrainHeader ? me.header.el : me.el).getConstrainVector(constrainTo);
		        }
		    }
		});
		
		//load constants store
		if (!amdaUI.CalculatorUI.constantStore)
		{
		  amdaUI.CalculatorUI.constantStore = Ext.create('Ext.data.Store',{model: 'amdaModel.Constant'});
		  amdaUI.CalculatorUI.constantStore.load({
		    scope   : this,
		    callback: function(records, operation, success) 
		    {
			  	this.createConstantBtns();
		    }
		  });
		}
		else
			this.createConstantBtns();

		//load functions store
		if (!amdaUI.CalculatorUI.functionStore)
		{
		  amdaUI.CalculatorUI.functionStore = Ext.create('Ext.data.Store',{model: 'amdaModel.Function'});
		  amdaUI.CalculatorUI.functionStore.load({
		    scope   : this,
		    callback: function(records, operation, success) 
		    {
			  	this.createAllFunctionBtns();
		    }
		  });
		}
		else
			this.createAllFunctionBtns();
	
		
	},
	
	onShow: function() {
		this.win.show(); 
		var parentWin = this.hostCmp.findParentByType('window');
		
		if (parentWin.getId() === myDesktopApp.dynamicModules.param.id) {
			this.win.setPosition(335,10);
		}
		else {
			
			var posX =  parentWin.getWidth() - this.win.getWidth() - 30;
			var posY = parentWin.getHeight() - this.win.getHeight() - 110 - 30/*20*/;
			this.win.setPosition(posX,posY);//(420,290);
		}
	},
	
	onHide : function()
	{
		this.win.hide(); 
	},
 	
	getFormConfig: function(){
		return {
			xtype: 'tabpanel',
			border: false, frame: true, plain: true,
			enableTabScroll: true, 
			defaults: { frame: true, border: false, plain: true, autoScroll:true},
			activeTab: 0,
			items: [ {
				title: 'Calculator',layout: 'column',
				defaults: { xtype: 'button', columnWidth: .11},
				items: this.getItems('Calculator')
			} , {
				title: 'Constants', layout: 'column',
				defaults: { xtype: 'button', columnWidth: .20},
				id : 'calc_tab_const_id'
			}, {
				title: 'Functions', xtype:'tabpanel', //iconCls: 'tabs',
				enableTabScroll: true,	tabPosition: 'bottom',	
				defaults: { frame: true, border: false, plain: true, layout: 'column', autoScroll:true},
				activeTab: 0,
				id : 'calc_tab_func_id'
			}]
		};					 
	},

	/**
	 * Prompt any argument to user before processing Formula
	 * @param sel selected text
	 * @param currentBtn calculator button pressed
	 * @param params array of parameters
	 */
	preProcessFormula: function(sel,currentBtn, params) {
		
		if (currentBtn.initialConfig.args!=0 && currentBtn.initialConfig.prompt!=""){
			// Prompt for user precision and process the result using a callback
			Ext.Msg.prompt('Argument',currentBtn.initialConfig.prompt, function(bt2, text){
				if (bt2 === 'ok'){
					var afterParamsText = "," + text + ")";
					//TODO: more than one args and prompt
					this.processFormula(sel,currentBtn, params, afterParamsText);

				}
			},this);
		} else {
			this.processFormula(sel,currentBtn, params, ")");
		}// endif prompt
	},
	
	processFormula : function(sel,currentBtn, params, afterParamsText){
		// calculation of the required number of parameters
		var nbParams = currentBtn.initialConfig.params;
		var fnText = currentBtn.text.split('(');
		var newConstruction = sel.beforeText + fnText[0]+"(";
		// if there at least one parameter selected
		if (params.length){
			for (var i=0;i<nbParams;i++) {
				if(i>0) {
					newConstruction += ",";
				}
				newConstruction += params[i];
			}
		}
		// we keep position
		var afterParameterPos = newConstruction.length;
		newConstruction += afterParamsText;
		var caretPos = newConstruction.length;
		newConstruction += sel.afterText;
		this.hostCmp.constructionField.setValue(newConstruction);

		// If we haven't the right number of selected parameters
		if (params.length < nbParams){
			var stringParamRequired = currentBtn.initialConfig.params+" parameter(s)";
			Ext.Msg.alert('Caution', 'you\'ll have to add '+ stringParamRequired +' to apply this function', 
				function(){
					// set Caret Position at placement of required parameter in function
					this.hostCmp.constructionField.setCaretPosition(afterParameterPos);
				},this
			);											        	
		} else {
			// set Caret Position after inserted Text
			this.hostCmp.constructionField.setCaretPosition(caretPos);
		}
	},
	
	/**
	 * This method construct an array of arguments into selected text
	 * @param selectedText the selection to parse
	 * @param parseIndex the index to start parsing
	 * @return the arguments array
	 */
	parseArgsInFormula : function (selectedText, parseIndex) {
		
		if (!selectedText || selectedText==""){
			return [];
		} else {
			var params = [];
			var startIndex = parseIndex;
			var curIndex = parseIndex;
			var openBrace = 0;
			var sep = 0;
			var closeBrace = 0;
			
			// while there is a separator
			while(sep!=-1){
				openBrace = selectedText.indexOf("(",curIndex);
				sep = selectedText.indexOf(",",curIndex);
				closeBrace = selectedText.indexOf(")",curIndex);
				
				// if there's an open bracket and no close bracket or inversely
				if (openBrace!=-1 && closeBrace==-1 || openBrace==-1 && closeBrace!=-1) {
					// invalid selection
					return -1;
				}
				
				// if there's a separator and opening brackets into selection
				if (sep!=-1 && openBrace!=-1){
					// if brace is before separator 
					if (openBrace<sep) {
						curIndex = this.getEndBracket(selectedText,openBrace+1);
						if (curIndex===-1){
							return -1;
						}
					} else {// else separator is before brace
						params.push(selectedText.substring(startIndex,sep));
						startIndex = curIndex = sep+1;
					}
				// if there's only separators into selection
				} else if (sep!=-1) {
					params.push(selectedText.substring(startIndex,sep));
					startIndex = curIndex = sep+1;
				}
			}
			params.push(selectedText.substring(startIndex,selectedText.length));			
			return params;
		}
	},

	getEndBracket : function(string,indOpenBrace){
		// we search for the corresponding end brace (after open bracket)
		var currentIndex = indOpenBrace;
		var nextCloseBrace = 0;
		var nextOpenBrace = 0;
		var braceLevel = 1;
		while (nextCloseBrace!==-1 && braceLevel!==0){
			// get index of next opening bracket
			nextOpenBrace = string.indexOf("(",currentIndex);
			// get index of next closing bracket
			nextCloseBrace = string.indexOf(")",currentIndex);
			// if both exist
			if (nextOpenBrace!=-1 && nextCloseBrace!=-1) {
				// if opening bracket is before closing one
				if (nextOpenBrace<nextCloseBrace) {
					currentIndex = nextOpenBrace+1;
					braceLevel++;
				} else { // if closing bracket is before opening one
					currentIndex = nextCloseBrace+1;
					braceLevel--;
				}
			// if there's only a next opening bracket
			} else if (nextOpenBrace!=-1 && nextCloseBrace==-1) {
				currentIndex = nextOpenBrace+1;
				braceLevel++;
			// if there's only a next closing bracket
			} else if (nextOpenBrace==-1 && nextCloseBrace!=-1) {
				currentIndex = nextCloseBrace+1;
				braceLevel--;
			}
		}
		// if no level imbrication left return index after closing bracket of block else -1
		return braceLevel==0 ? currentIndex : -1;
	},
	
	getCalculatorBtn : function()
	{
		var btns = [];
		
		Ext.each(CalculatorData, function (c){
		  btns.push({
		    text: c,
			scope: this,
			handler: function(b,e){
			  // keep selection into construction field
			  var selection = this.hostCmp.constructionField.getSelection();
			  // the new value of construction field
			  var newConstruction = "";
			  // replacement of selection into construction field by text of clicked button
			  newConstruction = selection.beforeText + b.text;
			  var caretPos = newConstruction.length;
			  newConstruction += selection.afterText;
			  this.hostCmp.constructionField.setValue(newConstruction);
			  // set Caret Position after inserted Text
			  this.hostCmp.constructionField.setCaretPosition(caretPos);
		    }
		  })
		},
		this
	  );
		
	  return btns;
	},
	
	createConstantBtns : function(){
	  var constTab = this.win.query('#calc_tab_const_id');
	  
	  if (constTab.length < 1)
		  return;
	  
	  amdaUI.CalculatorUI.constantStore.each( function(c){
		 constTab[0].add(
				 {
					 text: c.get('name'),
					 tooltip: c.get('units') == '' ? c.get('info')+'<br/>'+c.get('value') :
									 c.get('info')+'<br/>'+c.get('value')+' '+c.get('units'),
					 scope: this,
					 handler: function(b,e){
						 // keep selection into construction field
						 var selection = this.hostCmp.constructionField.getSelection();
						 // the new value of construction field
						 var newConstruction = "";
						 // replacement of selection into construction field by text of clicked button
						 newConstruction = selection.beforeText + '@'+b.text;
						 var caretPos = newConstruction.length;
						 newConstruction += selection.afterText;
						 this.hostCmp.constructionField.setValue(newConstruction);
						 // set Caret Position after inserted Text
						 this.hostCmp.constructionField.setCaretPosition(caretPos);
					 }
				 });
	  },this);
	},
	
	createAllFunctionBtns : function()
	{
		this.createFunctionBtns('IdlFunctions','Simple Maths');	  	
	  	this.createFunctionBtns('TimeFunctions','Statistics');
	  	this.createFunctionBtns('FunctionsSliding','Statistics/Sliding');
		this.createFunctionBtns('AmdaFunctions','Space Physics');
	},
	
	createFunctionBtns : function(item, tabTitle)
	{
		var funcTab = this.win.query('#calc_tab_func_id');
		  
		if (funcTab.length < 1)
		  return;
		
		switch (item) 
		{
		  case 'IdlFunctions' :
			  amdaUI.CalculatorUI.functionStore.filter('kind','idl');
		    break;
		  case 'AmdaFunctions' :
			  amdaUI.CalculatorUI.functionStore.filter('kind','amda');  
			break;
		  case 'TimeFunctions' :
			  amdaUI.CalculatorUI.functionStore.filter('kind','time'); 
			  break;
		  case 'FunctionsSliding' :
			  amdaUI.CalculatorUI.functionStore.filter('kind','sliding'); 
		    break;
		}
		
		var crtTab = funcTab[0].add(
				{
					title : tabTitle,
					defaults: { xtype: 'button',  columnWidth: .20}
				});
		
		amdaUI.CalculatorUI.functionStore.each( function(f){
			crtTab.add(
					{
						text: f.get('name'),
						args: f.get('args'),
						params: f.get('params'),//"+",
						prompt: f.get('prompt'),
						tooltip: f.get('info_brief'),
						disabled: f.get('argv') == 'vector',
						scope: this,
						handler: function(b,e){
							var selection = this.hostCmp.constructionField.getSelection();
							var selectedText = selection&&selection.text!="" ? Ext.util.Format.trim(selection.text) : null;
							if (selectedText && selectedText!==""){
								selectedText.replace("[","(");
								selectedText.replace("{","(");
								selectedText.replace("}",")");
								selectedText.replace("]",")");
							}
							// Formula Parsing for arguments
							var params = this.parseArgsInFormula(selectedText,0);
//							var params = selectedText ? selectedText.split(',') : [];
							if (params === -1) {
								Ext.Msg.alert("Invalid Selection", "Action aborted");
							} 
							else {
								// calculation of the required number of parameters
								var nbParams = b.initialConfig.params;

								if (params.length>nbParams)
								{
									// Show a dialog using config options:
									Ext.Msg.show({
										title:'Caution',
										msg: 'you have selected more than '+nbParams+' parameter(s) to apply this function<br>Only the first will be kept, others will be deleted',
										buttons: Ext.Msg.OKCANCEL,
//										animEl: 'elId',
										icon: Ext.MessageBox.WARNING,
										fn: function(bt1){
										if (bt1 === 'ok'){
											this.preProcessFormula(selection,b, params);
										}
									},
									scope:this
									});
								} 
								else 
								{
									this.preProcessFormula(selection,b, params);
								}
							}
						}
					});
		},
		this
	  );
      //clear filter
	  amdaUI.CalculatorUI.functionStore.clearFilter();
      
	},
      
	getItems:  function(item)
	{          
	  switch (item) 
	  {
	    case 'Calculator': 
		  return this.getCalculatorBtn();
		default: break;
	  }
	  return [];	      
	}				  
});