(function($) {

	function start() {
		loadConfig(loadTwitter);
	}
	
	function loadConfig(callback) {
		$.ajax({
			url: "/include/twitter/twitter-config.xml",
			dataType: "xml",
			success: configLoaded,
			complete: callback,
			global: false
		});

		function configLoaded(xml) {
			var replaceRefs = (function() {
				var re = /%{([a-zA-Z_:][-a-zA-Z0-9_:.]*)}%/;

				function replace(text) {
					while (text.search(re) >= 0) {
						text = text.replace(re, $.trim(config$.find(text.match(re)[1]).text()));
					}
					return text;
				}

				return function(node) {
					$(node).contents().filter(function() { return this.nodeType === 3 || this.nodeType === 4; }).each(function() {
						this.nodeValue = replace($.trim(this.nodeValue));
					}).end().filter(function() { return this.nodeType === 1; }).each(function() {
						if (this.attributes.length > 0)	{
							$.each(this.attributes, function() {
								this.nodeValue = replace($.trim(this.nodeValue));
							});
						}
						replaceRefs(this);
					});
				};
			})();

			config$ = $(xml).find("twitter-config");

			// recursively descend through the config XML and replace {%...%} references
			replaceRefs(config$.get(0));			
		}
	}

	function loadTwitter() {

		var tweetHTML$;

		var errorTimeoutID = (function() {
			var timeout = parseInt(getConfigText("timeout"));
			if (!isNaN(timeout) && timeout >= 0)
				return setTimeout(error, timeout);
			else
				return null;
		})();

		$.ajax({
			url: getConfigText("twitter-json-request > url"),
			timeout: getConfigText("timeout"),
			dataType: "jsonp",
			data: (function() {
				var data = { };
				config$.find("twitter-json-request > data > attr").each(function() {
					data[$.trim($(this).attr("name"))] = $.trim($(this).attr("value"));
				})
				return data;
			})(),
			success: success,
			complete: complete
		});

		function success(json) {
		
			clearTimeout(errorTimeoutID);

			if (Boolean(json.error)) {
				//alert(json.error);
				return error();
			}

			var hashTags$ = config$.find("hash-tags > match");
			var tweetIndex = 0;

			hashTagSearch: for (var i = 0; i < hashTags$.length; ++i) {
				var current$ = hashTags$.eq(i);

				var regex = (function() { var regex; try { regex = new RegExp($.trim(current$.text())); } catch(e) { regex = new RegExp(); } return regex; })();
				var maxAge = (function() { var maxAge = parseInt(current$.attr("max-age")) * 1000; maxAge = (isNaN(maxAge) || maxAge < 0) ? 0 : maxAge; return maxAge; })();

				var now = new Date().getTime();

				for (var t = 0; t < json.length; ++t) {
					if (maxAge > 0 && now - new Date(json[t]["created_at"]).getTime() > maxAge)
						break;
					for (var h = 0, HTs = json[t]["entities"]["hashtags"]; h < HTs.length; ++h) {
						if (new String(HTs[h]["text"]).search(regex) >= 0) {
							tweetIndex = t;
							break hashTagSearch;
						}
					}
				}
			}

			var tweetJSON = json[tweetIndex];

			tweetHTML$ = $.tmpl(getConfigText("tweet-template"), tweetJSON);

			if (Boolean(tweetJSON["entities"])) {
				var replacements = [];

				if (tweetJSON["entities"]["user_mentions"].length > 0) {
					$.template("user-mention-template", getConfigText("entity-templates > user-mention-template"));
					$.each(tweetJSON["entities"]["user_mentions"], function() {
						replacements.push({
							beginning: this["indices"][0],
							end: this["indices"][1],
							html: $.tmpl("user-mention-template", this)
						});
					});
				}

				if (tweetJSON["entities"]["urls"].length > 0) {
					$.template("external-link-template", getConfigText("entity-templates > external-link-template"));
					$.each(tweetJSON["entities"]["urls"], function() {
						replacements.push({
							beginning: this["indices"][0],
							end: this["indices"][1],
							html: $.tmpl("external-link-template", this)
						});
					});
				}

				if (tweetJSON["entities"]["hashtags"].length > 0) {
					$.template("hash-tag-template", getConfigText("entity-templates > hash-tag-template"));
					$.each(tweetJSON["entities"]["hashtags"], function() {
						replacements.push({
							beginning: this["indices"][0],
							end: this["indices"][1],
							html: $.tmpl("hash-tag-template", this)
						});
					});
				}

				if (replacements.length > 0) {
					replacements.sort(function(a, b) { return a.beginning - b.beginning; });

					var tweetContents$ = $(getConfigText("tweet-contents-selector"), tweetHTML$);
					var tweetText = tweetContents$.text();

					tweetContents$.empty();
					for (var i = 0; i < replacements.length; ++i) {
						tweetContents$.append(tweetText.substring((i === 0) ? 0 : replacements[i-1].end, replacements[i].beginning));
						tweetContents$.append(replacements[i].html);
					}
					tweetContents$.append(tweetText.substring(replacements[replacements.length - 1].end));
				}

				saveTweet(tweetHTML$);
			}
		}
		
		function error() {
			clearTimeout(errorTimeoutID);

			var savedTweet = getSavedTweet();
			
			if (Boolean(savedTweet))
				tweetHTML$ = $("<div></div>").html(savedTweet).children();
			else
				tweetHTML$ = $("<div></div>").html(getConfigText("default-tweet")).children();

			// This function is not defined as the error handler for the AJAX request, so we must directly invoke the complete handler function because it will not be called otherwise.
			complete();
		}

		function complete() {
			$(getConfigText("tweet-selector")).empty().append(tweetHTML$);
		}
	}
	
	function getConfigText(name) {
		return $.trim(config$.find(name).text());
	}

	function saveTweet(tweetHTML$) {
		var hasLocalStorage = (function() {
			try {
				var item = "localstorage";
				localStorage.setItem(item, item);
    	        localStorage.removeItem(item);
        	    return true;
			} catch(e) {
				return false;
			}		
		})();
		
		var hasCookies = Boolean(document.cookie);

		var tweetHTMLString = tweetHTML$.clone().wrap("<div></div>").parent().html();

		if (hasLocalStorage) {
			localStorage.setItem("last-tweet", tweetHTMLString);
		}
		else if (hasCookies) {
			document.cookie = "last-tweet=" + encodeURIComponent(tweetHTMLString) + "; max-age=" + (60*60*24*7); // 1 week
		}
	}

	function getSavedTweet() {
		var hasLocalStorage = (function() {
			try {
				var item = "localstorage";
				localStorage.setItem(item, item);
    	        localStorage.removeItem(item);
        	    return true;
			} catch(e) {
				return false;
			}		
		})();
		
		var hasCookies = Boolean(document.cookie);

		if (hasLocalStorage) {
			return localStorage.getItem("last-tweet");
		}
		else if (hasCookies) {
			var allCookies = document.cookie.split(";");
			for (var i = 0; i < allCookies.length; ++i) {
				var cookie = $.trim(allCookies[i]);
				if (cookie.indexOf("last-tweet=") == 0)
					return decodeURIComponent(cookie.substring(name.length, cookie.length));
			}
		}

		return null;
	}
	
	$(start);

})(jQuery);
