// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
  var cache = {};
  
  this.tmpl = function tmpl(str, data){
    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.
    var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :
      
      // Generate a reusable function that will serve as a template
      // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +
        
        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +
        
        // Convert the template into pure JavaScript
        str.replace(/[\r\t\n]/g, " ")
			.replace(/'(?=[^#]*#>)/g,"\t")
			.split("'").join("\\'")
			.split("\t").join("'")
			.replace(/<#=(.+?)#>/g, "',$1,'")
			.split("<#").join("');")
			.split("#>").join("p.push('")
			+ "');}return p.join('');");
    
    // Provide some basic currying to the user
    return data ? fn( data ) : fn;
  };
})();

Date.prototype.setISO8601 = function (string) {
    var regexp = "([0-9]{4})(-([0-9]{2})(-([0-9]{2})" +
        "(T([0-9]{2}):([0-9]{2})(:([0-9]{2})(\.([0-9]+))?)?" +
        "(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?";
    var d = string.match(new RegExp(regexp));

    var offset = 0;
    var date = new Date(d[1], 0, 1);

    if (d[3]) { date.setMonth(d[3] - 1); }
    if (d[5]) { date.setDate(d[5]); }
    if (d[7]) { date.setHours(d[7]); }
    if (d[8]) { date.setMinutes(d[8]); }
    if (d[10]) { date.setSeconds(d[10]); }
    if (d[12]) { date.setMilliseconds(Number("0." + d[12]) * 1000); }
    if (d[14]) {
        offset = (Number(d[16]) * 60) + Number(d[17]);
        offset *= ((d[15] == '-') ? 1 : -1);
    }

    offset -= date.getTimezoneOffset();
    time = (Number(date) + (offset * 60 * 1000));
    this.setTime(Number(time));
}

Date.prototype.toISO8601String = function (format, offset) {
    /* accepted values for the format [1-6]:
     1 Year:
       YYYY (eg 1997)
     2 Year and month:
       YYYY-MM (eg 1997-07)
     3 Complete date:
       YYYY-MM-DD (eg 1997-07-16)
     4 Complete date plus hours and minutes:
       YYYY-MM-DDThh:mmTZD (eg 1997-07-16T19:20+01:00)
     5 Complete date plus hours, minutes and seconds:
       YYYY-MM-DDThh:mm:ssTZD (eg 1997-07-16T19:20:30+01:00)
     6 Complete date plus hours, minutes, seconds and a decimal
       fraction of a second
       YYYY-MM-DDThh:mm:ss.sTZD (eg 1997-07-16T19:20:30.45+01:00)
    */
    if (!format) { var format = 6; }
    if (!offset) {
        var offset = 'Z';
        var date = this;
    } else {
        var d = offset.match(/([-+])([0-9]{2}):([0-9]{2})/);
        var offsetnum = (Number(d[2]) * 60) + Number(d[3]);
        offsetnum *= ((d[1] == '-') ? -1 : 1);
        var date = new Date(Number(Number(this) + (offsetnum * 60000)));
    }

    var zeropad = function (num) { return ((num < 10) ? '0' : '') + num; }

    var str = "";
    str += date.getUTCFullYear();
    if (format > 1) { str += "-" + zeropad(date.getUTCMonth() + 1); }
    if (format > 2) { str += "-" + zeropad(date.getUTCDate()); }
    if (format > 3) {
        str += "T" + zeropad(date.getUTCHours()) +
               ":" + zeropad(date.getUTCMinutes());
    }
    if (format > 5) {
        var secs = Number(date.getUTCSeconds() + "." +
                   ((date.getUTCMilliseconds() < 100) ? '0' : '') +
                   zeropad(date.getUTCMilliseconds()));
        str += ":" + zeropad(secs);
    } else if (format > 4) { str += ":" + zeropad(date.getUTCSeconds()); }

    if (format > 3) { str += offset; }
    return str;
}

var Twitter = (function() {
	
	var updatesToShow = 7;
	
	var Timeline = function(url, data) {
		/* Last status id to send to Twitter when reloading this timeline */
		var _lastStatusId = 0;
		
		/* Listeners to notify when we receive new statuses in this timeline */
		var _listeners = [];
		
		function fireStatusUpdate(statuses) {
			for (var i = 0; i < _listeners.length; i++) {
				_listeners[i](this, statuses);
			}
		}
		
		function handleStatuses(statuses) {
			fireStatusUpdate(statuses);
		}
		
		this.updateStatuses = function() {
			if (data == null) data = {};
			if (url.indexOf("search.twitter.com") != -1) {
				data.rpp = updatesToShow;
			} else {
				data.count = updatesToShow;
			}
			data.suppress_response_codes = true;
			if (_lastStatusId != 0) {
				data.since_id = _lastStatusId;
			}
			$.ajax({
				url: url,
				data: data,
				dataType: "jsonp",
				success: function(data, textStatus) {
					if (data.error != null) {
						alert(data.error);
					} else {
						if (data.results != null)
							data = data.results;
						handleStatuses(data);
						if (data.length > 0)
							_lastStatusId = data[0].id;
					}
				},
				error: function (req, textStatus, errorThrown) {
					alert("ERROR " + textStatus);
				}
			});
		};
		
		this.subscribe = function(f) {
			_listeners.push(f);
		};
	};
	
	var Utils = function() {
		var months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
		
		function pad(num, padding) {
			var str = "" + num;
			while (str.length < padding) {
				str = "0" + str;
			}
			return str;
		}
		
		function formatDate(d) {
			return d.getHours() + ":" + pad(d.getMinutes(), 2) + " " + 
				d.getDate() + " " + months[d.getMonth()] + " " + d.getFullYear();
		}
		
		return {
			pad: pad,
			formatDate: formatDate
		};
	}();
	
	function formatTweetText(text) {
		/* Make URLs into links */
		text = text.replace(/(^|\s+)(http:\/\/\S+)/g, "$1<a href=\"$2\" target=\"_blank\">$2</a>");
		/* Make @names into links */
		text = text.replace(/(^|\s+)@([a-zA-Z0-9_]+)/g, "$1<a href=\"http://twitter.com/$2\">@$2</a>");
		/* Make www. into links */
		text = text.replace(/(^|\s+)(www\.\S+)/g, "$1<a href=\"http://$2\" target=\"_blank\">$2</a>");
		return text;
	}
	
	var updateCounter = 0;
	
	function showStatuses(where, statuses, type, statusTemplate, oldestStatus) {
		if (statuses.length == 0)
			return;
		
		var updateId = "update-" + updateCounter++;
		
		var html = "";
		for (var i = 0; i < statuses.length; i++) {
			var status = statuses[i];
			if (oldestStatus != null && status.date.getTime() < oldestStatus.getTime())
				continue;
			
			var id = where + "-status-" + status.id;
			
			/* Check for and avoid creating duplicates. But note that we still iterate over them below
			 * so tweets that are in the friends list and a reply will be treated twice!
			 */
			if ($("#" + id).length == 0) {
				var result = tmpl(statusTemplate, {
					status:status,
					id:id,
					statusTextFormatted:Twitter.formatTweetText(status.text),
					statusDateFormatted:Twitter.Utils.formatDate(status.date),
					statusDateISO8601:status.date.toISO8601String(),
					updateId: updateId
				});
				html += result;
			}
		}
		
		$("#" + where + " .loading").remove();
		$("#" + where).prepend(html);
		$("#" + where + " .twitter-status:gt(" + (updatesToShow-1) + ")").remove();
		
		for (var i = 0; i < statuses.length; i++) {
			var status = statuses[i];
			var statusNode = $("#" + where + "-status-" + status.id);
			statusNode.data("twitter.id", status.id);
			statusNode.data("twitter.date", status.date);
			statusNode.data("twitter.type", type);
			statusNode.addClass(type);
		}
		
		updateTimeAgo();
	}
	
	function normalizeStatuses(statuses) {
		/* Create custom properties */
		for (var i = 0; i < statuses.length; i++) {
			/* Normalize our statuses to always have a user property even if they're a DM */
			if (!statuses[i].user && statuses[i].sender)
				statuses[i].user = statuses[i].sender;
			/* Date property */
			var createdAt = statuses[i].created_at;
			if (createdAt != null) {
				/* Correct search response dates that are missing the comma */
				if (createdAt.indexOf(",") == -1) {
					var ray = createdAt.split(" ");
					createdAt = ray[0] + ", " + ray[2] + " " + ray[1] + " " + ray[5] + " " + ray[3] + " " + ray[4];
				}
			}
			statuses[i].date = new Date(createdAt);
		}
	}
	
	var newsTimeline = new Timeline("http://twitter.com/statuses/user_timeline/15381634.json");
	newsTimeline.subscribe(function(t, statuses) {
		normalizeStatuses(statuses);
		showStatuses("tweets", statuses, "tweet", "newstemplate");
	});
	
	var praiseTimeline = new Timeline("http://search.twitter.com/search.json", { q: 'to:mobilefotos OR "mobile fotos" OR mobilefotos -from:mobilefotos -cracker' });
	praiseTimeline.subscribe(function(t, statuses) {
		normalizeStatuses(statuses);
		showStatuses("tweets2", statuses, "tweet", "praisetemplate");
	});
	
	function doUpdateStatuses() {
		newsTimeline.updateStatuses();
		praiseTimeline.updateStatuses();
	}
	
	function updateTimeAgo() {
		$('abbr[class*=timeago]').timeago();
	}
	
	function error(message) {
		
	}
	
	var statusUpdateInterval;
	
	function startUpdateInterval() {
		clearInterval(statusUpdateInterval);
		
		var statusUpdateInterval = setInterval(function() {
			doUpdateStatuses();
		}, 1000 * 60 * 5);
		
		setTimeout(doUpdateStatuses, 100);
	}
	
	function stopUpdateInterval() {
		clearInterval(statusUpdateInterval);
	}
	
	function updateStatuses() {
		startUpdateInterval();
	}
	
	return {
		init: function() {
			jQuery.timeago.settings.refreshMillis = 0;
			setInterval(function() {
				updateTimeAgo();
			}, 60000);
			
			startUpdateInterval();
		},
		
		updateStatuses: updateStatuses,
		formatTweetText: formatTweetText,
		
		Utils: Utils
	};
})();


$(function() {
	Twitter.init();
});