/**
 *	@ScriptName: Tomato.js
 * @Function: the namespace manager and the script loader of the whole framework.
 * @Insparation	 : tomato at<wangshijun2010@gmail.com>
 * @Author: tomato at<wangshijun2010@gmail.com>
 * @CopyRight: All Reserved
 * @CreateDate: 2009-03-31
 */

var Administrator = new Native({
	name: "Administrator",
	initialize: function(options){
		//this.Widgets = {};
		//this.Controls = {};
		//this.Utilities = {};
		this.Attributte = new Hash;
		//this.Cookies = new Hash;
		this.Modules = options.modules;
		this.version = options.version || "1.0.0";
		this.mode = options.debug || false;
		this.console = !!window.console || !!window.firebug;
		var methods = ["get","set","include","erase","extend"];
		methods.each(function(method){
			this[method] = this.Attributte[method].bind(this.Attributte);
		}, this);
		this.autoLoad(options);
		return this;
	}
});

Administrator.implement({
	// methods to autoload certain modules
	autoLoad: function(options){
		this.language = options.language || "zh-CN";
		this.preloadCss = options.preloadCss || false;
		this.preloadScript = options.preloadScript || false;
		this.autoLoader = new ScriptLoader(this,options);
		this.autoLoader.loadModule($A(options.intialModules));
		this.set("imageBase", this.autoLoader.imageBase || false);
	},
	// debug functions
	log: function(message){
		if(this.console && this.mode) console.log(message);
	},
	// methods to add, list, load Modules
	loadModule: function(){
		this.autoLoader.loadModule($A(arguments));
	},
	listModule: function(json){
		var modules = $H(this.Modules);
		return $pick(json, false) ? modules.getKeys().toJSON() : modules.getKeys();
	},
	addModule: function(name, data){
		if($type(arguments[0]) == "array"){
			var modules = arguments[0];
			for(var index=0, length=modules.length; index<length; index++){
				var item = modules[index];
				this.addModule(item.name, item);
			}
		}else{
			// if required parameters are missing OR we already have a module with the same name,return false.
			if(!$defined(name) || !$defined(data) || this.Modules.hasOwnProperty(name)) return false;
			// if required module property is not specified, return false
			//if(!data.files || !data.folder || !data.files.length) return false;

			// we fetch the folder property of widgets set, to save some line of code,
			// besides, making the methods more intelligent to users
			if(name.indexOf(".") > 0){
				var folder = name.slice(0,name.indexOf(".")).capitalize();
				folder = (folder.lastIndexOf("s") > 0) ? folder : folder+"s";
				//this.log("auto fetched folder=>"+folder);
			}

			// add new value-kye pairs to the Modules
			this.Modules[name] = {
				files: (data.files && data.files.length) ? data.files : false,
				folder: data.folder || folder ||false,
				lang: data.lang || false,
				bases: (data.bases && data.bases.length) ? data.bases : false};
			// then update the module property of ScriptLoader
			this.autoLoader.modules = this.Modules;
		}
	}
});

// make some shortcuts method of the Administrator Object
Administrator.alias({"loadModule":"use", "addModule": "add", "listModule": "list"});


/**
 * ScriptLoader Class: which will load scripts automatically
 * it has 3 public accessed method: // NEEDS FURTHER IMPROVEMENT
 * @loadModule: load certain Modules that user provided
 * @preloadCss: preload css stylesheets,
 * @preloadScript: preload framework scripts,
 * @preloadAll: preload all files that make the framework work.
*/
var ScriptLoader = new Class({
	name: "scriptloader",
	initialize: function(master,options){
		this.master = master;
		this.modules = options.modules;
		this.language = options.language;
		this.langSuffix = options.langSuffix || ".lang.js";
		this.loadedModules = {};
		this.loadedFiles = {};
		this.styleBase = false;
		this.scriptBase =false;
		this.imageBase = false;
		this.scriptDir = false;
		this._fetchBaseDirs();
	},
	_fetchBaseDirs: function(){
		// find all scripts and filter the script loader of the script
		var sources = $$("script").filter(function(item){ return item.src && item.src.match(/tomato\.js$/);	});
		this.source = sources.length ?  sources[0] : false;
		// if the script loader is find, fetch relative path for css, js, and images files
		if(this.source){
			var slashIdx = this.source.src.lastIndexOf('/');
			var path = this.source.src.substring(0, slashIdx+1);
			var filename = this.source.src.substring(slashIdx+1);
			this.loadedFiles[filename] = true;
			// define path constants that will be used in other scripts
			this.scriptBase = path+"src/";
			this.styleBase = path+"css/";
			this.imageBase = path+"images/";
			this.langDir = path+"lang/"+this.language+"/";
		}
	},
	_fetchModuleDirs: function(folder){
		this.scriptDir = this.scriptBase+folder+"/";
		this.master.log("changed dir: "+this.scriptDir);
	},
	_loadFile: function(filename){
		if(this.loadedFiles[filename])
			return this.master.log("skipped file: "+filename);
		this.loadedFiles[filename] = true;
		var extension = filename.substr(filename.lastIndexOf('.')+1).toLowerCase();
		if(extension == "js"){
			this.master.log("loaded js file: "+filename);
			document.write('<script type="text/javascript" src="'+this.scriptDir+filename+'"></script>');
		}else if(extension == "css"){
			this.master.log("loaded css file: "+filename);
			var stylesheet = document.createElement("link");
			stylesheet.type = "text/css";
			stylesheet.rel = "stylesheet";
			stylesheet.href = this.styleBase+filename;
			document.getElementsByTagName('head')[0].appendChild(stylesheet);
		}
	},
	_loadLangPackage: function(filename){
		if(filename.contains(".")) filename = filename.split(".").last();
		this.master.log("loaded language file: "+filename+this.langSuffix);
		document.write('<script type="text/javascript" src="'+this.langDir+filename+this.langSuffix+'"></script>');
	},
	_loadFiles: function(folder,files){
		this._fetchModuleDirs(folder);
		files.each(function(file){
			this._loadFile(file);
		}, this);
	},
	_loadModuleSet: function(name){
		var modules = [];
		$H(this.modules).getKeys().each(function(module){
			if(module.contains(name)) modules.push(module);
		});
		this.loadModule(modules);
	},
	_fetchModule: function(name){
		// strict match if we have one
		if(this.modules[name]){
			this.modules[name].name = name;
			return this.modules[name];
		// loose mathch if we have one that is the fullname or dependency
		}else{
			var found = false;
			$H(this.modules).getKeys().each(function(module){
				if(module.contains(name)){
					this.modules[module].name = module;
					return found = module;
				}
			}, this);
			return this.modules[found];
		}
	},
	loadModule: function(modules){
		for(var index=0, length=modules.length; index<length; index++){
			var name = modules[index];
			var module = this._fetchModule(name);
			if(name.contains(".*")){
				this._loadModuleSet(name.substr(0, name.length-2));
			}else if(module && !this.loadedModules[module.name]){
				this.loadedModules[module.name] = true;
				//var module = this.modules[name];
				if(module.bases) this.loadModule($A(module.bases));
				if(module.lang || false) this._loadLangPackage(module.name);
				if(module.files) this._loadFiles(module.folder,$A(module.files));
				this.master.log("loaded module: "+module.name);
			}else{
				this.master.log("skipped module: "+module.name);
			}
		}
	}
});

/**
 * NameSpacer Class: an class to manage objects in an namespace manner,
 * 2 public methods, which respectively serve as the setter and the getter,
 * @set(path, value), the path must begins with a slash "/Options/Default"
 * @get(path), same to NameSpacer.set, the path must begins with a slash.
 */
var NameSpace = new Class({

	conf : {},

	set : function(path,value){
		var fragments = path.split('/');

		if(fragments.shift() !== '') return false; // remove empty, first component
		if(fragments.length > 0 && fragments[fragments.length - 1] == '') fragments.pop();

		var object = {}, reference = object, length = fragments.length;
		if(length > 0){
			for(index = 0; index < length-1; index++){
				reference[fragments[index]] = {};
				reference = reference[fragments[index]];
			}
			reference[fragments[length-1]] = value;
			this.conf = $merge(this.conf,object);
		}else{
			this.conf = value;
		}
		return this;
	},

	get : function(path){
		var fragments = path.split('/');

		if( fragments.shift() !== '') return null;
		if(fragments.length > 0 && fragments[fragments.length -1] == '') fragments.pop();

		var reference = this.conf, exists = true, index = 0, length = fragments.length;
		while(exists && index < length){
			exists = exists && (reference[fragments[index]] !== undefined);
			reference = reference[fragments[index]];
			index++;
		}
		return reference;
	}
});

// implement Administrator with NameSpace
Administrator.implement({
	// moved three attributes from the initializer here to make code more readable.
	Options: new NameSpace,
	Configs: new NameSpace,
	Packages: new NameSpace,
	// methods to modify the above attributes
	setOption: function(path, value){
		return this.Options.set(path,value);
	},
	getOption: function(path){
		return this.Options.get(path);
	},
	setConfig: function(path, value){
		return this.Configs.set(path,value);
	},
	getConfig: function(path){
		return this.Configs.get(path);
	},
	setPackage: function(path, value){
		return this.Packages.set(path,value);
	},
	getPackage: function(path){
		return this.Packages.get(path);
	}
});

// initialize the framework
var Tomato = new Administrator({
	debug: false,
	language: "zh-CN",
	//cookie: "tomato-cookies",
	intialModules: ["Core","Extras"],
	preloadCss: false,
	preloadScript: false,
	modules: {
		// Core Modules that contains the mootools core files and core style sheets
		"Core":		{ "files": ["Core.Reset.css","Core.Global.css","Core.Debug.css","Core.js"], "folder": "Core", "lang": true},
		"Extras":		{ "files": ["Class.js", "Fx.js", "Array.js", "Element.js","String.js"], "folder": "Extras"}
	}
});

// add other modules to the framework
(function(){
	// Other Extras modules
	Tomato.add([{
			"name": "Extras.Assets",
			"files": ["Assets.js"]
		},{
			"name": "Extras.Browser",
			"files": ["Browser.js"]
		},{
			"name": "Extras.Cookie",
			"files": ["Cookie.js"]
		},{
			"name": "Extras.Color",
			"files": ["Color.js"]
		},{
			"name": "Extras.HotKey",
			"files": ["HotKey.js"]
		},{
			"name": "Extras.Group",
			"files": ["Group.js"]
		}]);
	// Mootools Drag and Effect Extensions
	Tomato.addModule("Drag", { "files": ["Drag.js"], "folder": "Core"});

	// MooTools Official Plugins
	Tomato.addModule("Accordion", { "files": ["Accordion.js"], "folder": "Plugins"});
	//Tomato.addModule("Slider", { "files": ["Slider.js"], "folder": "Plugins","bases": ["Drag"]});
	Tomato.addModule("Sortables", { "files": ["Sortables.js"], "folder": "Plugins"});
	//Tomato.addModule("Tips", { "files": ["Tips.js","Tips.css"], "folder": "Plugins"});
	// CNet Library Plugins
	Tomato.addModule("DatePicker", { "files": ["DatePicker.js","DatePicker.Fx.js"], "folder": "Form"});
	//Tomato.addModule("Indicator", { "files": ["Indicator.js","Indicator.css"], "folder": "Widgets"});
	Tomato.addModule("Modalizer", { "files": ["Modalizer.js"], "folder": "Widgets"});
	//Tomato.addModule("Notifier", { "files": ["Notifier.js","Notifier.css"], "folder": "Widgets"});
	Tomato.addModule("SimpleEditor", { "files": ["SimpleEditor.js"], "folder": "Form"});
	Tomato.addModule([{
		"name": "Windows",
		"folder": "Windows",
		"lang": true,
		"files": ["Windows.Core.js","Windows.Fx.js","Windows.Modal.js","Windows.UI.js","Windows.Notice.js","Windows.css"],
		"bases": ["Modalizer","Drag","Extras.Browser"]
	}]);

	// Form related widgets and controls set:
	// the development of this widgets set is put off to May. 2009
	Tomato.addModule([{
			"name": "Forms",
			"files": ["Form.Utilities.js"],
			"folder": "Forms",
			"lang": true
		},{
			"name": "Forms.Checkers",
			"files": ["Form.Checkers.js","Form.Checkers.css"],
			"bases": ["Forms"]
		},{
			"name": "Forms.Selectors",
			"files": ["Form.Selectors.js","Form.Selectors.css"],
			"bases": ["Forms"]
		},{
			"name": "Forms.Validation",
			"files": ["Form.Validators.js","Form.Validation.js","Form.Validation.css"],
			"bases": ["Forms", "Gears.Bubble"]
	}]);

	// effect plugins set:
	Tomato.addModule([{
			"name": "Effects.Marquee",
			"files": ["Marquee.js"]
		},{
			"name": "Effects.Matrix",
			"files": ["Matrix.js"]
		},{
			"name": "Effects.Move",
			"files": ["Move.js"]
		},{
			"name": "Effects.Reveal",
			"files": ["Reveal.js"]
		},{
			"name": "Effects.Scroll",
			"files": ["Scroll.js"]
		},{
			"name": "Effects.Slide",
			"files": ["Slider.js"]
		},{
			"name": "Effects.SmoothScroll",
			"files": ["SmoothScroll.js"]
		},{
			"name": "Effects.Sort",
			"files": ["Sort.js"]
	}]);

	// TWT, my own widgets
	//Tomato.addModule("Slider.UI", { "files": ["Slider.UI.js","Slider.UI.css"], "folder": "Widgets", "bases": ["Slider"]});
	//Tomato.addModule("ItemMenu", { "files": ["ItemMenu.js","ItemMenu.css"], "folder": "Widgets"});

	// inplaceEditor widget set, which concludes local, ajax, collection, table~~~
	Tomato.addModule([{
			"name": "InPlaceEditor",
			"lang": true,
			"bases": ["Indicator", "Form"]
		},{
			"name": "InPlaceEditor.Base",
			"files": ["InPlaceEditor.Base.js","InPlaceEditor.css"],
			"bases": ["InPlaceEditor"]
		},{
			"name": "InPlaceEditor.Ajax",
			"files": ["InPlaceEditor.Ajax.js"],
			"bases": ["InPlaceEditor.Base"]
		},{
			"name": "InPlaceEditor.Collection",
			"files": ["InPlaceEditor.Collection.js"],
			"bases": ["InPlaceEditor.Ajax"]
		},{
			"name": "InPlaceEditor.Float",
			"files": ["InPlaceEditor.Float.js"],
			"bases": ["InPlaceEditor.Ajax","Styles.RoundCorner"]
	}]);

	// tables control set, enables you to sort, modify and styling tables
	Tomato.addModule([{
			"name": "Tables",
			"files": ["Tables.css"],
			"folder": "Tables",
			"lang": true
		},{
			"name": "Tables.Core",
			"files": ["Table.Core.js"],
			"bases": ["Tables"]
	}]);

	// style collection set, some style tricks to make your page more beautiful
	Tomato.addModule([{
			"name": "Styles.Round",
			"files": ["Round.js","Round.Element.js","Styles.Round.css"],
			"bases": ["Styles"]
	}]);

	// tomato gears: slider, contextmenu, toolbar, tips, notifier, indicator ~~~
	Tomato.addModule([{
			"name": "Gears",
			"folder": "Gears"
		},{
			"name": "Gears.ToolBar",
			"files": ["Gears.ToolBar.js","Gears.ToolBar.css"],
			"bases": ["Gears"]
		},{
			"name": "Gears.Slider",
			"files": ["Gears.Slider.js"],
			"bases": ["Drag"]
		},{
			"name": "Gears.Slider.UI",
			"files": ["Gears.Slider.UI.js","Gears.Slider.UI.css"],
			"bases": ["Gears.Slider"]
		},{
			"name": "Gears.Indicator",
			"files": ["Gears.Indicator.js","Gears.Indicator.css"],
			"bases": ["Extras.Browser"]
		},{
			"name": "Gears.Notifier",
			"files": ["Gears.Notifier.js","Gears.Notifier.css"]
		},{
			"name": "Gears.ItemMenu",
			"files": ["Gears.ItemMenu.js","Gears.ItemMenu.css"]
		},{
			"name": "Gears.Tips",
			"files": ["Gears.Tips.js","Gears.Tips.css"]
		},{
			"name": "Gears.Bubble",
			"files": ["Gears.Bubble.js","Gears.Bubble.css"]
		}]);

	// style collection set, some style tricks to make your page more beautiful
	Tomato.addModule([{
			"name": "Widgets",
			"folder": "Widgets"
		},{
			"name": "Widgets.SlideShow",
			"files": ["SlideShow.js","SlideShow.css"],
			"bases": ["Widgets", "Gears.Slider", "Extras.Assets"],
			"lang": true
	}]);

	// tab toggling widget sets
	Tomato.addModule([{
			"name": "Tabs.Core",
			"files": ["Tabs.Core.js"],
			"folder": "Tabs"
		},{
			"name": "Tabs.Simple",
			"files": ["Tabs.Simple.js"],
			"bases": ["Tabs.Core"]
		},{
			"name": "Tabs.Slider",
			"files": ["Tabs.Slider.js"],
			"bases": ["Tabs.Core"]
		},{
			"name": "Tabs.Ajax",
			"files": ["Tabs.Ajax.js"],
			"bases": ["Tabs.Core","Gears.Indicator"]
		},{
			"name": "Tabs.Images",
			"files": ["Tabs.Images.js"],
			"bases": ["Tabs.Slider"]
		},{
			"name": "Tabs.Sticker",
			"files": ["Tabs.Sticker.js"],
			"bases": ["Tabs.Core"]
		}]);

})();

/**
 * @function: define an utility function that creates and task object
 * @scope: public
 * @paramas: $task, $detail, $data
 * @returns: task object
 */
function $task(task, detail, data){
	return {task: task, detail: detail, data: data};
}

/*
the structor of an module in Tomato.Modules
module = {
	"name": "DatePicker",
	"lang": true,
	"files": ["DatePicker.js","DatePicker.Fx.js"],
	"styles": ["DatePicker.css"],
	"floder": "Form",
	"bases": ["Windows", "Module"]
}
*/

/*
// test case for NameSpacer
Tomato.setPackage('/Slider.UI',{
	title: "",
	parent: document.body,
	indicator: true,
	calculator: function(value){return value},
	limits: true,
	target: false,
	position: "centerBottom",
	zIndex: 999,
	activate: "click",
	fadeOnBlur: false,
	slider: {
		snap: false,
		offset: 0,
		range: false,
		wheel: false,
		steps: 100,
		onChange: $empty
	}
});
Tomato.setPackage('/Slider.UI/title', 'changed');
Tomato.log(Tomato.getPackage('/Slider.UI'));
*/