var updates = (function() {
	var IDLE_TIMEOUT = 10000,
	    services = {
		'github': {title: 'Github'},
    		'lastfm': {title: 'Last.fm'},
    		'goodreads': {title: 'Goodreads'},
    		'youtube': {title: 'YouTube'},
    		'blog': {title: 'Blog'}
	    },
	    timeSegments = [
		[60, 'second'],
		[60, 'minute'],
		[24, 'hour'],
		[0, 'day']
	    ],
	    currentService = 'all',
	    updates = {},
	    serverTime = Date.now()/1000;

	function ago(now, then)
	{
		var diff = now - then;
		if (diff < 0) return 'The Future!';

		var lim = 1, r = 1, label = '', n;
		for (var i = 0; i < timeSegments.length; i++)
		{
			r = lim;
			lim *= timeSegments[i][0];
			label = timeSegments[i][1];
			if (lim === 0 || diff < lim) break;
		}

		n = Math.floor(diff / r);
		if (n !== 1) label += 's';

		return n + ' ' + label + ' ago';
	}

	function sortedDict(sortFn, reverse)
	{
		this._keys = [];
		this._dict = {};
		this._sortFn = sortFn;
		this._reverse = reverse;
	}
	sortedDict.prototype.set = function(key, value) {
		this._dict[key] = value;
		if (this._keys.indexOf(key) === -1)
		{
			this._keys.push(key);
			this._keys.sort(this._sortFn);
			if (this._reverse) this._keys.reverse();
		}
	};
	sortedDict.update = function(dict) {
		for (var k in dict)
		{
			if (!dict.hasOwnProperty(k)) continue;
			this.set(k, dict[k]);
		}
	};
	sortedDict.prototype.get = function(key) {
		return this._dict[key];
	};
	sortedDict.prototype.sget = function(index) {
		if (index >= this._keys.length) return undefined;
		return this._dict[this._keys[index]];
	};
	sortedDict.prototype.del = function(key) {
		delete this._dict[key];
		var i = this._keys.indexOf(key);
		if (i > -1) this._keys.splice(i, 1);
	};
	sortedDict.prototype.sdel = function(index) {
		delete this._dict[this._keys[index]];
		this._keys.splice(index, 1);
	};
	sortedDict.prototype.each = function(callback) {
		for (var i = 0; i < this._keys.length; i++)
			if (typeof this._keys[i] !== 'undefined')
				callback(this._keys[i], this._dict[this._keys[i]]);
	};

	function showService(service)
	{
		var list = [];

		if (service === 'all')
		{
			for (var s in services)
			{
				if (!updates.hasOwnProperty(s)) continue;
				var update = updates[s].sget(0);
				if (typeof update !== 'undefined') list.push(update.elem);
			}
		}
		else
		{
			updates[service].each(function(timestamp, update) {
				list.push(update.elem);
			});
		}

		$('#services li').removeClass('active');
		$(updatesList).empty();
		$(list).each(function(i, elem) { $(updatesList).append(elem); });
		setTimeout(function() {
			$('#updates li').removeClass('new');
		}, 2000);
		$('#services li.'+service).addClass('active');
	}

	function makeElem(service, update, init)
	{
		var cls = (init) ? service : service + ' new';
		if (!(service in services)) return update;
		update.elem = $('<li class="'+cls+'">'+
			'<a href="'+update.url+'" class="service" title="'+services[service].title+'">'+service+':</a>'+
			'<div class="content">'+update.content+'</div>'+
			'<div class="meta"><a class="date" href="'+update.url+'" data-timestamp="'+update.timestamp+'"></a></div></li>')[0];
		update.elem._update = update;
		return update;
	}

	function storeUpdates(service, updateList, init)
	{
		if (!(service in updates)) updates[service] = new sortedDict(undefined, true);

		for (var timestamp in updateList)
		{
			var update = updates[service].get(timestamp);
			if (!update || updateList[timestamp].content !== update.content)
				updates[service].set(timestamp,  makeElem(service, updateList[timestamp], init));
		}

		updates[service].each(function(timestamp, update) {
			if (!(timestamp in updateList)) updates[service].del(timestamp);
		});
	}

	function refreshList()
	{
		showService(currentService);
	}

	var initialized = false;
	function init(data)
	{
		if (!initialized)
		{
			$('#services li').first().before($(
				'<li class="all"><a href="http://joefriedl.net" title="All Updates"></a></li>'
			));
			$('#services li').each(function(i, el) {
				var service = $(el).attr('class');
				$(el).find('a').click(function() {
					currentService = service;
					showService(currentService);
					if (currentService !== 'all')
						$('#serviceLink').html($(el).html()).removeClass('hidden');
					else
						$('#serviceLink').html('').addClass('hidden');
					return false;
				});
			});
			$('#services').addClass('icons');

			$(servicesList).after(updatesList);
			$(servicesList).after($('<div id="serviceLink" class="hidden"></div>'));
			initialized = true;
		}

		for (var service in data.updates)
		{
			if (!data.updates.hasOwnProperty(service)) continue;
			storeUpdates(service, data.updates[service], true);
		}
		refreshList();
		syncTime(data.server_time);
	}

	function update(data)
	{
		storeUpdates(data.service, data.updates);
		if (currentService === data.service || currentService === 'all')
			refreshList();
		syncTime(data.server_time);
	}

	function syncTime(time)
	{
		serverTime = parseInt(time);
		for (var s in services)
		{
			if (!updates.hasOwnProperty(s)) continue;
			updates[s].each(function(timestamp, update) {
				$(update.elem).find('.date').html(ago(serverTime, update.timestamp));
			});
		}
	}

	return function(socketioUrl)
	{
		servicesList = $('#services');
		updatesList = $('<ul id="updates"></ul>');
		var socket = io.connect(socketioUrl, {
			reconnect: true,
			'reconnection delay': 500,
			'max reconnect attempts': 10
		    }),
		    idle = false,
		    idleTimeout,
		    idleTime = 0;
		socket.on('init', init);
		socket.on('update', update);
		socket.on('server_time', syncTime);

		$(window).blur(function() {
			idleTimeout = setTimeout(function() {
				socket.emit('pause');
				idle = true;
				idleTime = serverTime;
			}, IDLE_TIMEOUT);
		});
		$(window).focus(function() {
			clearTimeout(idleTimeout);
			if (idle)
				socket.emit('resume', idleTime);
			idle = false;
		});

		return socket;
	};
})();

