/**
 * Fetches and renderes Twitter search results
 *
 * @package codebase.jquery.tweets
 * @author miesjel@rhdl.nl
 * @compat jQuery >= 1.3.2
 */
(function($)
{
/**
 * @param Object|Number|String|String[]  - user name(s) or id
 *                                       - named options
 * @return jQuery
 */
$.fn.tweets = function(options)
{
	var tweets = {

		/**
		 * @var Number|String|String[]
		 */
		from: null,
		/**
		 * @var Number
		 */
		count: 5,
		/**
		 * Dictionary, use %s symbols
		 * @var Object
		 */
		lang: {
			'less than a minute ago': 'less than a minute ago',
			'about a minute ago': 'about a minute ago',
			'%s minutes ago': '%s minutes ago',
			'about an hour ago': 'about an hour ago',
			'about %s hours ago': 'about %s hours ago',
			'1 day ago': '1 day ago',
			'%s days ago': '%s days ago'
		},
		/**
		 * @var String
		 */
		base_url: '/framework/jquery/tweets',

		/**
		 * Callback
		 *
		 * @return String
		 */
		url: function()
		{
			var params = {};
			if (tweets.sourceIsRss()) {
				params.from = tweets.from;
				params.output = 'json';
			} else {
				params.from = tweets.from.join(',');
			}
			params.rpp = tweets.count;
			return tweets.base_url + '/tweets.php?' + $.param(params);
		},

		sourceIsRss: function()
		{
			return (typeof(tweets.from) === 'number');
		},

		/**
		 * Callback
		 *
		 * @HTMLElement
		 * @see TweetsView
		 */
		render: function(element)
		{
			var url = tweets.url();
			if (!url)
				throw 'Invalid URL';
			
			var view = new tweets.View(element);
			view.createLoading();
			view.createList();
	
			$.getJSON(url, function json(data)
			{
				view.createItems(data.results);
				tweets.after();
			});
		},

		/**
		 * Callback
		 * 
		 * @param Object  this object
		 */
		after: function() {},

		relativeTime: function(time, relative_to)
		{
			var parsed_date = Date.parse(time);
			if (typeof relative_to == 'undefined')
				var relative_to = new Date;
			var diff = parseInt((relative_to.getTime() - parsed_date) / 1000);

			if (diff < 60)
				tweets.translate('less than a minute ago');

			if (diff < 120)
				return tweets.translate('about a minute ago');

			if (diff < (45*60)) {
				var minutes = parseInt(diff / 60);
				return tweets.translate('%s minutes ago', minutes);
			}

			if (diff <= (90*60))
				return tweets.translate('about an hour ago');

			if (diff < (24*60*60)) {
				var hours = parseInt(diff / 3600);
				var key = (hours == 1) ? 'about an hour ago' : 'about %s hours ago';
				return tweets.translate(key, hours);
			}

			if (diff < (48*60*60))
				return tweets.translate('1 day ago');

			var days = parseInt(diff / 86400);
			return tweets.translate('%s days ago', days);
		},

		translate: function(key, arg)
		{
			if (!(key in tweets.lang))
				throw "Missing translation '" + key + "'";

			return (typeof arg == 'undefined')
				? tweets.lang[key]
				: tweets.lang[key].replace('%s', arg);
		}

	};

	tweets.View = function(element)
	{
		this.element = element;
	};

	tweets.View.prototype = {

		element: null,
		$loading: null,
		$list: null,

		createLoading: function()
		{
			this.$loading = $('<div class="loading">&nbsp;</div>');
			this.$loading.appendTo(this.element);
		},

		removeLoading: function()
		{
			if (this.$loading)
				this.$loading.remove();
		},

		createList: function()
		{
			this.$list = $('<ul class="tweets">').appendTo(this.element);
		},

		createItems: function(items)
		{
			this.removeLoading();

			var i = 0, attrs;
			while (attrs = items[i++]) {
				if (!('url' in attrs))
					attrs.url = this.tweetUrl(attrs.id, attrs.from_user);
				this.addItem(attrs);
			};
			
			this.$list.children('li:first').addClass('alpha');
			this.$list.children('li:odd').addClass('even');
			this.$list.children('li:even').addClass('odd');
			this.$list.children('li:last').addClass('omega');
		},

		/**
		 * @Object   from_user, url, text, created_at
		 */
		addItem: function(attrs)
		{
			var timestamp = '<a class="timestamp" href="' + attrs.url + '">'
				+ tweets.relativeTime(attrs.created_at) + '</a><div class="clear"></div>';

			text = this.stripUser(attrs.text, attrs.from_user);
			text = this.linkUrls(text);
			text = this.linkUsers(text);
			text = this.linkHashTags(text, attrs.from_user);
			text = this.makeHearts(text);
			var text = '<span class="text">' + text + '</span>';

			this.$list.append('<li>' + text + timestamp + '</li>');
		},

		stripUser: function(text, from_user)
		{
			var regexp = new RegExp('^' + from_user + '\:\\s+');
			return text.replace(regexp, '');	
		},

		tweetUrl: function(id, from_user)
		{
			return 'http://twitter.com/' + from_user + '/statuses/' + id;
		},

		linkUrls: function(string)
		{
			var regexp = /((ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?)/gi;
			var replace = '<a href="$1">$1</a>';
			return string.replace(regexp, replace);
		},

		linkUsers: function(string)
		{
			var regexp = /[\@]+([A-Za-z0-9-_]+)/gi;
			var replace = '<a href="http://twitter.com/$1">@$1</a>';
			return string.replace(regexp, replace);
		},

		linkHashTags: function(string, from)
		{
			var regexp = / [\#]+([A-Za-z0-9-_]+)/gi;
			var replace = ' <a href="http://search.twitter.com/search?q=&tag=$1&lang=all&from='
				+ from + '">#$1</a>';
			return string.replace(regexp, replace);
		},

		makeHearts: function(string)
		{
			var regexp = /(&lt;)+[3]/gi;
			var replace = '<tt class="heart">&#x2665;</tt>';
			return string.replace(regexp, replace);
		}

	};

	if (options) {
		if (typeof options == 'number' || typeof options == 'string' || options.join) {
			tweets.from = options;
		} else {
			$.extend(tweets, options);
		}
	}
	if (typeof(tweets.from) == 'string')
		tweets.from = [tweets.from];

	return this.each(function()
	{
		tweets.render(this);
	});
};
})(jQuery);