function LiveTiming(channel)
{
	this.pExecuter = null;
	this.onLiveTimingReceived = null;
	this.onStateCallbacks = null;
	this.onConnect = null;
	this.onDisconnect = null;
	this.onError = null;
	this.onReset = null;
	this.connection = null;
	this.subscriptions = [];
	this.previousDrivers = null;
	this.channel = channel;
	
	
	this.startLiveTimingUpdater = function()
	{
		this.subscriptions = [];
		this.connection = hookbox.connect('http://hookbox.uhlhydroplanes.com');
		var me = this;
		this.connection.onOpen = function() {
			if(me.onConnect != null)
				me.onConnect();
		}
		this.connection.onError = function(err) {
			if(me.onError != null)
				me.onError(err);
		}
		this.connection.onClose = function() {
			if(me.onDisconnect != null)
				me.onDisconnect();
		}
		
		this.connection.onSubscribed = function(channelName, _subscription) {
			for(var i = 0; i < me.subscriptions.length; i++) {
				var sub = me.subscriptions[i];
				if(channelName == sub.getChannel()) {
					sub.setSubscription(_subscription);
				}
			}
		}
		
		this._addSubscription(this.channel, this.callbackLiveTiming, this.onStateCallbacks)
	}
	
	this.addSubscription = function(name, callback, stateCallbacks) {
		this._addSubscription(this.channel + ":" + name, callback);
	}
	
	this._addSubscription = function(channel, callback, stateCallbacks) {
		this.subscriptions.push(new subscription(this, this.connection, channel, callback, stateCallbacks, this.onReset));
		this.connection.subscribe(channel);
	}
	
	this.stopLiveTimingUpdater = function()
	{
		this.connection.disconnect();
	}
	
	this.callbackLiveTiming = function(liveTimingData) {
		liveTimingData.drivers.sort(this.getParent().sortDrivers);
		liveTimingData.driversRemoved = this.getParent().getRemovedDrivers(liveTimingData.drivers);
		liveTimingData.driversAdded = this.getParent().getAddedDrivers(liveTimingData.drivers);
		this.getParent().previousDrivers = liveTimingData.drivers;
		
		if(this.getParent().onLiveTimingReceived != null)
			this.getParent().onLiveTimingReceived(liveTimingData);
	}
	
	this.getRemovedDrivers = function(drivers)
	{
		var removedDrivers = new Array();
		if(this.previousDrivers != null)
		{
			for(var i = 0; i < this.previousDrivers.length; i++)
			{
				if(!this.driversContains(this.previousDrivers[i].name, drivers))
				{
					removedDrivers.push(this.previousDrivers[i]);
				}
			}
		}
		return removedDrivers;
	}
	
	this.getAddedDrivers = function(drivers)
	{
		var addedDrivers = new Array();
		for(var i = 0; i < drivers.length; i++)
		{
			if(this.previousDrivers == null || !this.driversContains(drivers[i].name, this.previousDrivers))
			{
				addedDrivers.push(drivers[i]);
			}
		}
		return addedDrivers;
	}
	
	this.driversContains = function(name, dArray)
	{
		var i = dArray.length;
		while (i--) {
			if (dArray[i].name === name) {
				return true;
			}
		}
		return false;
	}
	
	this.sortDrivers = function(a,b)
	{
		return (a.position - b.position);
	}
	
	this.getText = function(obj)
	{
		if (window.DOMParser)
		{
			return obj.textContent;
		}
		else
		{
			return obj.text;
		}
	}
}

function subscription(parent, conn, channel, callback, stateCallbacks, reset) {
	this._conn = conn;
	this._channel = channel;
	this._callback = callback;
	this._stateCallbacks = stateCallbacks;
	this._reset = reset;
	this._isSubscribed = false;
	this._sub = null;
	this._retryExec = null;
	this._parent = parent;
	this.getParent = function() {return this._parent; }
	this.getChannel = function() { return this._channel; }
	this.setReset = function(reset) { this._reset = reset; }
	this.setSubscription = function(subscription) {
		this._sub = subscription;
		this._isSubscribed = true;
		var me = this;
		this._sub.onPublish = function(frame) {
			me._callback(frame.payload);
		}
		this._sub.onUnsubscribe = function(frame) {
			me._isSubscribed = false;
			if(me._reset)
				me._reset();
			if(!me._retryExec) {
				var me2 = me;
				me._retryExec = new PeriodicalExecuter(
				function(pe) {
					if(me2._isSubscribed) {
						pe.stop();
						me2._retryExec = null;
					}
					else {
						me2._conn.subscribe(me2._channel);
					}
				},2);
			}
		}
		this._sub.onState = function(frame) {
			if(me._stateCallbacks != null) {
				for(var key in frame.updates) {
					if(me._stateCallbacks[key] != null) {
						var data = frame.updates[key];
						if(key == "Drivers") {
							data.drivers.sort(me.getParent().sortDrivers);
							data.driversRemoved = me.getParent().getRemovedDrivers(data.drivers);
							data.driversAdded = me.getParent().getAddedDrivers(data.drivers);
							me.getParent().previousDrivers = data.drivers;
						}
						me._stateCallbacks[key](data);
					}
				}
			}
		}
		if(me._stateCallbacks != null) {
			for(var key in this._sub.state) {
				if(me._stateCallbacks[key] != null) {
					var data = this._sub.state[key];
					if(key == "Drivers") {
						data.drivers.sort(me.getParent().sortDrivers);
						data.driversRemoved = me.getParent().getRemovedDrivers(data.drivers);
						data.driversAdded = me.getParent().getAddedDrivers(data.drivers);
						me.getParent().previousDrivers = data.drivers;
					}
					me._stateCallbacks[key](data);
				}
			}
		}
		//for(var i = 0; i < this._sub.historySize; i++) {
		//	var hist = this._sub.history[i];
		//	if(hist[0] == "PUBLISH") {
		//		this._callback(hist[1].payload);
		//	}
		//}
	}
}
