/** 
 * Project  : AMDA-NG4
 * Name     : InteractiveNode.js
 * @class   amdaModel.InteractiveNode
 * @extends amdaModel.AmdaNode
 * @brief   Generic Model of Interactive Node
 * @author  CDA
 * @version $Id: InteractiveNode.js 2683 2014-12-02 10:58:53Z elena $
 */

Ext.define('amdaModel.InteractiveNode', {
	extend: 'amdaModel.AmdaNode',

	requires: [
		'amdaPlotObj.PlotRequestObject'
	],
    
	fields: [        
		{name: 'contextNode', type: 'amdaModel.AmdaNode', persist: false},
		{name: 'objectDataModel', type: 'string', persist: false},       
		{name: 'object', type: 'object', persist: false},
		{name: 'moduleId', type: 'string', persist: false},
		{name: 'filtered', type: 'boolean', defaultValue: false, persist: false},
		{name: 'needsArgs', type:'boolean', defaultValue: false},
		{name: 'disable', type: 'boolean', defaultValue: false, persist: false}
	],

	statics: {
		preloadNodes : function(node,onready)
		{
			var me = this;	
			var nodesToLoad = new Array();
			nodesToLoad.push(node);
			this.preloadTreeNode(node, nodesToLoad, function (node)
				{
					var isFinish = true;
					nodesToLoad.forEach(function (element, index, array)
					{
							if (!element.isLoaded())
								isFinish = false;
					});					
					if (isFinish && onready)
						onready.call();
				});
		},

		preloadTreeNode : function(node, nodesToLoad, onloaded)
		{
			var me = this;
			
			if (node.isLoaded())
			{
				node.eachChild(function(n)
					{		      
						if (!n.isLoaded() && !n.isLeaf())
						{
							nodesToLoad.push(n);
							me.preloadTreeNode(n,nodesToLoad,onloaded);
						}
					});
					
					if (onloaded)
						onloaded.call(me,node);
					return;
			}	
			
			node.store.load({
				node : node, 
				callback : function(records, operation, successful)
					{
						records.forEach(function (record)
							{                                                       
								if (!record.isLoaded() && !record.isLeaf())
									{
										nodesToLoad.push(record);
										me.preloadTreeNode(record,nodesToLoad,onloaded);
									}
							});
						if (onloaded)
							onloaded.call(me,node);
					}
			});
		}
	},
    
	onReady : null,
	
	constructor : function(config)
	{
		this.callParent(arguments); 
		this.set('nodeType',this.self.nodeType);		
		this.set('ownerTreeId',amdaUI.ExplorerUI.RESRC_TAB.TREE_ID);
		if (this.get('id')) { // TODO why sometimes (delete in remote interoper tree) this.get('id') = undefined ?
			// if id of this node have root category suffix		
			if (Ext.util.Format.substr(this.get('id'), -(amdaUI.ExplorerUI.CAT_SUFFIX.length), this.get('id').length) === amdaUI.ExplorerUI.CAT_SUFFIX)
			{
			// set the expanded property to true
				this.set('expanded',true);
			}  
		}
	},
    
/**
* this method is overriden into ExecutableNode to return true
*/
	isExecutable: function()
	{
		return false;
	},

/**
* open Module with THIS NODE
*/
	editInModule : function (contextNode, onReady) 
	{
		// set the contextNode of this node
		this.set('contextNode',contextNode);
		// parameter module
		var me = this;
		myDesktopApp.getLoadedModule(this.get('moduleId'),true, function (module) {
		// If the node to edit is not already linked to this module
			if (module.getLinkedNode() != me) 
			{
				// set relative node into parameter Module
				module.setLinkedNode(me);	 
				if (contextNode==null) 
				{
					// set the root node as contextNode
					contextNode = me.getRootNode();
				}       	 
				module.setContextNode(contextNode); 
							
			} else if (module.getLinkedNode() != null){
			//TODO the node to edit is already edited
				// myDesktopApp.warningMsg('This object is  being edited');
				//Sol1: msg alert: "warning this node is already edited! If you want to get the original, please press the 'reset' button"->'OK'
				//Sol2: msg with user choice: "warning this node is already edited! Would you confirm this action and lost your modification?"->'Confirm','Cancel'
			}
			// Opening parameter window     
			module.createWindow(onReady);  
		});				
	},

/**
* Method to rename the workspace node
*/
	rename: function(value,callBackFn) 
	{ 		 
		var dataToSend = {id : this.get('id'), old_name: this.modified.text, name: value,  parent : this.data.parentId, leaf: this.isLeaf(), nodeType: this.get('nodeType')};
		AmdaAction.renameObject(dataToSend, callBackFn);		 
	},
    
/**
* Method to rename the workspace node when D&D
*/
	renameDD: function(parentId, callBackFn) 
	{ 
		var dataToSend = {id : this.get('id'), old_name: this.get('name'), name: this.get('name'), parent : parentId, leaf: this.isLeaf(), nodeType: this.get('nodeType')};
		AmdaAction.renameObject(dataToSend, callBackFn);
	},
    
/**
* validation method on name (used in module forms)
* @param name the name to validate
* @returns 
*/
	isValidName : function(name, callBackFn)
	{
		var dataToSend = {name: name, nodeType: this.get('nodeType'), leaf: this.isLeaf()};
		AmdaAction.validNameObject(dataToSend, callBackFn);
	},

/**
* Method to persist modifications of an AmdaObject by Server side and update the workspace
* node linked to a Module
*/
	update : function(opt)
	{        
		AmdaAction.modifyObject(this.get('object').getJsonValues(true), function(res,e){
            
			if(e.status) {
				if (res.id) {              
					if (!this.get('contextNode')) {
						// set the root node of 'Derived Parameters' tree as contextNode
						this.set('contextNode',this.getRootNode());
					}
					this.get('contextNode').expand(false,false);
					this.myGetOwnerTree().getSelectionModel().select(this);
					
					if (opt)
					{
						var scope = opt.scope ? opt.scope : this;
						if (opt.callback)
							opt.callback.call(scope,'update');
					}
					
					Ext.Msg.alert('Complete', 'Object '+this.get('object').get('name')+' has been modified');
					// fix the modifications for object
					this.get('object').commit();
					
					if (res.info) {
						this.set('info',res.info);
					}
					
					// myDataParam  and Parameter special update
					this.specialUpdate(res, false);
					
					// reload object into the view of corresponding Module
					var me = this;
					myDesktopApp.getLoadedModule(this.get('moduleId'), true, function (module) {
						module.getUiContent().setObject(me.get('object'));
					});
				}
				else {
					//TODO proper error message handling
					// error code from server; but e.status==true                   
					// revert all modifications since last load or commit
					this.get('object').reject();
					myDesktopApp.errorMsg(res.error);
				}
			}
			else {
				// revert all modifications since last load or commit
				this.get('object').reject();
				myDesktopApp.errorMsg(e.message);   
			}
		},this);
	},

/**
* Method to create a new AmdaObject by server side and create the workspace node linked to a Module
* under its contextNode or the root node corresponding to this nodeType category
*/
	create : function(opt) 
	{
		if (!this.get('contextNode') || (this.get('contextNode').data.id == 'sharedtimeTable-treeRootNode') || (this.get('contextNode').data.id == 'sharedcatalog-treeRootNode')) {
			// set the root node of 'Derived Parameters' tree as contextNode
			this.set('contextNode',this.getRootNode());
		} 
		// call the Ext.Direct method to create parameter 
		AmdaAction.createObject(this.get('object').getJsonValues(false), this.get('contextNode').get('id'), function(res,e){          
			//success
			if(e.status) 
			{                
				// if correct response received
				if (res.id) {   //if (res.id || res.error == 'NAME_EXISTS') {
					// 'save as' case ; delete old node if it exists
					if (this.toRename) 
					{
						this.toRename	= false;  
						var myRoot = this.getRootNode();
						// search the same named node to override
						var updateNode = myRoot.findChild('text',this.get('object').get('name'),true);
						// destroy the overrided node
						updateNode.parentNode.removeChild(updateNode);//TODO ??if destroy==true => too many recursions....
						updateNode.destroy();			 
					} 
					// set text of this node
					this.set('text',this.get('object').get('name'));

					//set id of this node
					this.set('id',res.id);
					this.internalId = res.id;

					// set id of node's object
					this.get('object').set('id',res.id);    
					if (res.created){
						// set the created date
						this.get('object').set('created',res.created);
					}
                    
					if (res.info){
						// set the tooltip
						this.set('info',res.info);
					}
					//set globalStart & global Stop to be used for time selection
					this.specialUpdate(res, true);
                                                        
					//TODO do we need this commission ???
					// fix the modifications for object
					this.get('object').commit(); 

					// if ownerTree panel is not active
					if (this.myGetOwnerTree().ownerCt.getActiveTab()!==this.myGetOwnerTree()) {
						// set ownerTree panel as the active tab - to enable selection of this node his ownerTree must have a view
						this.myGetOwnerTree().ownerCt.setActiveTab(this.myGetOwnerTree());                        
					}
                    
					Ext.Msg.alert('Complete', 'New object '+this.get('object').get('name')+' has been created'); 
					// expand the contextNode
					this.get('contextNode').expand(false, function() {
						if (!this.get('contextNode').findChild('text',this.get('text'))) {
							// create node in tree as child of contextNode                 
							this.get('contextNode').appendChild(this);			                  				
						}
						// select the new node 
						this.myGetOwnerTree().getSelectionModel().select(this);
						if (opt) {
							var scope = opt.scope ? opt.scope : this;
							if (opt.callback)
									opt.callback.call(scope,'create');
						}                                                
					}, this);
				}               
				// error code from server; but e.status==true
				else {
					myDesktopApp.errorMsg(res.error);                         
					// revert all modifications since last load or commit
					this.get('object').reject();
				}
			}
			// failure: e.status == false
			else {
				// revert all modifications since last load or commit
				this.get('object').reject();
				//TODO: this.destroy();
				myDesktopApp.errorMsg(e.message);
			}
		},this);         
	},

	specialUpdate : function() {},
/**
* Generic part of Context Menu
*  
*/ 
	allMenuItems : function() {
		var src = this.self.objectName;
		var menuItems =
		[ {
			fnId : 'root-createLeaf',
			text : 'Create '+ src
		}, {
			fnId : 'root-createDir',
			text : 'Create Folder'
		}, {
			fnId : 'dire-createLeaf',
			text : 'Create ' + src
		}, {
			fnId : 'dire-createDir',
			text : 'Create Folder'
		}, {
			fnId : 'dire-renameNode',
			text : 'Rename Folder'
		}, {
			fnId : 'dire-deleteNode',
			text : 'Delete Folder'
		}, {
			fnId : 'leaf-editLeaf',
			text : 'Edit ' + src
		}, {
			fnId : 'leaf-renameNode',
			text : 'Rename ' + src
		}, {
			fnId : 'leaf-deleteNode',
			text : 'Delete '+ src
		} ];
		return menuItems;
	},

	allMenuMultiItems : function() {
		var menuMulti = [
		{
			fnId : 'mult-deleteMulti',
			text : 'Delete selected ' + this.self.objectName + 's'
		}
		];
		return menuMulti;
	},

	getAllContextMenuItems: function(){  
		return  this.allMenuItems();       
	},

	getMultiContextMenuItems: function(){
		return this.allMenuMultiItems();
	},
    
/**
* default implementation
* no menu display if there's no override of this function
*/
	getMultiContextMenuItems: function(){
		return null;
	},
    
/**
*  Context Menu Actions
*  
*/ 
	onMenuItemClick : function(menu,item,event) {
		// fnId parsing :
		var fnId = Ext.util.Format.substr(item.fnId, 5, item.fnId.length);

		switch (fnId) {
			case 'deleteNode':
				this.deleteNode(); 
				break;
			case 'createDir':
				this.createDir(); 
				break;
			case 'createLeaf':
				this.createLeaf(this);
				break;                    
			case 'renameNode':
				this.renameNode(false);
				break;
			case 'editLeaf':   
				this.editLeaf(); 
				break;        
			case 'deleteMulti':
				this.deleteMulti();
				break;            
			case 'plotParam':                        
				this.createPlot(this);                
				break;
			case 'downParam':                      
				this.createDownload(this);                
				break;   
			default:
				break;
		} // switch end
	},
   
	getTimeFromNode: function(node)  {
		var startString = new String(node.get('globalStart'));
		var stopString = new String(node.get('globalStop'));

		var startDate =  new Date(startString.replace(/\-/g,'\/').replace(/[T|Z]/g,' '));	
		var stopDate =  new Date(stopString.replace(/\-/g,'\/').replace(/[T|Z]/g,' '));

		if (stopDate -  startDate  > 86400000 ) {
			var startTime = Ext.Date.add(stopDate, Ext.Date.DAY, -1);		 
			// var timeObj = {start: Ext.Date.format(startTime, 'Y/m/d H:i:s'), stop: Ext.Date.format(stopDate, 'Y/m/d H:i:s')};
			var timeObj = {start: Ext.Date.format(startTime, 'Y/m/d'), stop: Ext.Date.format(stopDate, 'Y/m/d')};
		} 
		else {
			var timeObj = {start:  node.get('globalStart'), stop:  node.get('globalStop')};
		}
		return timeObj;			
	},

	createPlot: function(node) 
	{       			     
		if (node.get('disable')) return;			
		myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.plot.id, true, function (module) {
			if (!myDesktopApp.desktop.getWindow(myDesktopApp.dynamicModules.plot.id)) {               
				var request = Ext.create(amdaPlotObj.PlotRequestObject.$className);
				var newNode = Ext.create(amdaModel.PlotNode.$className, { object : request });                 
				// edit newNode into Plot Module with node as contextNode
				newNode.editInModule();
				if((node.get('globalStart') != null) && (node.get('globalStop') != null) && node.get('globalStart') != 'depending on mission' && node.get('isParameter')) {                                                                            
					module.getUiContent().setTimeFromData(node.getTimeFromNode(node)); 
				}
			}         
			module.getUiContent().addParameter(node);  
		});
	},
    
	createDownload: function(node) 
	{ 
		myDesktopApp.getLoadedModule(myDesktopApp.dynamicModules.download.id, true, function (module) {
			if (!myDesktopApp.desktop.getWindow(myDesktopApp.dynamicModules.download.id)) {               
				var request = Ext.create(amdaModel.Download.$className);
				amdaModel.DownloadNode.set('object',request);              
				// singleton!
				amdaModel.DownloadNode.editInModule();
				if ((node.get('globalStart') != null) && (node.get('globalStop') != null) && node.get('globalStart') != 'depending on mission' &&  node.get('isParameter')) {
					module.getUiContent().setTimeFromData(node.getTimeFromNode(node)); 
				}
			}         
				var paramName; 
				var components = null;
				switch (node.$className) {
					case 'amdaModel.AliasNode'        :                        
						paramName = "#"+node.get('text');
						break;
                                                                                                    case 'amdaModel.DerivedParamComponentNode' : 
                                                                                                    paramId = node.get('text');
                                                                                                    var parentId = paramId.substr(0, paramId.length - 3);
                                                                                                    paramName= "ws_" + parentId;
                                                                                                    var regExp = /\(([\d]+)\)/;
                                                                                                     var component_index = regExp.exec(paramId);
                                                                                                      if (component_index)
                                                                                                      {
                                                                                                        components = [];
                                                                                                        components['index1'] = component_index[1];
                                                                                                        }
                                                                                                    break;
					case 'amdaModel.DerivedParamNode' :                         
						paramName = "ws_"+node.get('text');
						break;
					case 'amdaModel.MyDataParamNode' :
						paramName = 'wsd_'+node.get('text');                                 
						break;                        
					default : 
						if (node.get('alias')!= "" )
							paramName = "#"+node.get('alias');
						else 
							paramName = node.get('id');
				}				
 				var component_info = node.get('component_info');
 				if (component_info && component_info.parentId) {
 					//It's a component
 					paramName = component_info.parentId;
 					components = [];
 					if (component_info.index1)
 						components['index1'] = component_info.index1;
 					if (component_info.index2)
 						components['index2'] = component_info.index2;
 				}		
				module.addParam(paramName,true,node.get('needsArgs'),components);  
		}); 
	},
	
	deleteNode: function() {		   
			// if the target is a directory
			if (!this.isLeaf()) {
				// determine if this directory is empty before launching the delete confirmation method
				this.isNotEmptyDir(this.confirmDirectoryDeletion);                        
			// else (the target is a leaf)
			} else {
				// no confirmation prompt for leaves
				this.confirmDirectoryDeletion(false);
			}
	},
    
/**
* this method return if node has Childs even if it was not already loaded
*/
	isNotEmptyDir : function(callbackFn) {
		var hasChilds;
		// if node not already loaded
		if (!this.isLoaded()){
			// call directFunction to load this node 
			AmdaAction.getTree({node:this.get('id'),nodeType:this.get('nodeType')},function(res,e){
				callbackFn.call(this,res.length>0?true:false);
			},this);
		} 
		else {
			callbackFn.call(this,this.hasChildNodes());
		}
	},
    
/**
* this method is used to display a confirmation message
*/
	confirmDirectoryDeletion : function(isNotEmptyDir)
	{        
		// if this is a non-empty directory
		if (isNotEmptyDir) {
			// Prompt to the user if he also wants to delete its content
			Ext.Msg.confirm('non-empty directory', 'The target is a non-empty directory!<br>Do you want to continue and also delete its content?', function(btn, text){
				if (btn == 'yes'){
					// do delete
					this.realDelete();
				}
			},this);
		} 
		else {
			this.realDelete();
		}        
	},
    
/*
*    Call the extDirect method to delete parameter
*    Callback method needed to execute node deletion in tree if id in result or to show error msg  
*/
	realDelete : function()
	{                       
		AmdaAction.deleteObject({id: this.get('id'), leaf: this.isLeaf(), nodeType: this.get('nodeType')}, function(res,e){ 
			//TODO proper errors handling                       
			// node deletion in tree 
			if (res) {  // if success
				if (res.id) {
					//Ext.Msg.show({title:'Warning', msg: 'Requests with parameter '+node.data.text+' are deleted', icon: Ext.MessageBox.ERROR, buttons: Ext.Msg.OK});
					if (this.parentNode) {
						if (this.isLeaf()){
							var moduleId = this.get('moduleId');
							// if really interactive node 
							if (moduleId) {
								var win = myDesktopApp.getDesktop().getWindow(moduleId);
								// if window is really open
								if (win) {
									var me = this;
									myDesktopApp.getLoadedModule(moduleId, true, function (module) {
										var editedNode = module.getLinkedNode();
										
										// file node is not linked directly to the module				    
										var isThisFile = false;
										if (editedNode && editedNode.$className == 'amdaModel.MyDataParamNode')                                        
											if (editedNode.get('fileObject').get('fileName') == me.get('text'))
												isThisFile = true; 
											
										// node was created BEFORE tree loading; node.id are different ; but it is the same node 
										if ( editedNode && editedNode.get('id') == me.get('id') && editedNode !== me ) {
											module.setLinkedNode(me);
											isThisFile = true;
										}
										
										if (me.$className == 'amdaModel.DerivedParamNode') {
											var obj = {
												paramId : 'ws_'+me.get('text')
											};
											AmdaAction.compilParamDelete(obj);
										}
										
										if (editedNode === me || isThisFile ){  
											var newNode = Ext.ModelManager.create({leaf : true}, me.$className);
											// several tabs could be connected to one node
											if (moduleId === myDesktopApp.dynamicModules.plot.id) {
												var linkedNodes = module.linkedNodes;
																																				
												if (linkedNodes) {
													linkedNodes.each(function(key, value){						      
														if (value === me) {	 
															linkedNodes.replace(key,newNode);
															var tabPanel = module.getUiContent().tabPanel.items.getByKey(key);
															tabPanel.setObject(Ext.create(amdaModel.Plot.$className, {}));						 
														}	
													}, me);
												}					  					 	
											} 
											newNode.editInModule();
										}
									});	
								}
							}
						}                    
						//update mask info in myData              
						if (res.maskDesc && !res.maskDeleted) {
							this.parentNode.set('info', res.maskDesc);
							this.updateMyDataParam(res.mask, res.maskDesc);
						}                         
						this.remove();                       
					}
					//TODO Several special node-dependent actions - to move to node functions..                   
					// nodes of another nodeType to be deleted as they depend on deleted node                     
					if (res.params) {
						this.deleteDependence(res.params);
						//TODO reset
					}                     
					// mask was deleted or updated - to update mydata tree		    
					if (res.maskDeleted) {
						this.updateMyData(); 
					}                       
				}
				else {
					myDesktopApp.warningMsg(res.error);  
				}
			}
			else {
				myDesktopApp.errorMsg(e.message);   
			}
		}, this);
	},

/*
*  Delete musti selection
*/
	deleteMulti: function()
	{
		var selection = this.myGetOwnerTree().getSelectionModel().selected.items;
		alert(selection.length +' to delete!');
		Ext.Array.each(selection,function(item,index,allItems){
			item.deleteNode();
		})
	},
    
/*
*  Create Folder
*/
	createDir: function() {
		var me = this;
		amdaModel.InteractiveNode.preloadNodes(this.getRootNode(),
			function()
			{
				var newNode = Ext.create(me.$className,
				{
					leaf : false, nodeType : me.get('nodeType'),
					text : amdaModel.AmdaNode.NEW_DIR_NAME,
					children : [],
          parentId: me.get('id')
				});

				// insert the new node as a child of node
        me.insertChild(0, newNode);
				// start text edition on this new directory node
				me.expand(false);
				newNode.expand(false);

				// select the new node
				me.myGetOwnerTree().getSelectionModel().select(newNode);
				// call the renameNode method for this new node
				newNode.renameNode(true);
			});
	},
    
/*
*  
*/
	createLeaf: function(contextNode) {
		// create new node with the same type than the contextNode
		var newNode = Ext.create(contextNode.$className, {leaf : true});

		// load the rootNode and recursively all its child nodes
		amdaModel.InteractiveNode.preloadNodes(contextNode.getRootNode(),
			function()
			{
				// edit newNode into Parameter Module with node as contextNode
				newNode.editInModule(contextNode);
			});
	},

	renameNode: function(deleteOnFailure)
	{
		if (this.myGetOwnerTree())
		{			
			// load the rootNode and recursively all its child nodes if not already loaded
			var me = this;
			amdaModel.InteractiveNode.preloadNodes(this.getRootNode(),
				function()
				{
					// fire the edition event on tree
					var item = me.myGetOwnerTree().getSelectionModel().selected.items[0];
					item.deleteOnFailure = deleteOnFailure;
					me.myGetOwnerTree().fireEvent('edition', me.myGetOwnerTree().view, item);
				});
		}
		else
		{
			myDesktopApp.errorMsg('tree is undefined');
		}
	},

/*
*       load the rootNode and recursively all its child nodes
*       to know all names of DerivedParameters
*/
	editLeaf: function(onReady) 
	{ 		
		var me = this;
		amdaModel.InteractiveNode.preloadNodes(this.getRootNode(),
		function()
		{
			if (me.get('object')) 
			{
				// launch edition of parameter into parameter module
				me.editInModule(null, onReady);
			}
			else
			{				
				// call the ext method to get the details of parameter
				// the edition of real parameter is done into callback method getObjectCallback   
				if (onReady) 
					me.onReady = onReady;
															
				AmdaAction.getObject(me.get('id'), me.get('nodeType'), me.getObjectCallback, me);
			}
		});		
	},

/*
* 
*/
	getObjectCallback : function(result,remoteEvent)
	{
		var t = remoteEvent.getTransaction();
		
		if (result) {  
			var paramObj = Ext.create(this.get('objectDataModel'), result);
			// set parameter into node
			this.set('object',paramObj);
			// Edition of parameter into parameter Module	
			this.editInModule(null, this.onReady);
		} 
		else {
		// EXCEPTION : parameter not found !?
			myDesktopApp.errorMsg(t.action + "." + t.method + " : No parameter '"
			+ this.get('name') + "' found!");          
		}
	} 	
});