/**
 * lib
 * 
 * @namespace ML_APP
 * @requires katex (com/katex/katex.js)
 * @requires renderMathInElement() (com/katex/contrib/auto-render.js)
 * @author Matthias Schulz
 * @version 2.0.0dev
 */

var ML_APP = ML_APP || {};

ML_APP.lib = ML_APP.lib || {};

(function( lib ) {
	if (typeof lib.getNamespace === 'function') {
		// die lib existiert bereits
		return;
	}
	/**
	 * Erzeugt Namespace-Objekte
	 * 
	 * @method	getNamespace
	 * @param	{String} ns_str		Ein String mit Namespaces, getrennt durch einen Punkt. "ML_APP.CLASSES.core"
	 * @return	{Objegt} Das Namespace-Objekt
	 */
	lib.getNamespace = function( ns_str ) {
		var global,								// das globale Objekt
			parts_array = ns_str.split('.'),
			parent;

		// Zuweisung des Globalen Objekts.
		// 
		// Für die Ermittlung des globalen Objekts existieren zahlreiche Vorschläge,
		// siehe z. B. http://stackoverflow.com/questions/3277182/how-to-get-the-global-object-in-javascript
		// Folgende Anforderungen sollten erfüllt werden:
		// 
		// 1. lauffähig im "use strict" Modus
		// 2. lauffähig in ECMAScript 3, 5
		// 3. lauffähig in anderen Laufzeitumgebungen außerhalb des Browsers (Node, Rhino, etc.)
		// 4. bestehen von JSLint
		// 
		// Variante 1:
		// - erfüllt 2, 4
		global = function() {
			return this; // Closure, this verweist hier auf das global Object
		}();
		
		parent = global;

		for (var i = 0; i < parts_array.length; i += 1) {
			// Eigenschaft erstellen, wenn sie nicht schon vorhanden ist
			if (typeof parent[parts_array[i]] === "undefined") {
				parent[parts_array[i]] = {};
			}
			parent = parent[parts_array[i]];
		}
		return parent;
	};
	
	
	/**
	 * Vererbung mittels Konstruktor-Funktionen
	 * 
	 * @method	extend
	 * @param	{Function} subClass		Die Kind-Klasse.
	 * @param	{Function} superClass	Die Eltern-Klasse.
	 */
	lib.extend = function( subClass, superClass ) {
		// Ein Reload der gesamten App sollte die absolute Ausnahme sein und eigentlich nicht mehr stattfinden,
		// da wir für die injizierten script-Nodes die Eigenschaft async=false setzen.
		if (!superClass) {
			location.reload();
		}
		
		var F = function() {}; // Temporary-Konstruktor (Proxy-Konstruktor)
		F.prototype = superClass.prototype;
		subClass.prototype = new F();
		subClass.prototype.constructor = subClass; // Zeiger auf den neuen Konstruktor setzen
		subClass.superclass = superClass.prototype; // Referenz auf das Eltern-Objekt (selbstdefinierte statische Eigenschaft)

		// nur in Apress, "Pro JavaScript Design Patterns", Ross Harmes, Dustin Diaz, S. 44
		//if (superClass.prototype.constructor == Object.prototype.constructor) {
		//	superClass.prototype.constructor = superClass;
		//}
	};
	
	
	/**
	 * Klont ein JSON-Objekt (deep clone).
	 * 
	 * Inspiriert von: http://stackoverflow.com/questions/122102/what-is-the-most-efficient-way-to-clone-a-javascript-object
	 * 
	 * Die Funktion klont ein JSON-Objekt. Da die nativen Methoden <code>JSON.stringify()</code> und
	 * <code>JSON.parse()</code> verwendet werden, gelten die Regeln für JSON-Objekte:
	 * - Es werden nur die eigenen Eigenschaften berücksichtigt.
	 * - mögliche Daten-Typen:
	 *     - <code>Object, Array, String, finite Number, Boolean, null</code>
	 * - nicht mögliche Daten-Typen:
	 *     - NaN, Infinity, und -Infinity werden zu <code>null</code>
	 *     - <code>Date</code>-Objekte werden zu ISO-formatierten Strings konvertiert
	 *     - <code>Function, RegExp, Error</code> und <code>undefined</code> werden ignoriert
	 * 
	 * @method	cloneJSON
	 * @param	{Object} JSONObject	Das zu klonende JSON-Objekt.
	 */
	lib.cloneJSON = function( JSONObject ) {
		return JSON.parse(JSON.stringify( JSONObject ));
	};
	

	/**
	 * Log-Wrapper für //console.log( object [, object, ...] )
	 * 
	 * Console APIs:
	 * - Firebug: https://getfirebug.com/wiki/index.php/Console_API
	 * - Safari / Webkit: https://developer.apple.com/library/safari/#documentation/AppleApplications/Conceptual/Safari_Developer_Guide/DebuggingYourWebsite/DebuggingYourWebsite.html
	 * 
	 * @method	log
	 * @param	{Object} [expression]		Mitteilung oder Ausdruck etc.
	 */
	lib.log = function() {
		// inspiriert von: 
		//   usage: log('inside coolFunc',this,arguments);
		//   http://paulirish.com/2009/log-a-lightweight-wrapper-for-consolelog/
		//   und debugMobi: http://www.youtube.com/watch?v=k0t2oZKpmA8
		
		// TODO: DEBUG-Flag
		if (false) {
			// Debug-Modus ist aus
		} else {
			lib.log.history = lib.log.history || [];   // store logs to an array for reference
			lib.log.history.push( arguments );
			if(console){
				// kann sein, dass dies nicht in allen Browsern funktioniert
				// siehe: http://stackoverflow.com/questions/7942323/pass-arguments-to-console-log-as-first-class-arguments-via-proxy-function
				//console.log.apply( console, arguments );
			}
		}
	};

	
	/**
	 *  Stoppuhr, ala console.time('id') und console.timeEnd('id')
	 *  
	 *  Singleton-Objekt
	 */
	lib.stopwatch = {
		/**
		 * Speicher der IDs
		 * @property	_ids
		 * @type		String
		 * @private
		 */
		_ids: {},
		/**
		 * Startet die Zeitmessung für einen selbsdefinierten Bezeichner.
		 * 
		 * @param {String} id	Bezeichner der Zeitmessung.
		 */
		time: function time( id ) {
			this._ids[id] = {
								start: new Date(), 
								stop: null,
								duration: 0
							};
		},
		/**
		 * Stoppt die Zeitmessung für die mit <code>time( id )</code> übergebenen Bezeichner.
		 * 
		 * @param	{String} id	Bezeichner der Zeitmessung.
		 * @return	{String}	Gibt die gestoppte Zeit in ms in der Form <code>Bezeichner: 29 ms</code> zurück.
		 */
		timeEnd: function timeEnd( id ) {
			var obj = this._ids[id];
			obj.stop = new Date();
			obj.duration = obj.stop - obj.start;
			return id +': '+ obj.duration + ' ms';
		},
		/**
		 * Gibt die gestoppte Zeit zurück.
		 * 
		 * @param	{String} id	Bezeichner der Zeitmessung.
		 * @return	{Number}	Die gestoppte Zeit für eine vorangegangene Zeitmessung.
		 */
		getDuration: function getDuration( id ) {
			return this._ids[id].duration;
		}
	};
	
}(ML_APP.lib));