
dojo.provide("dojo.widget.TreeRpcControllerV3");

dojo.require("dojo.event.*");
dojo.require("dojo.json")
dojo.require("dojo.io.*");
dojo.require("dojo.widget.TreeLoadingControllerV3");

dojo.widget.defineWidget(
	"dojo.widget.TreeRpcControllerV3",
	dojo.widget.TreeLoadingControllerV3,
{
	// TODO: do something with addChild / setChild, so that RpcController become able
	// to hook on this and report to server

	extraRpcOnEdit: false,
				
	/**
	 * Make request to server about moving children.
	 *
	 * Request returns "true" if move succeeded,
	 * object with error field if failed
	 *
	 * I can't leave DragObject floating until async request returns, need to return false/true
	 * so making it sync way...
	 *
	 * Also, "loading" icon is not shown until function finishes execution, so no indication for remote request.
	*/
	doMove: function(child, newParent, index, sync){

		//if (newParent.isTreeNode) newParent.markLoading();

		
		var params = {
			// where from
			child: this.getInfo(child),
			childTree: this.getInfo(child.tree),
			oldParent: this.getInfo(child.parent),
			oldParentTree: this.getInfo(child.parent.tree),
			// where to
			newParent: this.getInfo(newParent),
			newParentTree: this.getInfo(newParent.tree),
			newIndex: index
		};


		var deferred = this.runRpc({		
			url: this.getRpcUrl('move'),
			sync: sync,			
			params: params
		});

		var _this = this;
		var args = arguments;	
		
		//deferred.addCallback(function(res) { dojo.debug("doMove fired "+res); return res});
		
		deferred.addCallback(function() {			
			dojo.widget.TreeBasicControllerV3.prototype.doMove.apply(_this,args);
		});

		
		return deferred;
	},

	// -------------- detach
	
	prepareDetach: function(node, sync) {
		var deferred = this.startProcessing(node);		
		return deferred;
	},
	
	finalizeDetach: function(node) {
		this.finishProcessing(node);
	},

	doDetach: function(node, sync){

		
		var params = {
			node: this.getInfo(node),
			tree: this.getInfo(node.tree)
		}

		var deferred = this.runRpc({
			url: this.getRpcUrl('detach'),
			sync: sync,
			params: params			
		});
		
		
		var _this = this;
		var args = arguments;
		
		deferred.addCallback(function() {			
			dojo.widget.TreeBasicControllerV3.prototype.doDetach.apply(_this,args);
		});
		
						
		return deferred;

	},

	// -------------------------- Inline edit node ---------------------	

	/**
	 * send edit start request if needed
	 * useful for server-side locking 
	 */
	requestEditConfirmation: function(node, action, sync) {
		if (!this.extraRpcOnEdit) {			
			return dojo.Deferred.prototype.makeCalled();
		}
	
		//dojo.debug("requestEditConfirmation "+node+" "+action);
		
		var _this = this;
	
		var deferred = this.startProcessing(node);
			
		//dojo.debug("startProcessing "+node);
		
		var params = {
			node: this.getInfo(node),
			tree: this.getInfo(node.tree)
		}
		
		deferred.addCallback(function() {
			//dojo.debug("add action on requestEditConfirmation "+action);
			return _this.runRpc({
				url: _this.getRpcUrl(action),
				sync: sync,
				params: params			
			});
		});
		
		
		deferred.addBoth(function(r) {
			//dojo.debug("finish rpc with "+r);
			_this.finishProcessing(node);
			return r;
		});
	
		return deferred;
	},
	
	editLabelSave: function(node, newContent, sync) {
		var deferred = this.startProcessing(node);
						
		var _this = this;
		
		var params = {
			node: this.getInfo(node),
			tree: this.getInfo(node.tree),
			newContent: newContent
		}
		
	
		deferred.addCallback(function() {
			return _this.runRpc({
				url: _this.getRpcUrl('editLabelSave'),
				sync: sync,
				params: params			
			});
		});
		
		
		deferred.addBoth(function(r) {
			_this.finishProcessing(node);
			return r;
		});
	
		return deferred;
	},
	
	editLabelStart: function(node, sync) {		
		if (!this.canEditLabel(node)) {
			return false;
		}
		
		var _this = this;
		
		if (!this.editor.isClosed()) {
			//dojo.debug("editLabelStart editor open");
			var deferred = this.editLabelFinish(this.editor.saveOnBlur, sync);
			deferred.addCallback(function() {
				return _this.editLabelStart(node, sync);
			});
			return deferred;
		}
						
		//dojo.debug("editLabelStart closed, request");
		var deferred = this.requestEditConfirmation(node, 'editLabelStart', sync);
		
		deferred.addCallback(function() {
			//dojo.debug("start edit");
			_this.doEditLabelStart(node);
		});
	
		
		return deferred;
	
	},

	editLabelFinish: function(save, sync) {
		var _this = this;
		
		var node = this.editor.node;
		
		var deferred = dojo.Deferred.prototype.makeCalled();
		
		if (!save && !node.isPhantom) {
			deferred = this.requestEditConfirmation(this.editor.node,'editLabelFinishCancel', sync);
		}
		
		if (save) {
			if (node.isPhantom) {
				deferred = this.sendCreateChildRequest(
					node.parent,
					node.getParentIndex(),
					{title:this.editor.getContents()},
					sync
				);
			} else {				
				// this deferred has new information from server
				deferred = this.editLabelSave(node, this.editor.getContents(), sync);
			}
		}
		
		deferred.addCallback(function(server_data) {			
			_this.doEditLabelFinish(save, server_data);
		});
		
		deferred.addErrback(function(r) {
			//dojo.debug("Error occured");
			//dojo.debugShallow(r);
			_this.doEditLabelFinish(false);
			return false;
		});
		
		return deferred;
	},
	
			
	
	/**
	 * TODO: merge server-side info
	 */
	createAndEdit: function(parent, index, sync) {
		var data = {title:parent.tree.defaultChildTitle};
		
		if (!this.canCreateChild(parent, index, data)) {
			return false;
		}
		
		/* close editor first */
		if (!this.editor.isClosed()) {
			//dojo.debug("editLabelStart editor open");
			var deferred = this.editLabelFinish(this.editor.saveOnBlur, sync);
			deferred.addCallback(function() {
				return _this.createAndEdit(parent, index, sync);
			});
			return deferred;
		}
			
		var _this = this;
		
		/* load parent and create child*/
		var deferred = this.prepareCreateChild(parent, index, data, sync);
		
		
		deferred.addCallback(function() {
			var child = _this.makeDefaultNode(parent, index);			
			child.isPhantom = true;
			return child;
		});
		
		
		deferred.addBoth(function(r) {
			_this.finalizeCreateChild(parent, index, data, sync);
			return r;
		});
		
		/* expand parent */
		deferred.addCallback(function(child) {
			var d = _this.exposeCreateChild(parent, index, data, sync);
			d.addCallback(function() { return child });
			return d;
		});
		
		
		deferred.addCallback(function(child) {
			//dojo.debug("start edit");
			_this.doEditLabelStart(child);
			return child;
		});
		
		
		
		return deferred;
	
	},

	prepareDestroyChild: function(node, sync) {
		//dojo.debug(node);
		var deferred = this.startProcessing(node);		
		return deferred;
	},
	
	finalizeDestroyChild: function(node) {
		this.finishProcessing(node);
	},
		

	doDestroyChild: function(node, sync){

		
		var params = {
			node: this.getInfo(node),
			tree: this.getInfo(node.tree)
		}

		var deferred = this.runRpc({
			url: this.getRpcUrl('destroyChild'),
			sync: sync,
			params: params			
		});
		
		
		var _this = this;
		var args = arguments;
		
		deferred.addCallback(function() {			
			dojo.widget.TreeBasicControllerV3.prototype.doDestroyChild.apply(_this,args);
		});
		
						
		return deferred;

	},

	// -----------------------------------------------------------------------------
	//                             Create node stuff
	// -----------------------------------------------------------------------------
	sendCreateChildRequest: function(parent, index, data, sync) {
		var params = {
			tree: this.getInfo(parent.tree),
			parent: this.getInfo(parent),
			index: index,
			data: data
		}

		var deferred = this.runRpc({
			url: this.getRpcUrl('createChild'),
			sync: sync,
			params: params
		});
		
		return deferred;
	},
		

	doCreateChild: function(parent, index, data, sync){		
		
		if (dojo.lang.isUndefined(data.title)) {
			data.title = parent.tree.defaultChildTitle;
		}

		var deferred = this.sendCreateChildRequest(parent,index,data,sync);
		
		var _this = this;
		var args = arguments;
		
		
		deferred.addCallback(function(server_data) {
			dojo.lang.mixin(data, server_data); // add my data as less priority
			//dojo.debug("Create ");
			//dojo.debug(server_data);
			return dojo.widget.TreeBasicControllerV3.prototype.doCreateChild.call(_this,parent,index,data);
		});
		
						
		return deferred;
	},
	
	// TODO: merge server data into cloned node, like in createChild	
	doClone: function(child, newParent, index, deep, sync) {
		
		var params = {
			child: this.getInfo(child),
			oldParent: this.getInfo(child.parent),
			oldParentTree: this.getInfo(child.parent.tree),
			newParent: this.getInfo(newParent),
			newParentTree: this.getInfo(newParent.tree),
			index: index,
			deep: deep ? true : false, // undefined -> false
			tree: this.getInfo(child.tree)
		}
		
		
		var deferred = this.runRpc({
			url: this.getRpcUrl('clone'),
			sync: sync,
			params: params
		});
		
		var _this = this;
		var args = arguments;
		
		deferred.addCallback(function() {			
			dojo.widget.TreeBasicControllerV3.prototype.doClone.apply(_this,args);
		});
		
						
		return deferred;	
	}

	
});
