var CleverNodeClass = Class.create({
	initialize : function() {
		this.Node = {user:{},perm:{}};
		this.IncludedFiles = {};
		this.IncludedQueue = [];
		this.MsgHookQueue = [];
		this.Plugin = {};
		this.PluginCache = false;
		this.MsgHooks = [];
		this.PluginLastCompile = 0;
		this.PageLoaded = false;
		this.WaitForPageLoad = Prototype.Browser.IE;
		this.Debug = false;
		Event.observe(window, 'load', this.onLoad.bind(this));
	},
	configure : function(Node)	{
		this.Node = Object.extend(this.Node, Node || {});
		this.Debug = (this.Node.user && parseInt(this.Node.user.debug))?true:false;
		this.PluginLastCompile = parseInt(this.Node.globals['COMPILE_PLUGIN']);
		if(this.PluginCache==false)
			this.include_once('?mode=none&action=getpluginjscache&style=json&json[dojsfunction]=CleverNode.ConfigurePluginCache&json[dojsfunctionformat]=js&json[datapath]=result.cache',{type:'js'});
	},
	doPluginAction : function() {
	try{
		var vars = $A(arguments);
		var name = vars[0];
		if(!name && this.Debug)	this.doMsg({msg:'Error in function('+name+','+vars.join(',')+')',status:false,debug:true});
		if(!this.Plugin[name] || !this.Plugin[name].Class)
		{
			this.configurePlugin(name);	
			if(!this.Plugin[name].ActionQueue) this.Plugin[name].ActionQueue = [];
			this.Plugin[name].ActionQueue[this.Plugin[name].ActionQueue.length] = vars;
		}
		else
		{
			if(this.Plugin[name].Class.doAction) 
			{
				var n = vars.shift();
				// TODO: Check for existing method first. If none, try doAction
				var result=this.Plugin[name].Class.doAction.apply(this.Plugin[name].Class,vars);
				if(typeof(result)!='object') result = {msg:result,status:true};
				if(this.Debug) this.doMsg({msg:"Plugin Action: "+name+".doAction("+vars.join(',')+") => "+result.msg,debug:true,status:result.status});
			}
			else this.doMsg({msg:"Plugin "+name+" does not have a 'doAction' method to handle '"+vars[0]+"'",status:false});
		}
		}catch(e){this.doMsg('Error in doPluginAction('+name+','+vars.join(',')+'):'+e,false)}	
	},
	configurePlugin : function (name){
	try{
		if(this.Plugin[name]) return !this.Plugin[name].Initiated; // Already configured or is configuring.
		this.Plugin[name] = {'Initiated':false,'Options':{}};
		var config = this.PluginCache[name];
		if(config && config.status==true ) // parseInt(config.LastCompile) >= this.PluginLastCompile ) // TODO: Check value to see if its valid
			this.ConfigurePluginData(name);
		else if(1)
			request = new Ajax.Request('?mode=none&style=json&action=getplugin&name='+name+'&json[datapath]=result.Javascript', 
			{'onComplete': this.configurePlugin_Response.bind(this,name),'method':'Get'	});

		else this.doMsg("Unable to retrieve config options for '"+name+"'",false);
		return true;
		}catch(e){this.doMsg('Error in configurePlugin('+name+'):'+e,false)}	
	},
	configurePlugin_Response : function (name,response){
	try{		
		var json = response.responseText.gsub(/[\n\t]/,'');
		var config = json.evalJSON();
		if(!config || !config.status) return this.doMsg("Invalid Config Options for Plugin '"+name+"'.",false);
		this.PluginCache[name] = config;
		this.ConfigurePluginData(name);
		} catch(e) {CleverNode.doMsg("Error: Plugin not loaded. Config Query for '"+name+"' has an invalid format:\n" + e,false)};
	},
	ConfigurePluginCache : function (cache,response)
	{
		if(response && response.responseText)
		{
			var json = response.responseText.gsub(/[\n\t]/,'');
			var config = json.evalJSON();
			this.doMsg(config.result);
			cache = config.result.cache;
		}
		this.PluginCache = Object.extend(cache, this.PluginCache || {})
	},
	ConfigurePluginData : function (name,config){
	try{
		if(!config) config = this.PluginCache[name];
		else this.PluginCache[name] = config;
		if(!this.Plugin[name]) 		this.Plugin[name] = {'Initiated':false,'Options':{}};
		if(config.Path) this.Plugin[name].Options.Path = config.Path;
		var path = this.Plugin[name].Options.Path;
		this.Plugin[name].Options.Config = config;
		if(!config.SkipIncludes)
		{
			if(config['ClassInclude'])
			{
				if(typeof(config['ClassInclude'])!='array') config['ClassInclude'] = [config['ClassInclude']];
				for(var i = 0;i<config['ClassInclude'].length;i++)
					if(!this.Plugin[config['ClassInclude'][i]]) 
						this.configurePlugin(config['ClassInclude'][i]);
					// TODO: What if theres an ajax query for the cache?
			}
					
			if(config['Includes'])
				for(var i = 0;i<config['Includes'].length;i++)
					this.include_once(path+config['Includes'][i]);
			if(config['ClassPath'])
				this.include(path+config['ClassPath']);	
			if(config['ClassInitiate'])
				this.include_data('script',{'innerHTML':"CleverNode.init"+(config['ClassInitiate']=='object'?"Object":"Class")+"('"+name+"', "+config['ClassName']+");"});	
		}
		}catch(e){this.doMsg('Error in ConfigurePluginData('+name+'):'+e,false)}		
	},
	createPlugin : function(name,classConfig)
	{
		var newClass = Class.create(CleverNodePluginClass, classConfig);
		return this.initClass(name,newClass);		
	},
	setOnPluginLoad : function(name,callback,args)
	{
		var plugin = this.Plugin[name];
		if(!plugin) this.configurePlugin(name,{});
		if(plugin.Initiated) 
			return callback();
		else 
		{
			if(!plugin.OnInitiate) plugin.OnInitiate = [];
			plugin.OnInitiate[plugin.OnInitiate.length] = [callback,args];
		}
		return {'status':true,'msg':"Command Queued for Plugin '"+name+"'"};
	},
	initClass : function(name,newClass)
	{
		this.Plugin[name].Class = new newClass(this.Plugin[name].Options);
		return this.initObject(name,this.Plugin[name].Class);
	},
	initObject : function(name,object)
	{
		var plugin = this.Plugin[name];
		if(!plugin) return this.configurePlugin(name,{});
		plugin.Class = object;
		if(!object.result) object.result = {msg:'Successfully Loaded Plugin '+name+'.',debug:true,status:true};
		if(!object.result.debug) object.result.debug = true;
		this.doMsg(object.result);
		if(plugin.Options.Config['ClassInclude'])
		{
			var classes = plugin.Options.Config['ClassInclude'];
			for(var i = 0;i<classes.length;i++)
				if(this.Plugin[classes[i]]) plugin.Class[classes[i]] = this.Plugin[classes[i]].Class;
				else this.doMsg("Class Include "+classes[i]+" not found for Class "+name+" after initiation.",false);
		}
		
		plugin.Initiated = true;
		if(plugin.ActionQueue && plugin.Class.doAction)
			for(var i = 0; i<plugin.ActionQueue.length; i++)
				this.doPluginAction.apply(CleverNode,plugin.ActionQueue[i]);				
		plugin.ActionQueue = [];
		
		if(plugin.OnInitiate)
			for(var i = 0; i<plugin.OnInitiate.length; i++)
			{
				try{
				plugin.OnInitiate[i][0].apply(CleverNode,plugin.OnInitiate[i][1]||[]);	
				}catch(e){this.doMsg("OnInitiate Error ("+name+"):"+ e.description+" ("+plugin.OnInitiate[i][0]+").apply("+CleverNode+","+plugin.OnInitiate[i][1]+")");}
			}
		plugin.OnInitiate = [];
				
		return plugin.Class;
	},
	setupFileIncludes : function ()
	{
		for(var i = 0;i<this.IncludedQueue.length;i++)
			this.include_data(this.IncludedQueue[i].type,this.IncludedQueue[i].opts);
		this.IncludedQueue = [];
	},
	setupMsgHooks : function ()
	{
		for(var i = 0;i<this.MsgHookQueue.length;i++)
			this.setup_msg_callback(this.MsgHookQueue[i].func,this.MsgHookQueue[i].showexisting);
		this.MsgHookQueue = [];
	},
	onLoad : function ()
	{
		this.PageLoaded = true;
		this.setupFileIncludes();
		this.setupMsgHooks();
	},
	setCookie: function(value, parameters) {
		document.cookie= parameters['name'] + "=" + escape(value) +
		((parameters['expires']) ? "; expires=" + parameters['expires'].toGMTString() : "") +
		((parameters['path']) ? "; path=" + parameters['path'] : "; path=/") +
		((parameters['domain']) ? "; domain=" + parameters['domain'] : "") +
		((parameters['secure']) ? "; secure" : "");
	},
	getCookie: function(name) {
		var dc = document.cookie;
		var prefix = name + "=";
		var begin = dc.indexOf("; " + prefix);
		if (begin == -1) {
		  begin = dc.indexOf(prefix);
		  if (begin != 0) return null;
		} else {
		  begin += 2;
		}
		var end = document.cookie.indexOf(";", begin);
		if (end == -1) {
		  end = dc.length;
		}
		return unescape(dc.substring(begin + prefix.length, end));
	},
	doLoginForm : function(form)
	{
		request = new Ajax.Request($(form).readAttribute('action')+'&style=json&mode=none&json[defermsgs]=1', 
		{
			parameters: form.serialize(true), 
  			onComplete: this.doLoginFormResponse.bind(this,form)
		});
		return false;
	},
	doLoginFormResponse : function(form,response)
	{
		var node = response.responseText?response.responseText.evalJSON():{};
		if(!node || !node.result)
		{
			this.doMsg("Error reading server response. Submitting login request...");
			form.submit();
			return false;
		}
		this.doMsg(node.result);
		if(node.result.status)
		{
			this.doMsg("Redirecting ...");
			setTimeout("document.location.href = '"+ form.readAttribute('action')+"';" , 2000);
		}
	},
	getRandString: function (string_length) {
		var chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz";
		if(!string_length || string_length<=0) string_length = 32;
		var randomstring = '';
		for (var i=0; i<string_length; i++) {
			var rnum = Math.floor(Math.random() * chars.length);
			randomstring += chars.substring(rnum,rnum+1);
		}
		return randomstring;
	},
	updateVerifyKey: function(newkey) {
		if(!newkey) return false;
		if(!this.Node.user) this.Node.user = {}
		this.Node.user.verifykey = newkey;
		return true;
	},
	getVerifyKey: function(newkey) {
		return this.Node.user.verifykey;
	},
	include_data : function (type,opts) {
	try{
		if(!opts) opts = {};
			
		if((this.WaitForPageLoad && !this.PageLoaded) && !opts['immediate'])
		{
			this.IncludedQueue[this.IncludedQueue.length] = {'type':type,'opts':opts};
			//Event.observe(window, 'load', this.include.bind(this,file,immediate));		
			return true;
		}
		var html_doc = document.getElementsByTagName('head').item(0);
		var innerHTML = opts['innerHTML'];
		if(innerHTML) 
			opts['innerHTML'] = null;
			
		//if(type=='script') alert(document.getElementsByTagName('head').item(0).innerHTML);
		if(type=='script') opts = Object.extend({'language':'javascript','type':'text/javascript'},opts);
		if(type=='link') opts = Object.extend({'rel':'stylesheet','type':'text/css'},opts);
		
		var el;
		el = new Element(type,opts);
		if(innerHTML) 
			el.appendChild(document.createTextNode(innerHTML));
		html_doc.appendChild(el);
		return true;			
		}catch(e){this.doMsg('Error in include_data:'+e,false)}	
	},
	include : function (filepath,config) {
		var opts,type;
		var fileInfo;
		if(config && config.type) fileInfo = [filepath,filepath,config.type];
		else fileInfo=filepath.match(/[\/|\\]([^\\\/]+[.]([a-zA-Z0-9]+))$/);
		fileInfo[2] = fileInfo[2].toLowerCase();
		if(fileInfo[2]=='js') 
		{
			opts = {'src':filepath};
			type = 'script';
		}
		else if(fileInfo[2]=='css') 
		{
			opts = {'href':filepath};
			type = 'link';
		}		
		else return this.doMsg("File type is not supported: "+fileInfo[2],false) && false;
		this.IncludedFiles[filepath] = 1;
		//this.doMsg("Successfully included file "+fileInfo[1]);
		return this.include_data(type,opts);
	},
	include_once : function (filepath,config){
		var found = false;
		if(this.IncludedFiles[filepath]) return this.doMsg({msg:"File '"+filepath+"' has already been included.",debug:true}) && false;
		$$('script').each(function (script) {				
			if(script.src && (filepath.indexOf(script.src)>=0 || script.src.indexOf(filepath)>=0)) found = true;	
			//if(found) alert("File '"+filepath+"' has already been included.");					
		});
		if(found) return this.doMsg({msg:"File '"+filepath+"' has already been included.",debug:true}) && false;  
		else return this.include(filepath,config||{});		
	},
	setup_msg_box : function(elm,config)
	{
		config = Object.extend({'show_existing':true},config||{});
		this.setup_msg_callback(function (msgdata)
		{
			if(msgdata.debug) return true;
			var MsgBox =$(elm);
			var div;
			//if(!MsgBox)	return alert(msgdata.msg);
			$(MsgBox,MsgBox.parentNode,MsgBox.parentNode.parentNode).invoke('show');
			MsgBox.appendChild(div = new Element('div',{'class':msgdata.status?'':'Error'}));
			div.appendChild(document.createTextNode(msgdata.msg));
			if(MsgBox.getHeight() >= 100)
			{
				if(!MsgBox.resized)
				{
					MsgBox.setStyle({'overflow':'hidden','height':'100px','overflowY':'scroll'});
					MsgBox.resized = true;
				}
				MsgBox.scrollTop = 9999999;
			}
			return true;
		}
		, config['show_existing']);
	},
	setup_msg_callback : function(func,showexisting)
	{
		if((this.WaitForPageLoad && !this.PageLoaded))
		{
			this.MsgHookQueue[this.MsgHookQueue.length] = {'func':func,'showexisting':showexisting};
			//Event.observe(window, 'load', this.include.bind(this,file,immediate));		
			return true;
		}
		this.MsgHooks[this.MsgHooks.length] = func;
		if(showexisting && this.Node.msg && this.Node.msg.length)
			for(var i=0;i<this.Node.msg.length;i++)
				if(typeof func == 'function') func(this.Node.msg[i]);
				else func[0][func[1]](this.Node.msg[i]);
		return true;
	},
	objToQueryParams : function(obj, forPHP, parentObject){
	   if( typeof obj != 'object' ) return '';
	
	   if (arguments.length == 1)
		  forPHP = /\.php$/.test(document.location.href);
	   
	   var rv = '';
	   for(var prop in obj) if (obj.hasOwnProperty(prop) ) {
	
		  var qname = parentObject
			 ? parentObject + '[' + prop + ']'
			 : prop;
	
		  // Expand Arrays
		  if (obj[prop] instanceof Array)
			 for( var i = 0; i < obj[prop].length; i++ )
				if( typeof obj[prop][i] == 'object' )
				   rv += '&' + this.objToQueryParams( obj[prop][i], forPHP, qname );
				else
				   rv += '&' + encodeURIComponent(qname) + (forPHP ? '[]' : '')
						+ '=' + encodeURIComponent( obj[prop][i] );
	
		  // Expand Dates
		  else if (obj[prop] instanceof Date)
			 rv += '&' + encodeURIComponent(qname) + '=' + obj[prop].getTime();
	
		  // Expand Objects
		  else if (obj[prop] instanceof Object)
			 // If they're String() or Number() etc
			 if (obj.toString && obj.toString !== Object.prototype.toString)
				rv += '&' + encodeURIComponent(qname) + '=' + encodeURIComponent( obj[prop].toString() );
			 // Otherwise, we want the raw properties
			 else
				rv += '&' + this.objToQueryParams(obj[prop], forPHP, qname);
	
		  // Output non-object
		  else
			 rv += '&' + encodeURIComponent(qname) + '=' + encodeURIComponent( obj[prop] );
	
	   }
	   return rv.replace(/^&/,'');
	},
	graft : function (container, data) {
		var e;
		var _elm = '';
		var res,result = {elements:{},status:true,msg:'Graft Successful'};
		var doc = (doc || container.ownerDocument || document);
		container = $(container);
		if(typeof(data) == 'undefined') 
			return({msg: "Can't graft an undefined value. Parent="+container.inspect().escapeHTML(),status:false,debug:false});
		else if(typeof(data) == 'string') 
			 new Insertion.Bottom(container, data);
		else {
			if(typeof(data[0])!='string' || !data[0])
				return({msg: "Can't graft an array/blank value",status:false,debug:false});
			if(data[1] && data[1]['return']) _elm = data[1]['return'];
			if(!_elm) _elm = '_last_'+data[0];
			e = new Element(data[0],data[1]);
			result.elements[_elm] = e;
			container.appendChild(e);
			if(data.length>2)
			for(var i = 2; i < data.length; i++)
			{
				res = this.graft(e,data[i]);
				if(!res.status) return res;
				result.elements = Object.extend(res.elements,result.elements);
			}
			result.top = e;
		}
		return result;
	},
	doMsgs : function (msgarray)
	{
		if(msgarray && msgarray.length)
			for(var i=0;i<msgarray.length;i++)
				this.doMsg(msgarray[i]);
	},
	doMsg : function (msgdata,status)
	{
		if(typeof(msgdata)!='object') msgdata = {'msg':msgdata,'status':typeof(status)=="undefined"?true:status}
		if(!this.Node.msg) this.Node.msg = [];
		this.Node.msg[this.Node.msg.length] = msgdata;
		//if(!this.Node.user) return alert(msgdata.msg);
		if((msgdata.debug || !msgdata.status) && this.Debug) 
		{	
			if(typeof(console)!='undefined') msgdata.status?console.debug(msgdata.msg):console.warn(msgdata.msg);	
			else msgdata.debug=0;
		}
		if(this.MsgHooks.length)
			for(var i=0;i<this.MsgHooks.length;i++)
				if(typeof this.MsgHooks[i] == 'function') this.MsgHooks[i](msgdata);
				else this.MsgHooks[i][0][this.MsgHooks[i][1]](msgdata);
	}
});
var CleverNode = new CleverNodeClass();


var CleverNodePluginClass = Class.create({
	doMsg : CleverNode.doMsg.bind(CleverNode),
	graft : CleverNode.graft.bind(CleverNode),
	objToQueryParams : CleverNode.objToQueryParams.bind(CleverNode)
});