/** @example

// example for class with imports
fidda.core.Import.load('fidda.event.Observable');
fidda.core.Import.createClass('fidda.html.Body', '', function(argObjClass)
{
	// protected
	argObjClass.prototype.objProtectedMemberVariable = null;
	
	argObjClass.staticFunction = function()
	{
		
	}
	
	argObjClass.prototype.publicFunction = function()
	{
		
	}
});

// example for inline javascript with a load callback
fidda.core.Import.load('fidda.ui.TabSwitch', function()
{
	new fidda.ui.TabSwitch();
});

*/

// custom creation of namespace for Import class
if (undefined === window.fidda) window.fidda = new Object();
if (undefined === window.fidda.core) window.fidda.core = new Object();
{
	/**
	 *	constructor
	 *	
	 *	@access		public
	 *	@param		void
	 *	@return		void
	**/
	fidda.core.Import = function()
	{
	}
	
	// static variables
	fidda.core.Import.strBaseUrl		= null;
	fidda.core.Import.arrUrlsLoaded		= new Array();
	fidda.core.Import.arrDelayedLoad	= new Array();
	fidda.core.Import.arrCallbacks		= new Array();
	fidda.core.Import.arrUrlsToLoad		= new Array();
	
	/**
	 *	set url where javascript classes are located
	 *	
	 *	@access		public static
	 *	@param		string		argStrUrl
	 *	@return		void
	**/
	fidda.core.Import.setBaseUrl = function(argStrUrl)
	{
		fidda.core.Import.strBaseUrl = argStrUrl;
		
		for (var i = 0; i < fidda.core.Import.arrDelayedLoad.length; ++i)
		{
			var objArguments = fidda.core.Import.arrDelayedLoad[i];
			fidda.core.Import.load(objArguments.classes, objArguments.callback);
		}
		fidda.core.Import.arrDelayedLoad = new Array();
	}
	
	/**
	 *	creates a namespaced class with the possibility to extend a super class
	 *	
	 *	@access		public static
	 *	@param		string					argStrNameSpace
	 *	@param		string					argStrSuperNameSpace = ''
	 *	@param		function				argFuncLoadCallback
	 *	@return		void
	**/
	fidda.core.Import.createClass = function(argStrNameSpace, argStrSuperNameSpace, argFuncClassCallback)
	{
		//@todo import.load checken of er niet al script tag is met die url
		/*
			@todo 2x zelfde class achter elkaar loaden
			wordt laatste callback gezien als dependency en als eerste aangeroepen nog voor class callback
			misschien:
			load callbacks altijd lilo
			createclass callbacks lifo
		*/
		
		if ('' !== argStrSuperNameSpace) fidda.core.Import.load(argStrSuperNameSpace);
		
		// @note not using a nice Delegate.create here, because we can't use the Delegate class since it depends on this very function
		fidda.core.Import.addLoadCallback(function()
		{
			var strPackageName	= fidda.core.Import.getPackageName(argStrNameSpace);
			var strClassName	= fidda.core.Import.getClassName(argStrNameSpace);
			fidda.core.Import.namespace(strPackageName);
			var objPackage		= fidda.core.Import.getPackage(strPackageName);
			// create class / constructor
			var objClass		= function()
			{
				// this constructor calls the class's 'constructor' function if it's overwritten by the callback (as it should be)
				if (objClass !== this.constructor)
				{
					this.constructor.apply(this, arguments);
				}
			}
			objPackage[strClassName] = objClass;
			
			// extend super class
			if ('' !== argStrSuperNameSpace)
			{
				var objSuperClass = fidda.core.Import.getClass(argStrSuperNameSpace);
				
				// create a new empty class (empty constructor so the constructor of the super class won't be called when copying the prototype functions)
				var SuperConstructorReplacement = function(){};
				// give it the prototype of the super class (the constructor function won't be overwritten)
				SuperConstructorReplacement.prototype = objSuperClass.prototype;
				// copy the prototype of the super class into the sub class to enable use of the functions and the instanceof operator (instanceof doesn't work when just copying the prototype['functionName'] functions)
				objClass.prototype = new SuperConstructorReplacement();
				// reset the constructor variable
				objClass.prototype.constructor = objClass;
				// @todo ability to call super class functions from sub class
				//objClass.prototype.parent = ?; // save the super class as 'parent' variable @note doesn't work with a longer inheritance chain (when the parent has its own parent)
			}
			
			argFuncClassCallback(objClass);
		}, null, true);
	}
	
	/**
	 *	
	 *	
	 *	@access		public static
	 *	@param		string		argStrNameSpace
	 *	@return		string
	**/
	fidda.core.Import.getPackageName = function(argStrNameSpace)
	{
		var intPos = argStrNameSpace.lastIndexOf('.');
		if (-1 !== intPos)
		{
			argStrNameSpace = argStrNameSpace.substr(0, intPos);
		}
		
		return argStrNameSpace;
	}
	
	/**
	 *	
	 *	
	 *	@access		public static
	 *	@param		string		argStrNameSpace
	 *	@return		string
	**/
	fidda.core.Import.getClassName = function(argStrNameSpace)
	{
		var intPos = argStrNameSpace.lastIndexOf('.');
		if (-1 !== intPos)
		{
			argStrNameSpace = argStrNameSpace.substr(intPos + '.'.length);
		}
		
		return argStrNameSpace;
	}
	
	/**
	 *	returns the package object
	 *	
	 *	@access		public static
	 *	@param		string		argStrNameSpace
	 *	@return		object
	 *	@throws		String
	**/
	fidda.core.Import.getPackage = function(argStrNameSpace)
	{
		var objPreviousPackage = window;
		
		var arrNameSpace = argStrNameSpace.split('.');
		for (var i = 0; i < arrNameSpace.length; ++i)
		{
			var strPackage = arrNameSpace[i];
			if (undefined === objPreviousPackage[strPackage]) throw new 'Package not found';
			objPreviousPackage = objPreviousPackage[strPackage];
		}
		
		return objPreviousPackage;
	}
	
	/**
	 *	returns the class object
	 *	
	 *	@access		public static
	 *	@param		string		argStrNameSpace
	 *	@return		object
	 *	@throws		String
	**/
	fidda.core.Import.getClass = function(argStrNameSpace)
	{
		var objPreviousPackage	= window;
		var blnExists			= true;
		
		var arrNameSpace = argStrNameSpace.split('.');
		for (var i = 0; i < arrNameSpace.length; ++i)
		{
			var strPackage = arrNameSpace[i];
			if (undefined === objPreviousPackage[strPackage]) throw new 'Class not found: ' + argStrNameSpace;
			objPreviousPackage = objPreviousPackage[strPackage];
		}
		
		return objPreviousPackage;
	}
	
	/**
	 *	creates a namespace if it doesn't already exist
	 *	
	 *	@access		public static
	 *	@param		string		argStrPackage
	 *	@return		void
	**/
	fidda.core.Import.namespace = function(argStrPackage)
	{
		var objPreviousPackage = window;
		
		var arrNameSpace = argStrPackage.split('.');
		for (var i = 0; i < arrNameSpace.length; ++i)
		{
			var strPackage = arrNameSpace[i];
			if (undefined === objPreviousPackage[strPackage]) objPreviousPackage[strPackage] = new Object();
			objPreviousPackage = objPreviousPackage[strPackage];
		}
	}
	
	/**
	 *	checks if a class with the specified namespace already exists
	 *	
	 *	@access		public static
	 *	@param		string		argStrNameSpace
	 *	@return		boolean
	**/
	fidda.core.Import.classExists = function(argStrNameSpace)
	{
		var blnExists = false;
		try
		{
			fidda.core.Import.getClass(argStrNameSpace); // throws
			blnExists = true;
		}
		catch (e)
		{
			// class not found, blnExists remains false, ignore
		}
		
		return blnExists;
	}
	
	/**
	 *	load specified class(es) and call callback function when classes are loaded
	 *	
	 *	@access		public static
	 *	@param		Array<string>|string	argMixClassesOrUrls		when preceded by http:// or https:// it is assumed a url, when preceded by protocol:// it is assumed a url that should be loaded with the current protocol
	 *	@param		function				argFuncLoadCallback = null
	 *	@return		void
	 *	@throws		?
	**/
	fidda.core.Import.load = function(argMixClassesOrUrls, argFuncLoadCallback)
	{
		if (undefined === argFuncLoadCallback) argFuncLoadCallback = null;
		
		// convert to array if it's not an array
		if (false === argMixClassesOrUrls instanceof Array) argMixClassesOrUrls = [argMixClassesOrUrls];
		
		// add to delayed load if there's no base url yet
		if (null === fidda.core.Import.strBaseUrl)
		{
			fidda.core.Import.arrDelayedLoad.push({
				classes		: argMixClassesOrUrls,
				callback	: argFuncLoadCallback
			});
		}
		else
		{
			var arrUrls				= new Array();
			var blnIsAlreadyLoading	= false;
			for (var i = 0; i < argMixClassesOrUrls.length; ++i)
			{
				var mixClassOrUrl	= argMixClassesOrUrls[i];
				var strUrl			= null;
				if (0 === mixClassOrUrl.indexOf('http://') || 0 === mixClassOrUrl.indexOf('https://'))
				{
					// url is complete
					strUrl = mixClassOrUrl;
				}
				else if (0 === mixClassOrUrl.indexOf('protocol://'))
				{
					// replace protocol:// with current protocol
					strUrl = mixClassOrUrl.replace(/^protocol:/, document.location.protocol);
				}
				else
				{
					// add base url and replace . with /
					strUrl = fidda.core.Import.strBaseUrl + mixClassOrUrl.split('.').join('/') + '.js';
				}
				
				// check if url isn't already being loaded
				if (null !== strUrl)
				{
					if (true === fidda.core.Import.classExists(mixClassOrUrl))
					{
						// @todo check if all dependencies of the class are loaded
						if (0 !== fidda.core.Import.arrUrlsToLoad.length)
						{
							blnIsAlreadyLoading = true;
						}
					}
					else if (undefined === fidda.core.Import.arrUrlsLoaded[strUrl])
					{
						fidda.core.Import.arrUrlsLoaded[strUrl] = true;
						arrUrls.push(strUrl);
					}
					else
					{
						// check is url is already loading
						for (var i = 0; i < fidda.core.Import.arrUrlsToLoad.length; ++i)
						{
							var strCheckUrl = fidda.core.Import.arrUrlsToLoad[i];
							if (strUrl === strCheckUrl)
							{
								blnIsAlreadyLoading = true;
								
								break;
							}
						}
					}
				}
			}
			
			for (var i = 0; i < arrUrls.length; ++i)
			{
				// add to urls to load
				fidda.core.Import.arrUrlsToLoad.push(arrUrls[i]);
			}
			
			for (var i = 0; i < arrUrls.length; ++i)
			{
				// load urls
				// @note not using a delegate for the callback!
				fidda.core.Import.addScript(arrUrls[i], fidda.core.Import.onScriptLoadCallback);
			}
			
			// add callback to array (last in last out, to make sure createClass callbacks fire first)
			if (null !== argFuncLoadCallback) fidda.core.Import.addLoadCallback(argFuncLoadCallback);	
		}
	}
	
	/**
	 *	calls the specified callback as soon as all the pending classes have been loaded
	 *	
	 *	@access		public static
	 *	@param		Function				argFuncLoadCallback
	 *	@param		Array<mixed>			argArrArguments
	 *	@param		boolean					argBlnAddToBeginning = false
	 *	@return		void
	**/
	fidda.core.Import.addLoadCallback = function(argFuncLoadCallback, argArrArguments, argBlnAddToBeginning)
	{
		if (undefined === argBlnAddToBeginning) argBlnAddToBeginning = false;
		if (undefined === argArrArguments || null === argArrArguments) argArrArguments = new Array();
		
		var obj = {func: argFuncLoadCallback, arguments: argArrArguments};
		// add callback to array
		if (true === argBlnAddToBeginning)
		{
			// last in first out
			fidda.core.Import.arrCallbacks.unshift(obj);
		}
		else
		{
			// last in last out
			fidda.core.Import.arrCallbacks.push(obj);
		}
		
		//if (null !== fidda.core.Import.strBaseUrl && 0 === fidda.core.Import.arrUrlsToLoad.length)
		if (0 === fidda.core.Import.arrUrlsToLoad.length && 0 === fidda.core.Import.arrDelayedLoad.length)
		{
			// no more urls to load, call all load callbacks
			fidda.core.Import.callLoadCallbacks();
		}
	}
	
	/**
	 *	calls all pending load callbacks
	 *	
	 *	@access		protected static
	 *	@param		void
	 *	@return		void
	**/
	fidda.core.Import.callLoadCallbacks = function()
	{
		for (var i = 0; i < fidda.core.Import.arrCallbacks.length; ++i)
		{
			var obj = fidda.core.Import.arrCallbacks[i];
			obj.func.apply(window, obj.arguments);
		}
		fidda.core.Import.arrCallbacks = new Array();
	}
	
	/**
	 *	
	 *	
	 *	@access		protected static
	 *	@param		string		argStrUrl
	 *	@return		void
	 *	@throws		?
	**/
	fidda.core.Import.onScriptLoadCallback = function(argStrUrl)
	{
		// @note we're not using a delegate, but the url argument is passed by the callback
		
		for (var i = 0; i < fidda.core.Import.arrUrlsToLoad.length; ++i)
		{
			var strUrl = fidda.core.Import.arrUrlsToLoad[i];
			if (argStrUrl === strUrl)
			{
				fidda.core.Import.arrUrlsToLoad.splice(i, 1);
				
				break;
			}
		}
		
		// no more urls to load, call all load callbacks
		if (0 === fidda.core.Import.arrUrlsToLoad.length)
		{
			fidda.core.Import.callLoadCallbacks();
		}
	}
	
	/**
	 *	
	 *	
	 *	@access		public static
	 *	@param		string		argStrUrl
	 *	@param		function	argFuncLoadCallback = null
	 *	@return		void
	 *	@throws		?
	**/
	fidda.core.Import.addScript = function(argStrUrl, argFuncLoadCallback)
	{
		if (undefined === argFuncLoadCallback) argFuncLoadCallback = null;
		
		var objScript					= document.createElement('script');
		objScript.type					= 'text/javascript';
		objScript.async					= true; // html5 attribute
		objScript.src					= argStrUrl;
		if (null !== argFuncLoadCallback)
		{
			objScript.onload = function()
			{
				argFuncLoadCallback(argStrUrl);
			};
			objScript.onreadystatechange = function()
			{
				if ('loaded' === this.readyState || 'complete' === this.readyState)
				{
					argFuncLoadCallback(argStrUrl);
				}
			};
		}
		document.getElementsByTagName('head')[0].appendChild(objScript);
	}
}
