/**
 * Akyla_Prototype_JSON
 *
 * Can process a typical JSON response from the Akyla framework.
 * Also is able to set itself up as the default JSON processor of the Akyla_Prototype_Ajax
 */
var Akyla_Prototype_JSON = Class.create(
{
	/**
	 * Constructor 
	 *
	 * Checks if the project has setup a setting that asks the Akyla_Prototype_JSON to be the default handler
	 * for the JSON responses of the Akyla.Ajax component. If so it replaces the processJSON Transport
	 */
	initialize : function ()
	{
		if (Akyla.getSetting("Akyla.JSON.extendAkylaAjax"))
		{
			Object.extend(Akyla.Ajax,
			{
				processJSONTransport : function (transport, requestOptions)
				{
					this.processJSONTransport(transport,requestOptions);
				}.bind(this)
			});
		}
	},
	/**
	 * Processes a list of components. If the processor of that component already exists the component gets passed
	 * to that processor right away. If it doesn't exist, it might mean that the processor is still loading. In that
	 * case a stack is created in the Akyla.Observed and the component is pushed onto that stack.
	 * @param array components JSON components to be processed
	 * @param object requestOptions The request options used to create the ajaxRequest
	 */
	processComponents : function (components, requestOptions, data)
	{
		// Components : delegate handling the component to the observer
		$A(components).each(function (component)
		{
			if (Akyla.Observed.hasObserver(component.processingFunction))
			{
				Akyla.Observed.observers.get(component.processingFunction).processComponent(component, requestOptions, data);
			}
			else
			{
				if (!Akyla.Observed.componentStack.get(component.processingFunction))
				{
					Akyla.Observed.componentStack.set(component.processingFunction, new $A());
				}
				Akyla.Observed.componentStack.get(component.processingFunction).push({component : component, requestOptions : requestOptions, data : data});
			}
		});
	},
	/**
	 * Processes the call requirements
	 * @param Array calls - the calls to process.
	 */
	processCallRequirements : function(calls)
	{
		if(!(calls instanceof Array))
		{
			return;
		}
		
		for(var x=0; x<calls.length; x++)
		{
			try
			{
				eval(calls[x]);
			}
			catch(err)
			{
				alert("Exception while evaluation the following call: "+calls[x]);
			}
		}
	},
	/**
	 * Processes a AJAX JSON transport
	 * @param Object transport AJAX JSON transport
	 */
	processJSONTransport : function(transport,requestOptions)
	{
		this.evaluateJSON(transport.responseText,requestOptions);
	},
	/**
	 * Loads all the requirements and calls back the callback function after all the requirements have been succesfully loaded
	 * For this purpose an Akyla.Countdown object gets created which gets initialized with the headerRequirements size. Each succesful
	 * load lowers the countdown, when the countdown reaches 0, the callback gets fired. This is done this way to overcome the fact
	 * that Javascript is evaluated multithread especially with Asynchronous calls (Asynchronous of the A of Ajax, yes) while there is
	 * no way of accessing this "threads".
	 *
	 * @param Array headerRequirements Array of headerRequirements which has to be loaded.
	 * @param Function callback Function which needs to be called after the headerRequirements have been loaded.
	 */
	loadRequirements : function(headerRequirements, callback)
	{
		var counter = new Akyla.CountDown($A(headerRequirements).size(), callback);
		var head = $$("head").first();

		$A(headerRequirements).each(function (headerRequirement)
		{
			Akyla.loadHeaderElement(headerRequirement,head, function ()
			{
				counter.countDown();
			});
		});
	},
	/**
	 * Gets called with the required Data from the json string if this existed in the JSON response
	 *
	 * @param data Data from the JSON response
	 */
	processData : function (data)
	{
	},
	/**
	 * Evaluates a JSON string and uses the Akyla module to load the needed header requirements.
	 * Also processes all the components in the jsonString
	 * @param string jsonString.
	 */
	evaluateJSON : function (jsonString, requestOptions)
	{
		var data = jsonString.evalJSON();
		var requiredData = null;
		if (data.requiredData)
		{
			requiredData = data.requiredData;
			this.processData(data.requiredData);
		}

		// If the data contains a redirect request, execute this and do not care about the rest of the data
		if (data.redirect)
		{
			window.location = data.redirect;
			return;
		}
		// Load requirements and
		this.loadRequirements(data.headerRequirements, function ()
		{
			this.processComponents(data.components,requestOptions,requiredData);
			this.processCallRequirements(data.callRequirements);
			var requestState = "onRequest"+(data.state).capitalize();
			if (typeof requestOptions[requestState] == "function")
			{
				requestOptions[requestState](data);
			}
		}.bind(this));
		
		if (data.messages)
		{
			$(data.messages).each(function (message)
			{
				Akyla.Logger.log(message);
			});
		}
	}
});

Akyla.CountDown = new Class.create(
{
	initialize : function (counter, callback)
	{
		this.counter = counter;
		this.callback = callback;
		if (counter === 0)
		{
			this.callback();
		}
	},
	countDown : function ()
	{
		this.counter--;
		if (this.counter <= 0)
		{
			this.callback();
		}
	}
});
/**
 * Instantiate and add to the Akyla Object
 */
Akyla.JSON = new Akyla_Prototype_JSON();
	
/*
exampleJSON = 
{
	"state": "success",
	"messages" :[],
	"headerRequirements": [
	{
		"tag":"link",
		"attributes":
		{
			"href":"css\/AkylaJSONTable.css",
			"type":"text\/css",
			"rel":"stylesheet"
		}
	},
	{
		"tag":"script",
		"attributes":
		{
			"src":"js\/prototype\/JSON\/Table.js",
			"type":"text\/javascript"
		}
	}
	],
	"callRequirements":[],
	"components": [
	{
		"containerId":"container",
		"processingFunction":"Akyla.JSON.Table",
		"content":[
		{
			"person_id":1,
			"name":"Alpha",
			"address":"Alpha-street"
		},
		{
			"person_id":2,
			"name":"Beta",
			"address":"Beta-street 2"
		}],
		"decoratedContent":[
		{
			"person_id":1,
			"name":"Alpha",
			"address":"Alpha-street"
		},
		{
			"person_id":2,
			"name":"Beta",
			"address":"Beta-street2"
		}
		],
		"columns":
		{
			"person_id":
			{
				"readableName":"person_id",
				"visible":false
			},
			"name":
			{
				"readableName":"name",
				"visible":true
			},
			"address":
			{
				"readableName":"address",
				"visible":true
			}
		}
	}]
}
 
*/

