Ext.data.DWRProxy = function(dwrCall, pagingAndSort) {
	Ext.data.DWRProxy.superclass.constructor.call(this);
	this.dwrCall = dwrCall;
	//this.args = args;
	this.pagingAndSort = !(pagingAndSort===false);
};

Ext.extend(Ext.data.DWRProxy, Ext.data.DataProxy, {
	load: 
		function(params, reader, callback, scope, arg) {
			if(this.fireEvent("beforeload", this, params) !== false) {
				//var sort = (params.sort && params.dir) ? params.sort + ' ' + params.dir : '';
				var callParams = new Array();
				if(arg.arg) {
					callParams = arg.arg.slice();
				}
				
				if(this.pagingAndSort) {
					callParams.push(params.start);
					callParams.push(params.limit);
					callParams.push(params.sort);
					callParams.push(params.dir ? params.dir == 'DESC' : false);
				}
			
				callParams.push(new DWRCallback({
					callback: this.loadResponse.createDelegate(this, [reader, callback, scope, arg], 1),
					errorHandler: this.errorHandler.createDelegate(this, [callback, scope, arg], 3)
				}));
				this.dwrCall.apply(this, callParams);
			} else {
				callback.call(scope || this, null, arg, false);
			}
		},

	loadResponse: 
		function(response, reader, callback, scope, arg) {
			var result;
			try {
				result = reader.readRecords(response);
			} catch(e) {
				this.fireEvent("loadexception", this, null, response, e);
				callback.call(scope, null, arg, false);
				return;
			}
			this.fireEvent("load", this, response, arg);
			callback.call(scope, result, arg, true);
		},

	errorHandler:
		function(msg, e, dealed, callback, scope, arg) {
			this.fireEvent("loadexception", this, null, msg, e);
			callback.call(scope, null, arg, false);
			if (!dealed) {
				Ext.MessageBox.alertException(msg, e);
			}
			return false;
		},

	update: 
		function(dataSet) {
			alert(dataSet);
		},

	updateResponse: 
		function(dataSet) {
		}
});

Ext.data.ObjectReader = function(meta, recordType) {   
	Ext.data.ObjectReader.superclass.constructor.call(this, meta, recordType);   
}   
   
Ext.extend(Ext.data.ObjectReader, Ext.data.DataReader, {   
	getJsonAccessor: 
		function() {
			var re = /[\[\.]/;
			return function(expr) {
				try {
					return(re.test(expr)) ? new Function("obj", "return obj." + expr) : function(obj) { return obj[expr]; };
				} catch(e) {
				}
				return Ext.emptyFn;
			};
		}(),
	readRecords: 
		function(o) {
			var recordType = this.recordType, fields = recordType.prototype.fields;

			//Generate extraction functions for the total property, success property, the root, the id, and for each field
			if (!this.ef) {
				if(this.meta.data) {
					this.getData = this.getJsonAccessor(this.meta.data);
					if(this.meta.total) {
						this.getTotal = this.getJsonAccessor(this.meta.total);
					}
				}

				if(this.meta.success) {
					this.getSuccess = this.getJsonAccessor(this.meta.success);
				}

				if (this.meta.id) {
					var g = this.getJsonAccessor(this.meta.id);
					this.getId = function(rec) {
						var r = g(rec);
						return (r === undefined || r === "") ? null : r;
					};
				} else {
					this.getId = function(){return null;};
				}
				
				this.ef = [];
				for(var i = 0; i < fields.length; i++) {
					var f = fields.items[i];
					var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
					this.ef[i] = this.getJsonAccessor(map);
				}
			}

			var records = [];
			var root = this.meta.data ? this.getData(o) : (o.constructor==Array? o : [ o ]);
			var c = root.length, totalRecords = c, success = true;
	
			if(this.meta.total){
				var v = parseInt(this.getTotal(o), 10);
				if(!isNaN(v)){
					totalRecords = v;
				}
			}
	
			if(this.meta.success){
				var v = this.getSuccess(o);
				if(v === false || v === 'false'){
					success = false;
				}
			}
			var defaultValuesLength=0;
			if(this.meta.defaultValues)
			{
				var defaultValues=[].concat(this.meta.defaultValues);
				defaultValuesLength=defaultValues.length;
				for(var i=0;i<defaultValuesLength;i++)
				{
					var n=defaultValues[i];
					var values = {};
					var id = this.getId(n);
					for(var j = 0; j < fields.length; j++) {
						var f = fields.items[j];
						var v = this.ef[j](n); 
						values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
					}
					var record = new recordType(values, id);
					records[i] = record;
				}
			}

			for(var i = 0; i < c; i++){
				var n = root[i];
				var values = {};
				var id = this.getId(n);
				for(var j = 0; j < fields.length; j++) {
					var f = fields.items[j];
					var v = this.ef[j](n); 
					values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
				}
				var record = new recordType(values, id);
				records[i+defaultValuesLength] = record;
			}

			return {
				success: success,
				records: records,
				totalRecords: totalRecords
			};
		}
});

Ext.tree.DWRTreeLoader = function(config) {
	Ext.apply(this, config, {
		handleNodeData: function(data) {
			return data;
		}
	});

	this.addEvents(
		/**
		 * @event beforeload
		 * Fires before a network request is made to call dwr function to receive the information which specifies a node's children.
		 * @param {Object} This TreeLoader object.
		 * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
		 * @param {Object} callback The callback function specified in the {@link #load} call.
		 */
		"beforeload",
		/**
		 * @event load
		 * Fires when the node has been successfuly loaded.
		 * @param {Object} This TreeLoader object.
		 * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
		 * @param {Object} response The response object containing the data from the server.
		 */
		"load",
		/**
		 * @event loadexception
		 * Fires if the network request failed.
		 * @param {Object} This TreeLoader object.
		 * @param {Object} node The {@link Ext.tree.TreeNode} object being loaded.
		 * @param {Object} msg The error msg returned from the server.
		 * @param {Object} e The exception object returned from the server.
		 */
		"loadexception"
	);

	Ext.tree.DWRTreeLoader.superclass.constructor.call(this);
};

Ext.extend(Ext.tree.DWRTreeLoader, Ext.util.Observable, {
	/**
	* @cfg {String} dataUrl The URL from which to request a Json string which
	* specifies an array of node definition objects representing the child nodes
	* to be loaded.
	*/
	/**
	 * @cfg {String} requestMethod The HTTP request method for loading data (defaults to 'POST').
	 */
	/**
	 * @cfg {String} url Equivalent to {@link #dataUrl}.
	 */
	/**
	 * @cfg {Boolean} preloadChildren If set to true, the loader recursively loads "children" attributes when doing the first load on nodes.
	 */
	/**
	* @cfg {Object} baseParams (optional) An object containing properties which
	* specify HTTP parameters to be passed to each request for child nodes.
	*/
	/**
	* @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
	* created by this loader. If the attributes sent by the server have an attribute in this object,
	* they take priority.
	*/
	/**
	* @cfg {Object} uiProviders (optional) An object containing properties which
	* specify custom {@link Ext.tree.TreeNodeUI} implementations. If the optional
	* <i>uiProvider</i> attribute of a returned child node is a string rather
	* than a reference to a TreeNodeUI implementation, this that string value
	* is used as a property name in the uiProviders object.
	*/
	uiProviders: {},

	/**
	* @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
	* child nodes before loading.
	*/
	clearOnLoad: true,
	/**
	* @cfg {Function} handleNodeChildrenData (optional) 
	*/
	handleNodeChildrenData: Ext.emptyFn,

	/**
	 * Load an {@link Ext.tree.TreeNode} from the URL specified in the constructor.
	 * This is called automatically when a node is expanded, but may be used to reload
	 * a node (or append new children if the {@link #clearOnLoad} option is false.)
	 * @param {Ext.tree.TreeNode} node
	 * @param {Function} callback
	 */
	load: function(node, callback){
		if(this.clearOnLoad){
			while(node.firstChild){
				node.removeChild(node.firstChild);
			}
		}
		if(this.doPreload(node)){ // preloaded json children
			if(typeof callback == "function"){
				callback();
			}
		}else if(this.dwrCall){
			this.requestData(node, callback);
		}
	},

	doPreload : function(node){
		if(node.attributes.children){
			if(node.childNodes.length < 1){ // preloaded?
				var cs = node.attributes.children;
				node.beginUpdate();
				for(var i = 0, len = cs.length; i < len; i++){
					var cn = node.appendChild(this.createNode(cs[i]));
					if(this.preloadChildren){
						this.doPreload(cn);
					}
				}
				node.endUpdate();
			}
			return true;
		}else {
			return false;
		}
	},

	requestData : function(node, callback){
		if(this.fireEvent("beforeload", this, node, callback) !== false){
			var callParams = [];
			if (this.baseParams) {
				callParams = callParams.concat(this.baseParams);
			}
			callParams.push(new DWRCallback({
				callback: this.handleResponse.createDelegate(this, [ node, callback], 1),
				errorHandler: this.handleFailure.createDelegate(this, [node, callback], 2)
			}));
			this.loading = true;
			this.dwrCall.apply(this, callParams);
		} else {
			// if the load is cancelled, make sure we notify
			// the node that we are done
			if(typeof callback == "function"){
				callback();
			}
		}
	},

	isLoading : function(){
		return this.loading ? true : false;
	},

	/**
	* Override this function for custom TreeNode node implementation
	*/
	createNode : function(attr){
		// apply baseAttrs, nice idea Corey!
		if(this.baseAttrs){
			Ext.applyIf(attr, this.baseAttrs);
		}
		if(this.applyLoader !== false){
			attr.loader = this;
		}
		if(typeof attr.uiProvider == 'string'){
		   attr.uiProvider = this.uiProviders[attr.uiProvider] || eval(attr.uiProvider);
		}
		return(attr.leaf ?
						new Ext.tree.TreeNode(attr) :
						new Ext.tree.AsyncTreeNode(attr));
	},

	processResponse: function(response, node, callback) {
		this.processResponse2(response, node);
		try {
			if(typeof callback == "function"){
				callback(this, node);
			}
		}catch(e){
			this.handleFailure(response);
		}
	},
	
	processResponse2: function(response, node) {
		if (response) {
			var o = response.constructor==Array ? response : [ response ];
			node.beginUpdate();
			for(var i = 0, len = o.length; i < len; i++) {
				var d = this.handleNodeData(o[i]);
				if (d) {
					var n = this.createNode(d);
					if (n) {
						node.appendChild(n);
						if (!n.isLeaf()) {
							var leafData = this.handleNodeChildrenData(o[i]);
							if (leafData) {
								this.processResponse2(leafData, n);
								n.loaded = true;
							}
						}
					}
				}
			}
			node.endUpdate();
		}
	},

	handleResponse: function(response, node, callback){
		this.loading = false;
		this.processResponse(response, node, callback);
		this.fireEvent("load", this, node, response);
	},

	handleFailure : function(msg, e, node, callback){
		this.loading = false;
		this.fireEvent("loadexception", this, node, msg, e);
		if(typeof callback == "function"){
			callback(this, node);
		}
	}
});

