/**
 * CMJSContext!
 */

var CMJSContext = CMJSContext || {};

CMJSContext.namespace = function (ns_string) {
	var parts = ns_string.split('.'),
		parent = CMJSContext,
		i;
		
		// strip redundant leading gloabal
	if (parts[0] === "CMJSContext") {
		parts = parts.slice(1);
	}
		
	for (i = 0; i < parts.length; i += 1) {
		// create property if it doesn't exist
		if (typeof parent[parts[i]] === "undefined") {
			parent[parts[i]] = {};
		}
		parent = parent[parts[i]];
	}
	return parent;
};

CMJSContext.namespace('CMJSContext.page.properties');
CMJSContext.namespace('CMJSContext.page.elements');

CMJSContext.namespace('CMJSContext.Element');

/**
 * Constructor
 */
CMJSContext.Element = (function () {
	// dependencies
	if (!jQuery()) {
		if (console !== undefined) {
			console.warn("jQuery library is required.");
		}
	}
	
	// private properties

	var Constr, didInit = false;
	// private methods
	
	// init
	
	// Public API
	Constr = function () {};
	Constr.prototype = {
		constructor: CMJSContext.Element,
		version: "1.0"
	};
	
	return Constr;
}());

/**
  * calls init() when document is ready.
  */
CMJSContext.Element.prototype.willInit = function () {
	var self = this;
	
	if (!self.didInit && typeof this.init === 'function') {
		jQuery().ready(function () { 
			return self.init(); 
		});
	}	
	
	return this;
};
 

/**
 *
 * Factory Method
 * 
 * Returns null if an Element for the elemId has already been created
 */
CMJSContext.Element.factory = function (type, elemId, jsProperties, pageProperties) {
	
	if (CMJSContext.page.elements[elemId] !== undefined) {
		return null;
	}
	
	
	var constr = type,
		newElement;
	
	// error if the constructor does not exist
	if (!constr || typeof CMJSContext.Element[constr] !== "function") {
		constr = "Data";
		if (console !== undefined) {
			console.log("undefined type, defaulting to Data.");
		}
	}
	
	// setup prototype for type
	if (typeof CMJSContext.Element[constr].prototype.willInit !== "function") {
		CMJSContext.Element[constr].prototype = new CMJSContext.Element();
	}
	
	newElement = new CMJSContext.Element[constr]();
	newElement.elemId = elemId;
	if (pageProperties) {
		newElement.pageProperties = pageProperties;
	}
	else {
		newElement.pageProperties = {};		
	}
	
	if (jsProperties) {
		newElement.jsProperties = jsProperties;
	} 
	else {
		newElement.jsProperties = {};		
	}
	
	newElement.children = [];
	
	CMJSContext.page.elements[elemId] = newElement;
	
	return newElement;
};

/**
 * returns the elements "class" name
 */ 
CMJSContext.Element.prototype.getName = function () {
	return this.name;
};


/**
 * @return the corresponding DOM Element
 */
CMJSContext.Element.prototype.domElement = function () {
	if (this._domElement === undefined) {
		this._domElement = document.getElementById(this.elemId);
	}
	
	return this._domElement;
};

/**
 * @return the corresponding jQuery Element
 */
CMJSContext.Element.prototype.j = function () {
	return jQuery("#" + this.elemId);
};


/**
 * @return the parent Element
 */
CMJSContext.Element.prototype.parent = function () {
	
	if (this._parentElement === undefined) {
		var parentNode = this.domElement().parentNode,
			parentElement = null;
	
		while (parentNode !== null) {
			if (CMJSContext.page.elements[parentNode.id] !== null) {
				parentElement = CMJSContext.page.elements[parentNode.id];
				break;
			}
			parentNode = parentNode.parentNode;		
		}
		this._parentElement = parentElement;
	}
	
	return this._parentElement;
};

/**
 * set the parent and calls didSetParent. That method may be implemented e.g to subscribe to notifications
 */ 
CMJSContext.Element.prototype.setParent = function (parent) {
	var self = this;
	this._parentElement = parent;

	if (typeof this.didSetParent === "function") {
		this.didSetParent();
	}

	return this;
};

CMJSContext.Element.prototype.addToChildren = function (newChild) {
	if (typeof newChild.setParent === "function") { // typecheck
		this.children.push(newChild);
		newChild.setParent(this);
	}
	
	return this;
};

/**
 * Default Element, containing only data.
 */ 
CMJSContext.Element.Data = function () {
	this.name = "Data";
};

/*
CMJSContext.Element.Data.prototype = new CMJSContext.Element();
CMJSContext.Element.Data.prototype.willInit = function () {
	console.log("data will not init");
	return this;
};

CMJSContext.Element.Data.prototype.didSetParent = function () {
	console.log(this.getName() + "_" + this.elemId + ".didSetParent");
};
*/


/************************************************************************************
 * Notification Center stuff 
 *
 */

CMJSContext.namespace('CMJSContext.Notification');

/**
  * notification object passed around during notifications
  */
CMJSContext.Notification = (function () {
	
	// dependencies
	
	// private proprerties
	var Constr,
	 	_notificationName,
		_notificationObject,
		_userInfo;
	

	// Public API
	Constr = function (notificationName, notificationObject, userInfo) {
		_notificationName = notificationName;
		_notificationObject = notificationObject;
		_userInfo = userInfo;
	};
	Constr.prototype = {
		constructor: CMJSContext.NotificationCenter,
		version: "1.0",

		notificationName : (function () { 
			return _notificationName; 
		}),
		
		notificationObject : (function () { 
			return _notificationObject; 
		}),
		
		userInfo : (function () {
			return _userInfo;
		})
	};
	
	return Constr;
}());

CMJSContext.namespace('CMJSContext.NotificationCenter');
CMJSContext.NotificationCenter = (function () {
	// dependencies
	
	// private proprerties

	var Constr,
	 	_observerList = [];

	// Public API
	Constr = function () {};
	Constr.prototype = {
		constructor: CMJSContext.NotificationCenter,
		version: "1.0",
		name: "NotificationCenter",
		
		addObserver : (function (obs, fn, name, object) {
			this.removeObserver(obs, name, object);
			
			var newObserver = {
				observer: obs,
				method: fn,
				selector_name: name,
				selector_object: object
			};

			_observerList.push(newObserver);
			
		}),
		
		observerCount : (function () { 
			return _observerList.length;
		}),
		
		removeObserver: (function (observerObject, name, object) {
			var list = _observerList, doRemoveObserver, n, anObserver;
			
			for (n in list) {
				doRemoveObserver = false;
				
				anObserver = list[n];
				if (observerObject === anObserver.observer) {
					if (anObserver.selector_name !== null && anObserver.selector_object !== null) {
						if (anObserver.selector_object === object && anObserver.selector_name === name) {
							doRemoveObserver = true;
						}
					}
					else if (anObserver.selector_name !== null) {
						if (anObserver.selector_name === name) {
							doRemoveObserver = true;
						}
					}
					else if (anObserver.selector_object !== null) {
						if (anObserver.selector_object === object) {
							doRemoveObserver = true;
						}
					}
					else {
						// we don't care about 'listen to evrerything' observer
					}
				
					if (doRemoveObserver) {
						_observerList.splice(n, 1);
					}	
				}			
			}
			
		}),
		
		postNotification: (function (notificationName, notificationObject, userInfo) {
//			console.log('postNotification: ' + notificationName + ", " + notificationObject.name + ", " + userInfo);
			var notification = new CMJSContext.Notification(notificationName, notificationObject, userInfo),
				list = _observerList, n, anObserver, 
				doSendNotification;
			
			for (n in list) {
				doSendNotification = false;
				
				anObserver = list[n];
				if (anObserver.selector_name !== null && anObserver.selector_object !== null) {
					if (anObserver.selector_object === notificationObject && anObserver.selector_name === notificationName) {
						doSendNotification = true;
					}
				}
				else if (anObserver.selector_name !== null) {
					if (anObserver.selector_name === notificationName) {
						doSendNotification = true;
					}
				}
				else if (anObserver.selector_object !== null) {
					if (anObserver.selector_object === notificationObject) {
						doSendNotification = true;
					}
				}
				else {
					// we don't care about 'listen to evrerything' observer
				}
				
				if (doSendNotification) {
					anObserver.method.call(anObserver.observer, notification);
				}				
			}
		})

	};
	return Constr;
}());

CMJSContext.NotificationCenter.defaultCenter = new CMJSContext.NotificationCenter();



