/**
 * @file js_app.js
 * @author zero (zero@nzeo.com)
 * @brief XE JavaScript Application Framework (JAF)
 * @namespace xe
 * @update 20091120
 */
(function($){

var _xe_base, _app_base, _plugin_base;
var _apps = [];

_xe_base = {
	/**
	 * @brief return the name of Core module
	 */
	getName : function() {
		return 'Core';
	},

	/**
	 * @brief Create an application class
	 */
	createApp : function(sName, oDef) {
		var _base = getTypeBase();

		$.extend(_base.prototype, _app_base, oDef);

		_base.prototype.getName = function() {
			return sName;
		};

		return _base;
	},

	/**
	 * @brief Create a plugin class
	 */
	createPlugin : function(sName, oDef) {
		var _base = getTypeBase();

		$.extend(_base.prototype, _plugin_base, oDef);

		_base.prototype.getName = function() {
			return sName;
		};

		return _base;
	},

	/**
	 * @brief Get the array of applications
	 */
	getApps : function() {
		return $.makeArray(_apps);
	},

	/**
	 * @brief Get one application
	 */
	getApp : function(indexOrName) {
		indexOrName = (indexOrName||'').toLowerCase();
		if (typeof _apps[indexOrName] != 'undefined') {
			return _apps[indexOrName];
		} else {
			return null;
		}
	},

	/**
	 * @brief Register an application instance
	 */
	registerApp : function(oApp) {
		var sName = oApp.getName().toLowerCase();

		_apps.push(oApp);
		if (!$.isArray(_apps[sName])) {
			_apps[sName] = [];
		}
		_apps[sName].push(oApp);

		oApp.parent = this;
	},

	/**
	 * @brief Unregister an application instance
	 */
	unregisterApp : function(oApp) {
		var sName  = oPlugin.getName().toLowerCase();
		var nIndex = $.inArray(oApp, _apps);

		if (nIndex >= 0) _apps.splice(nIndex, 1);

		if ($.isArray(_apps[sName])) {
			nIndex = $.inArray(oApp, _apps[sName]);
			if (nIndex >= 0) _apps[sName].splice(nIndex, 1);
		}
	},

	/**
	 * @brief overrides broadcast method
	 */
	broadcast : function(sender, msg, params) {
		for(var i=0; i < _apps.length; i++) {
			_apps[i]._cast(sender, msg, params);
		}

		// cast to child plugins
		this._cast(sender, msg, params);
	}
}

_app_base = {
	_plugins  : [],
	_messages : [],

	_fn_level : [],

	/**
	 * @brief register a plugin instance
	 */
	registerPlugin : function(oPlugin) {
		var sName = oPlugin.getName().toLowerCase();

		// check if the plugin is already registered
		if ($.inArray(oPlugin, this._plugins) >= 0) return false;

		// push the plugin into the _plugins array
		this._plugins.push(oPlugin);

		if (!$.isArray(this._plugins[sName])) {
			this._plugins[sName] = [];
		}
		this._plugins[sName].push(oPlugin);

		// register method pool
		var msgs = this._messages;
		$.each(oPlugin, function(key, val){
			if (!$.isFunction(val)) return true;
			if (!/^API_((BEFORE_|AFTER_)?[A-Z0-9_]+)$/.test(key)) return true;
			var fn = function(s,p){ return oPlugin[key](s,p) };
			fn._fn = val;

			if (!$.isArray(msgs[RegExp.$1])) msgs[RegExp.$1] = [];
			msgs[RegExp.$1].push(fn);
		});

		// set the application
		oPlugin.oApp = this;

		// binding
		oPlugin.cast = function(msg, params) {
			return oPlugin._cast(msg, params);
		};

		oPlugin.broadcast = function(msg, params) {
			oPlugin._broadcast(msg, params);
		};

		return true;
	},

	/**
	 * @brief unregister a plugin  instance
	 */
	unregisterPlugin : function(oPlugin) {
		var sName = oPlugin.getName().toLowerCase();

		// remove from _plugins array
		var nIndex = $.inArray(oPlugin, this._plugins);
		if (nIndex >= 0) this._plugins.splice(nIndex, 1);

		if ($.isArray(this._plugins[sName])) {
			nIndex = $.inArray(oPlugin, this._plugins);
			if (nIndex >= 0) this._plugins[sName].splice(nIndex, 1);
		}

		// unregister method pool
		var msgs = this._messages;
		$.each(oPlugin, function(key, val){
			if (!$.isFunction(val)) return true;
			if (!/^API_([A-Z0-9_]+)$/.test(key)) return true;
			if (typeof msgs[RegExp.$1] == 'undefined') return true;

			if ($.isArray(msgs[RegExp.$1])) {
				msgs[RegExp.$1] = $.grep(msgs[RegExp.$1], function(fn,i){ return (fn._fn != val); });
				if (!msgs[RegExp.$1].length) {
					delete msgs[RegExp.$1];
				}
			} else {
				if (msgs[RegExp.$1]._fn == val) {
					delete msgs[RegExp.$1];
				}

			}
		});

		// unset the application
		oPlugin.oApp = null;
	},

	cast : function(msg, params) {
		return this._cast(this, msg, params || []);
	},

	broadcast : function(sender, msg, params) {
		if (this.parent && this.parent.broadcast) {
			this.parent.broadcast(sender, msg, params);
		}
	},

	_cast : function(sender, msg, params) {
		var i, len;
		var aMsg = this._messages;

		msg = msg.toUpperCase();

		// BEFORE hooker
		if (aMsg['BEFORE_'+msg] || this['API_BEFORE_'+msg]) {
			var bContinue = this._cast(sender, 'BEFORE_'+msg, params);
			if (!bContinue) return;
		}

		// main api function
		var vRet = [], sFn = 'API_'+msg;
		if ($.isFunction(this[sFn])) vRet.push( this[sFn](sender, params) );
		if ($.isFunction(aMsg[msg])) vRet.push( aMsg[msg](sender, params) );
		else if ($.isArray(aMsg[msg])) {
			for(i=0; i < aMsg[msg].length; i++) {
				vRet.push( aMsg[msg][i](sender, params) );
			}
		}
		if (vRet.length < 2) vRet = vRet[0];

		// AFTER hooker
		if (aMsg['AFTER_'+msg] || this['API_AFTER_'+msg]) {
			this._cast(sender, 'AFTER_'+msg, params);
		}

		if (!/^(?:AFTER_|BEFORE_)/.test(msg)) { // top level function
			return vRet;
		} else {
			return $.isArray(vRet)?($.inArray(false, vRet)<0):((typeof vRet=='undefined')?true:!!vRet);
		}
	}
};

_plugin_base = {
	oApp : null,
	_binded_fn : [],

	_cast : function(msg, params) {
		if (this.oApp && this.oApp._cast) {
			return this.oApp._cast(this, msg, params || []);
		}
	},
	_broadcast : function(msg, params) {
		if (this.oApp && this.oApp.broadcast) {
			this.oApp.broadcast(this, mag, params || []);
		}
	}

	/**
	 * Event handler prototype
	 *
	 * function (oSender, params)
	 */
};

function getTypeBase() {
	var _base = function() {
		if ($.isArray(this._plugins))   this._plugins   = [];
		if ($.isArray(this._messages))  this._messages  = [];
		if ($.isArray(this._binded_fn)) this._binded_fn = [];

		if ($.isFunction(this.init)) {
			this.init.apply(this, arguments);
		}
	};

	return _base;
}

window.xe = $.extend(_app_base, _xe_base);
window.xe.lang = {}; // language repository

// domready event
$(function(){ xe.broadcast(xe, 'ONREADY'); });

// load event
$(window).load(function(){ xe.broadcast(xe, 'ONLOAD'); });

})(jQuery);