/**
 * 
 * @fileOverview Event listener singleton. Provides vboxEventListener
 * @author Ian Moore (imoore76 at yahoo dot com)
 * @version $Id: eventlistener.js 596 2015-04-19 11:50:53Z imoore76 $
 * @copyright Copyright (C) 2010-2015 Ian Moore (imoore76 at yahoo dot com)
 */

/**
 * vboxEventListener
 * 
 * Polls vboxwebsrv for pending events and triggers
 * events on $('#vboxPane')
 * 
 * @namespace vboxEventListener
 */
var vboxEventListener = {
	
	// Timeout
	timeout: 20,
	
	// Not initially running
	_running: false,
	
	// persistent request data
	_persist: {},

	// List of machines to subscribe to at runtime
	_subscribeList: [],
	
	// Watchdog to make sure vboxEventListener is still
	// running and attempting to get events
	_watchdog: {
		lastRun: 0,
		start: function() {
			window.setInterval(function() {
			if(vboxEventListener._running && 
					((new Date().getTime()/1000) - vboxEventListener._watchdog.lastRun > vboxEventListener.timeout)) {
				phpVirtualBoxFailure(' (EventListener watchdog failure)');
				vboxEventListener.stop();
				window.clearInterval(vboxEventListener._watchdog.timer);
			} 
			}, vboxEventListener.timeout)
		},
		stop: function() {
			window.clearInterval(vboxEventListener._watchdog.timer);
		}
	},
		
	// Since VirtualBox handles to event listener objects are persistent,
	// calls using the same handle should be synchronous
	_requestQueue: {
		
		requests: [],
		running: false,
		
		// run timer
		timer: window.setInterval(function(){
			vboxEventListener._requestQueue.run();
		}, 5000), // 5 seconds
		
		// Add a request to the queue
		addReq: function(q) {
			
			var d = $.Deferred();
			
			vboxEventListener._requestQueue.requests.push({'request':q,'deferred':d});
			vboxEventListener._requestQueue.run();
			
			return d.promise();
		},
		
		// Run through the queue
		run : function() {
			
			// Already running through queue
			if(vboxEventListener._requestQueue.running) return;
			
			vboxEventListener._requestQueue.running = true;
			vboxEventListener._requestQueue.runReq();
			
		},
		
		// Run a single request, removing it from the queue
		runReq: function() {
			var r = vboxEventListener._requestQueue.requests.shift();
			if(r) {
				$.when(r.request())
					.done(r.deferred.resolve)
					.fail(r.deferred.reject)
					.always(vboxEventListener._requestQueue.runReq);
			} else {
				vboxEventListener._requestQueue.running = false;
			}
		}
		
	},
	
	/**
	 *  Start event listener loop
	 *  @param {Array} vmlist - list of VM ids to subscribe to
	 */
	start: function(vmlist) {
		
		// Already started?
		if(vboxEventListener._running) return;
		
		// Get timeout if exists
		if($('#vboxPane').data('vboxConfig').eventListenerTimeout)
			vboxEventListener.timeout = $('#vboxPane').data('vboxConfig').eventListenerTimeout;
		
		vboxEventListener._running = true;
		
		var started = $.Deferred();
		
		// Subscribe to events and start main loop
		$.when(vboxAjaxRequest('subscribeEvents',{vms:vmlist})).done(function(d) {
			vboxEventListener._persist = d.persist;
			$.when(vboxEventListener._getEvents()).done(function(){
				vboxEventListener._watchdog.start();
				started.resolve();
			});
		});
		
		return started.promise();
		
	},
	
	/**
	 * Subscribe to a single machine's events.
	 * 
	 * @param {String} vmid - ID of VM to subscribe to
	 */
	subscribeVMEvents: function(vmid) {
		
		// Push into list
		vboxEventListener._subscribeList.push(vmid);
		
		// Add subscription request to queue
		return vboxEventListener._requestQueue.addReq(function(){
			
			if(!vboxEventListener._subscribeList.length) return;
			
			var vms = vboxEventListener._subscribeList.concat();
			vboxEventListener._subscribeList = [];
			
			var vmEvents = $.Deferred();
			$.when(vboxAjaxRequest('machineSubscribeEvents', {'vms':vms},{'persist':vboxEventListener._persist})).done(function(d){
				// Always set persistent request data
				vboxEventListener._persist = d.persist;
			}).always(function(){
				vmEvents.resolve();
			});
			return vmEvents.promise();
		});
		
	},
	
	/**
	 *  Stop event listener loop and unsubscribe from events
	 */
	stop: function() {
		
		if(!vboxEventListener._running)
			return;
		
		window.clearTimeout(vboxEventListener._running);
		vboxEventListener._running = false;
		
		vboxEventListener._watchdog.stop();
		
		// Unsubscribe from events. Returns a deferred object
		return vboxEventListener._requestQueue.addReq(function(){
			return vboxAjaxRequest('unsubscribeEvents', {}, {'persist':vboxEventListener._persist});
		});
		
	},
	
	/**
	 * Main loop - get pending events
	 */
	_getEvents: function(){

		// Don't do anything if we aren't running anymore
		if(!vboxEventListener._running) return;
		
		// Add to queue
		return vboxEventListener._requestQueue.addReq(function(){
			
			return $.when(new Date().getTime(), vboxAjaxRequest('getEvents',{}, {'persist':vboxEventListener._persist})).done(function(lastTime,d) {
				
				// Don't do anything if this is not running
				if(!vboxEventListener._running) return;
				
				// Check for valid result
				if(!d || !d.success) {
					if(vboxEventListener._running)
						phpVirtualBoxFailure();
					return;
				}
				
				
				// Check key to make sure this isn't a stale
				// response from a previously selected server
				if(!d.key || (d.key != $('#vboxPane').data('vboxConfig').key)) return;
				
				// Tell the watch dog that we were run
				vboxEventListener._watchdog.lastRun = (new Date().getTime() / 1000);
				
				// Always set persistent request data
				vboxEventListener._persist = d.persist;
				
				// Loop through each event triggering changes
				if(d.responseData && d.responseData.length) {
									
					// Trigger each event individually
					for(var i = 0; i < d.responseData.length; i++) {
						
						// Trigger raw vbox events
						$('#vboxPane').trigger('vbox' + d.responseData[i].eventType, [d.responseData[i]]);
						
					}
					
					// Trigger event list queue
					$('#vboxPane').trigger('vboxEvents', [d.responseData]);
					
				}
				
				// Wait at most 3 seconds
				var wait = Math.min(3000,3000 - ((new Date().getTime()) - lastTime));
				if(wait <= 0) {
					vboxEventListener._running = true;
					vboxEventListener._getEvents();
				} 
				else { 
					vboxEventListener._running = window.setTimeout(vboxEventListener._getEvents, wait);
				}
				
			});

		});
	}
};

// Stop event listener on window unload
$(document).ready(function() {
	$(window).on('unload',function() {
		vboxEventListener.stop();
	});	
});
