//= require <mootools-core-1.3-full-nocompat>
//= require <uiframework/UICEvent>
//= require <uiframework/UIComponent>
var LayoutManager = {
    
	callLaterObject : new UIComponent(),
	callLaterPending : false,
	invalidatePropertiesFlag : false,
	invalidateSizeFlag : false,
	invalidateDisplayListFlag : false,
	usePhasedInstantiation : true,
	//---------------------------------------
	invalidatePropertiesQueue : [],
	invalidateSizeQueue : [],
	invalidateDisplayListQueue : [],
	updateCompletePendingQueue : [],

	/**
     * Adds an object to the list of components
     * that need their validateProperties() function called.
     */
	invalidateProperties : function(obj)
	{
		if (!this.invalidatePropertiesFlag)
		{
			this.invalidatePropertiesFlag = true;
			if (!this.callLaterPending)
			{
				this.callLaterObject.callLater(this.doPhasedInstantiation.bind(this));
				this.callLaterPending = true;
			}
		}
		if (!this.invalidatePropertiesQueue[obj.nestLevel])
			this.invalidatePropertiesQueue[obj.nestLevel] = [];
		this.invalidatePropertiesQueue[obj.nestLevel].push(obj);
	},

	/**
     * Adds an object to the list of components
     * that need their measure() function called
     */
	invalidateSize : function(obj)
	{
		if (!this.invalidateSizeFlag)
		{
			this.invalidateSizeFlag = true;
			if (!this.callLaterPending)
			{
				this.callLaterObject.callLater(this.doPhasedInstantiation.bind(this));
				this.callLaterPending = true;
			}
		}
		if (!this.invalidateSizeQueue[obj.nestLevel])
			this.invalidateSizeQueue[obj.nestLevel] = [];
		this.invalidateSizeQueue[obj.nestLevel].push(obj);
	},

	/**
      * Adds an object to the list of components
      * that need their validateDisplayList() function called
      */
	invalidateDisplayList : function(obj)
	{
		if (!this.invalidateDisplayListFlag)
		{
			this.invalidateDisplayListFlag = true;
			if (!this.callLaterPending)
			{
				this.callLaterObject.callLater(this.doPhasedInstantiation.bind(this));
				this.callLaterPending = true;
			}
		}
		if (!this.invalidateDisplayListQueue[obj.nestLevel])
			this.invalidateDisplayListQueue[obj.nestLevel] = [];
		this.invalidateDisplayListQueue[obj.nestLevel].push(obj);
	},

	/**
      * @private
      */
	doPhasedInstantiation : function()
	{
		if (this.usePhasedInstantiation)
		{
			if (this.invalidatePropertiesFlag)
				this.validateProperties();
			else if (this.invalidateSizeFlag)
				this.validateSize();
			else if (this.invalidateDisplayListFlag)
				this.validateDisplayList();
		}
		else
		{
			if (this.invalidatePropertiesFlag)
				this.validateProperties();
			if (this.invalidateSizeFlag)
				this.validateSize();
			if (this.invalidateDisplayListFlag)
				this.validateDisplayList();
		}

		if (this.invalidatePropertiesFlag || this.invalidateSizeFlag || this.invalidateDisplayListFlag)
			this.callLaterObject.callLater(this.doPhasedInstantiation.bind(this));
		else
		{
			this.callLaterPending = false;
			this.usePhasedInstantiation = false;
			while(true)
			{
				if (!this.updateCompletePendingQueue.length)
					break;
				//-------------
				var list = this.updateCompletePendingQueue.pop();
				if (!list)
					continue;
				var len = list.length;
				for (var j = 0; j < len; j++)
				{
					var obj = list[j];
					if (obj.parentComponent && !obj.initialized)
						obj.initialized = true;
					obj.dispatchEvent(new UICEvent("updateComplete", false, false, null));
					obj.updateCompletePending = false;
				}
			}
		}
	},

	/**
     * Validates all components who have called invaidateProperties()
     * If subsequent calls to invalidateProperties are made while
     * processing this operation, those too will get validated during this
     * pass.
     */
	validateProperties : function()
	{
		// Start from the highest level objects and
		// drill down from there. That is, objects with the
		// lowest nestLevel.
		while(true)
		{
			if (!this.invalidatePropertiesQueue.length)
			{
				this.invalidatePropertiesFlag = false;
				break;
			}
			//-----------------------------------------------------
			var list = this.invalidatePropertiesQueue.shift();
			if (!list)
				continue;
			var len = list.length;
			for (var j = 0; j < len; j++)
			{
				var obj = list[j];
				obj.validateProperties();
				this.queueUpdateComplete(obj);
			}
		}
	},

	/**
     * Validates all components who have called their
     * invalidateSize() method.  If subsequent calls to
     * invalidateSize() are called while processing the objects
     * in this operation, those too will get validated during this pass
     */
	validateSize : function()
	{
		// Start from the deepest nested objects
		// and move up from there
		while(true)
		{
			if (!this.invalidateSizeQueue.length)
			{
				this.invalidateSizeFlag = false;
				break;
			}
			//-----------------------------------------------------
			var list = this.invalidateSizeQueue.pop();
			if (!list)
				continue;
			var len = list.length;
			for (var j = 0; j < len; j++)
			{
				var obj = list[j];
				obj.validateSize();
				this.queueUpdateComplete(obj);
			}
		}
	},

	/**
     * Validates all components who have called their
     * invalidateDisplayList() method.  If subsequent calls to
     * invalidateDisplayList() are called while processing the objects
     * in this operation, those too will get validated during this pass.
     */
	validateDisplayList : function()
	{
		// Start from the highest level objects and
		// drill down from there. That is, objects with the
		// lowest nestLevel.
		while(true)
		{
			if (!this.invalidateDisplayListQueue.length)
			{
				this.invalidateDisplayListFlag = false;
				break;
			}
			//-----------------------------------------------------
			var list = this.invalidateDisplayListQueue.shift();
			if (!list)
				continue;
			var len = list.length;
			for (var j = 0; j < len; j++)
			{
				var obj = list[j];
				obj.validateDisplayList();
				this.queueUpdateComplete(obj);
			}
		}
	},

	validateClient : function(client /* UIComponent */)
	{
		var thisClient = client;
		var i = queue ? queue.length : 0;
		var j = 0;
		var len = 0;
		var child = null;
		var list = null;
		var queue = this.invalidatePropertiesQueue;
		// Properties
		while(i--)
		{
			list = queue[i];
			if (!list)
				continue;
			len = list.length;
			for (j = 0; j < len; j++)
			{
				child = list[i];
				if ((child == thisClient || thisClient.contains(child)) && child.invalidatePropertiesFlag == true)
				{
					child.validateProperties();
					this.queueUpdateComplete(child);
					list.splice(j, 1);
				}
			}
		}
		// Size
		thisClient = client;
		queue = this.invalidateSizeQueue;
		len = queue ? queue.length : 0;
		for (i = 0; i < len; i++)
		{
			list = queue[i];
			if (!list)
				continue;
			for (j = 0; j < len; j++)
			{
				child = list[i];
				if ((child == thisClient || thisClient.contains(child)) && child.invalidateSizeFlag == true)
				{
					child.validateSize();
					this.queueUpdateComplete(child);
					list.splice(j, 1);
				}
			}
		}
		// DisplayList
		thisClient = client;
		queue = this.invalidateDisplayListQueue;
		i = queue ? queue.length : 0;
		while(i--)
		{
			list = queue[i];
			if (!list)
				continue;
			len = list.length;
			for (j = 0; j < len; j++)
			{
				child = list[i];
				if ((child == thisClient || thisClient.contains(child)) && child.invalidateDisplayListFlag == true)
				{
					child.validateDisplayList();
					this.queueUpdateComplete(child);
					list.splice(j, 1);
				}
			}
		}
	},

	queueUpdateComplete : function(obj /* UIComponent */)
	{
		if (!obj.updateCompletePending)
		{
			if (!this.updateCompletePendingQueue[obj.nestLevel])
				this.updateCompletePendingQueue[obj.nestLevel] = [];
			this.updateCompletePendingQueue[obj.nestLevel].push(obj);
			obj.updateCompletePending = true;
		}
	}
};
