var BBCTWITTERFEED = function () {

	// Set some state and config variables
	var init = true;
	var timeOut = null;
	var userConfig = null;
	
	// Default Configuration:
	// This can be overwritten by passing a "config object"
	// to the main function (updateFeed)
	var defaultConfig = {
		id: 'bbcreadingfest',
		targetDivID: 'feed-main',
		staticDivID: 'feed-static',
		maxUrlLength: 25,
		newWindowID: "bbcTwitterFeedWindow",
		photoWidth: 48,
		photoHeight: 48,
		updateFreqInSeconds: 60,
		apiRoot: 'http://twitter.com/statuses/user_timeline.json',
		linkRoot: 'http://twitter.com/',
		numberOfTweets: 50,
		showAvatars: false,
		images: {
                    root: '/readingandleeds/2009/twitter/images/',
                    reply: 'btn_reply.gif',
                    retweet: 'btn_retweet.gif'
		}
	}
	
	// CSS Config:
	// These classes are applied to the output HTML
	var css = {
		el_list: 'feed-list',
		el_loader: 'feed-loading',
		el_outer: 'feed-text-outer',
		el_text: 'feed-text-inner',
		el_link: 'feed-link',
		el_date: 'feed-date',
		el_photo: 'feed-photo',
		el_photo_link: 'feed-photo-link',
		el_new_tweet: 'feed-new-tweet',
		el_prev_tweet: 'feed-prev-tweet',
		el_tweet_id: 'feed-tweet-id',
		el_hide: 'feed-hide',
		el_reply: 'reply',
		el_retweet: 'retweet'
	}
	
	// Parsing Helpers:
	// The following (very useful!) prototypes are adapted from
	// www.simonwhatley.co.uk/parsing-twitter-usernames-hashtags-and-urls-with-javascript
	
	String.prototype.parseURL = function(maxUrlLength) {
		return this.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+/g, function(url) {
			if( url.length > maxUrlLength ) {
				var trimmedURL = url.substr(0,maxUrlLength) + "...";
				return trimmedURL.link(url);
			} else {
				return url.link(url);
			}
		});
	};

	String.prototype.parseUsername = function() {
		return this.replace(/[@]+[A-Za-z0-9-_]+/g, function(u) {
			var username = u.replace("@","")
			return u.link("http://twitter.com/"+username);
		});
	};
	
	String.prototype.parseHashtag = function() {
		return this.replace(/[#]+[A-Za-z0-9-_]+/g, function(t) {
			var tag = t.replace("#","%23")
			return t.link("http://search.twitter.com/search?q="+tag);
		});
	};

	// Time/Date Conversion:
	// Converts time... into WORDS!!
	function relative_time(time_value) {
		var parsed_date = Date.parse(time_value);
		var relative_to = (arguments.length > 1) ? arguments[1] : new Date();
		var delta = parseInt((relative_to.getTime() - parsed_date) / 1000);
		if(delta < 60) {
			return 'less than a minute ago';
		} else if(delta < 120) {
			return 'about a minute ago';
		} else if(delta < (45*60)) {
			return (parseInt(delta / 60)).toString() + ' minutes ago';
		} else if(delta < (90*60)) {
			return 'about an hour ago';
		} else if(delta < (24*60*60)) {
			return 'about ' + (parseInt(delta / 3600)).toString() + ' hours ago';
		} else if(delta < (48*60*60)) {
			return '1 day ago';
		} else {
			return (parseInt(delta / 86400)).toString() + ' days ago';
		}
	}
	
	// Main Function:
	// This function makes the request to Twitter, and writes out the 
	// result to the specified div. It's just a simple REST API query,
	// which is passed by the initial calling function.
	function updateFeed(since_id,myConfig) {
		
		// Check if default config object should be overwritten by user config object
		if(myConfig){
			userConfig = myConfig;
		}
		
		var config = glow.lang.apply(defaultConfig, userConfig || {});

		//console.warn("(function call 1) Since ID: " + since_id);

		
		// If it's our first time, hide the static content and create a UL
		if(init){
			glow.dom.get("#" + config['staticDivID']).addClass(css['el_hide']);
			glow.dom.get("a.external").each(function(i){
				glow.events.addListener(this,'click',function () {
					window.open(this.href,config['newWindowID']);
					return false;
				});
			});
			var ulOuterNode = glow.dom.create('<ul id="' + css['el_list'] + '"></ul>');
			glow.dom.get('#' + config['targetDivID']).append(ulOuterNode);
		}
		
		// Form the query string to add to the API call
		var delayInMS = config['updateFreqInSeconds']*1000;
		var apiCall = config['apiRoot'] + "?callback={callback}";
		apiCall+= "&id=" + config['id'];
		apiCall+= "&count=" + config['numberOfTweets'];
		apiCall+= "&since_id=" + since_id;
		//console.log("(function call 2) apiCall: " + apiCall);

		// Do a search query and get the results
		glow.net.loadScript(apiCall, {
		
			// Call back function which runs when the data is returned
			onLoad: function(results) {

				//console.log("(onLoad 1) init: " + init);
				
				// Only update HTML if we've got some new tweets
				if (results.length > 0) {

					// Update CSS classes on existing tweets
					// This can be removed if it causes performance issues
					glow.dom.get("#" + css['el_list'] + " li").removeClass(css['el_prev_tweet']);
					glow.dom.get("#" + css['el_list'] + " li." + css['el_new_tweet']).removeClass(css['el_new_tweet']).addClass(css['el_prev_tweet']);
					
					// Loop through each tweet, creating an LI element for it
					for (var i in results) {
						var item = results[i];
						parsedText = item.text.parseURL(config['maxUrlLength']).parseUsername().parseHashtag();
						
						if(init){
							var listClass = "";
						} else {
							var listClass = css['el_new_tweet'];
						}
						
						var listItem = '<li id="' + item.id + '" class="' + listClass + '">';
						if(config['showAvatars']){
							listItem += '<a class="' + css['el_photo_link'] + '" href="http://twitter.com/' + item.user.screen_name + '"><img class="' + css['el_photo'] + '" width="'+config['photoWidth']+'" height="'+config['photoHeight']+'" src="' + item.user.profile_image_url + '" alt="'+item.user.screen_name+'"/></a>';
						}
						listItem += '<div class="' + css['el_outer'] + '">';
						listItem += '<span class="' + css["el_text"] + '">';
						//listItem += '<span class="' + css["el_tweet_id"] + '">[' + item.id + ']</span>';
						listItem += parsedText + '</span>';
						listItem += '<a class="' + css["el_date"] + '" href="http://twitter.com/' + item.user.screen_name + '/statuses/'+ item.id + '" title="View tweet on twitter">' + relative_time(item.created_at) + '</a>';
						
						// Reply
						var replyURL = config['linkRoot'] + "home?status=" + encodeURIComponent("@" + item.user.screen_name) + "&in_reply_to_status_id=" + item.id + "&in_reply_to=" + item.user.screen_name;
						listItem += '<a title="Reply to this tweet" class="' + css["el_reply"] + '" href="' + replyURL + '"><img src="' + config['images']['root'] + config['images']['reply'] + '"></a>';
						
						// Retweet
						var retweetURL = config['linkRoot'] + "home?status=" + encodeURIComponent("RT @" + item.user.screen_name + ' ' + (item.text));
						listItem += '<a title="Repost this tweet" class="' + css["el_retweet"] + '" href="' + retweetURL + '"><img src="' + config['images']['root'] + config['images']['retweet'] + '"></a>';
					
						listItem += '</div></li>';
						
						// Add the LI element to the list, add an animation if it's "new"
						if(init){
							glow.dom.create(listItem).appendTo("#" + css['el_list']);
						} else {
							glow.dom.create(listItem).prependTo("#" + css['el_list']);
							glow.anim.css("#" + item.id, 1, { "opacity" : {from:0,to:1} } ).start();
						}
						
						// Force all links in the tweet to open in a new window
						glow.dom.get("#" + item.id + " a").each(function(i){
							glow.events.addListener(this,'click',function () {
								window.open(this.href,config['newWindowID']);
								return false;
							});
						});
						
					}
					
					since_id = results[0].id;
                                        init = false;
				}

				// Set up the next call
				timeOut = clearTimeout();
				functionCall = "BBCTWITTERFEED.updateFeed(" + since_id + ")";
				//console.log("(function call 3) function call: " + functionCall + "," + delayInMS);
				timeOut = setTimeout(functionCall,delayInMS);
					
			},

                        onError: function() {
				// Set up the next call
				timeOut = clearTimeout();
				functionCall = "BBCTWITTERFEED.updateFeed(" + since_id + ")";
				//console.log("(function onError call) function call: " + functionCall + "," + delayInMS);
				timeOut = setTimeout(functionCall,delayInMS);
                        }
					
		});

	}
	
	return {
		updateFeed : updateFeed
	};

}();