/**
 * Transit and caching Classes
 * 
 * Classes for making calls to and receiving from the framework
 * 
 * @author Ollie Maitland
 * @copyright Byng Systems LLP
 * 
 */
 
/**
 * Represents a .csv output
 * 
 * @param Integer
 */
var BYNG_TRANSIT_OUTPUT_CSV = "csv";

/**
 * Represents a .svg output
 * 
 * @param Integer
 */
var BYNG_TRANSIT_OUTPUT_SVG = "svg";

/**
 * Represents a .png output
 * 
 * @param Integer
 */
var BYNG_TRANSIT_OUTPUT_PNG = "png";


/**
 * Handles transit mechanims between the application layer and
 * the user interface, inputs and feedback 
 *
 * ByngTransit is responsible for:
 *
 *			- Transport layer
 * 			- Encoding and decoding requests
 * 			- Hydration of XML to objects
 * 			- Outputting of views
 *
 * @author Ollie Maitland
 * @copyright Byng Systems LLP
 */
Byng.register('byng.transit',
{
	/**
	 * Use the Options constructs
	 * 
	 */
	Implements : Options,
	
	
	/**
	 * Initialize the transit class
	 * 
	 * @param Options options
	 */
	initialize : function ( options )
	{
		// read configuration options	
		this.setOptions( options );
	},
	
	/**
	 * Set the request for the default transit
	 * 
	 * @param ByngRequest
	 */
	setRequest : function ( request )
	{
		this.options.request = request;
	},
	
	/**
	  Send a request to the server
	 * 
	 * @param Options options
	 */
	post : function ( options, params )
	{
		if (!options) options = {};
		
		// condition: request object not supplied
		if (!options.request) 	 options.request = this.options.request;
		if (!options.onComplete) options.onComplete = Byng.ui.response.read;
		
		// new Byng XmlPost object
		Byng.app.xmlpost = Byng.init('byng.ajax.post', options );
 		Byng.app.xmlpost.fromRequest( options.request );
 		
 		// condition: form supplied in options
 		if (options.form) {
 			// hydrate the parameters with the form data
 			try {
 				Byng.input.getBuilder().hydrate(Byng.app.xmlpost, options.form);
 			} catch (e) {
 				console.log(e);
 			}
 		}
 		
 		// condition: parameters given
		if (params) {
			// add the params
			$each(params, function(v,k) {
				this.addParam(k,v);
			}.bind(Byng.app.xmlpost));
		}
		
		// send the POST		
 		Byng.app.xmlpost.fetch( options.onComplete );
 		return Byng.app.xmlpost;
	},
	
	/**
	 * Retreive HTML from the server
	 * 
	 * @param Options options
	 */
	html : function ( options, params )
	{
		if (!options) options = {};
		
		// if the request object is passed directly then push into an object
		if (!options.request) options.request = this.options.request;
		if ($type(options.cache) == 'object') {
			if (!options.cache.pool) options.cache.pool = ( Byng.cacher ? Byng.cacher : null );
			if (options.cache.pool.has(options.cache.id)) {
				return options.onComplete(options.cache.pool.get(options.cache.id));
			}
		}
		
		// condition: parameters given
		if (params) {
			// add the params
			$each(params, function(v,k) {
				this.addParam(k,v);
			}.bind(options.request));
		}

		return Byng.init('byng.ajax.html', ($merge(options,{'container' : options.target}))).update();
	},
	
	/**
	 * Output a view
	 * 
	 * @param Integer flag
	 * @param Object options
	 */
	output : function ( flag, options )
	{
		var params = {};
		switch (flag) {
			case BYNG_TRANSIT_OUTPUT_CSV:
				var params = {'data'     : this.toJson(options.rows),
							  'headings' : this.toJson(options.headings) }
			case BYNG_TRANSIT_OUTPUT_SVG:
			case BYNG_TRANSIT_OUTPUT_PNG:
				var params = {'data'   : options.data,
							  'source' : BYNG_TRANSIT_OUTPUT_SVG}
			break;
		}
		
		// new request to the export module
		var request = new ByngRequest('default','exporter','ui','ajax');
			request.addParam('output', flag);
			request.setAction('output');

		// set the repquest
		Byng.transit.post({
				'request'    : request,
				'onComplete' : this.download.bind(this)
				}, params);
	},
		
	/**
	 * Present files for download
	 * 
	 * @param DomElement xml ByngXml structured response
	 * @return void
	 */
	download : function ( xml )
	{
		var rspn = Byng.init('byng.ajax.xml', xml);

		// condition: Internet explorer
		if (window.ie == true) {
			// display message for download
			Byng.ui.response.read(xml);
		} else {
			// present the download in an iframe
			var iframe = new Element('iframe');
				iframe.setAttribute('src', rspn.getParam('asset'));
				iframe.setStyles({'width' : '1px', 'height' : '1px', 'border' : '1px'});
			try {
				iframe.injectAfter($(ELEM_FEEDBACK));
			} catch (e) {
				iframe.injectAfter(document.getElement('body'));
			} 
		}
	},
	
	/**
	 * To JSON
	 * 
	 * @param String string
	 */
	toJson : function (string)
	{
		var j = Byng.init('byng.ajax.json');
		return j.encode(string);
	},
	
	/**
	 * From JSON
	 * 
	 * @param Object data
	 */
	fromJson : function (data)
	{
		return Byng.init('byng.ajax.json', data);
	}
	
});


/**
 * ByngCache object
 * 
 * @author Ollie Maitland
 * @copyright Byng Systems LLP
 * @requires Mootools-1.2
 */
Byng.cache = new Hash(
{
	
});

/**
 * Cache handler for the ByngApp
 * 
 * @author Ollie Maitland
 * @copyright Byng Systems LLP
 * @requires Mootools-1.2
 */
Byng.register('byng.cacher',
{
	/**
	 * Implement options
	 * 
	 */
	Implements : Options,
	
	/**
	 * Holds options for this cache
	 * 
	 * @type Object
	 */
	options : {
		
		/**
		 * Holds the lifespan of this cache in seconds
		 * 
		 * @type Integer
		 */
		lifespan : 5,
		
		/**
		 * Garbage collection time period in milliseconds
		 * 
		 * @type Int
		 */
		gcPeriod : 10000
	},
	
	/**
	 * Holds the pool of cached objects
	 * 
	 * @type Hash
	 */
	pool : Byng.cache,
	
	/**
	 * Construct the Cache with options
	 * 
	 * @param Object options
	 */
	initialize : function (options)
	{
		this.setOptions(options);
		this.purge.periodical(this.options.gcPeriod,this);
	},
	
	/**
	 * Check is the cache has a value
	 * 
	 * @param String id
	 * @return Boolean
	 */
	has : function (id)
	{
		var test = this.pool.get(id);
		return ($type(test) == 'object' && test.expires > this.getStamp());
	},
	
	/**
	 * Get a cache entry
	 * 
	 * @param String id
	 * @return Mixed
	 */
	get : function (id)
	{
		if (this.pool.has(id)) {
			return this.pool.get(id).store;
		} else {
			return null;
		}
	},
	
	/**
	 * Set a cache entry
	 * 
	 * @param String id
	 * @param Mixed store
	 * @param Int lifespan
	 * @return ByngCacher
	 */
	set : function (id, store, lifespan)
	{
		this.pool.set(id, {'store' : store, 'expires' : (this.getStamp() + (lifespan ? lifespan : this.options.lifespan))});
		return this;
	},
	
	/**
	 * Get a new timestamp
	 * 
	 * @return Integer
	 */
	getStamp : function ()
	{
		var d = new Date();
		return parseInt(d.getTime()/1000);
	},
	
	/**
	 * Purge expired cache entries
	 * 
	 * @return ByngCacher
	 */
	purge : function ()
	{
		var now = this.getStamp();
		this.pool.each(function(cache,id,hash){
			if (cache.expires <= now) hash.erase(id);
		});
		return this;
	}
});
