// bof closure
;(function($) {

	// some variables
	var 
		els = ['div:wrap','div:header','div:controls','div:loader','a:more','a:less','ul:list'], // elements for the markup
		cmd = {}, // store methods 
		e = {}, // store elements
		o = {}; // instance options
	
	// plugin declaration
	cmd = $.fn.gtweetsfeed = function(options) {
		
		// sort options out
		o = $.extend({}, $.fn.gtweetsfeed.defaults, options);
		
		// if no username no point going further
		if (!o.username) return $(this);
		
		// cache the root element
		e.root = $(this);
		
		// initialize plugin
		cmd.init();
		
		// chainability
		return e.root;
	};
	
	// extend the commands object
	$.extend(cmd, {
		
		// initialize the plugin
		init: function() {
			cmd.build(); // build elements required
			cmd.markup(); // insert the markup
			cmd.load(); // load the data
		},
		
		// reduce number of tweets by one
		less: function() {
			if (o.key >= o.minimum) {
				if ('fade' == o.effect) e.items.eq(o.key).fadeTo(o.duration, 0, function() { 
					e.items.eq(o.key+1).removeClass(o.prefix+'last');
					e.items.eq(o.key).addClass(o.prefix+'last');
					$(this).hide(); 
				});
				else if ('slide' == o.effect) e.items.eq(o.key).slideUp(o.duration, function() { 
					e.items.eq(o.key+1).removeClass(o.prefix+'last');
					e.items.eq(o.key).addClass(o.prefix+'last');
					$(this).hide(); 
				});
				else e.items.eq(o.key).removeClass(o.prefix+'last').show();
				o.key--;
			};
		},
		
		// increase number of tweets by one
		more: function() {
			if (o.key < o.maximum) {
				e.items.eq(o.key).removeClass(o.prefix+'last');
				o.key++;
				if ('fade' == o.effect) e.items.eq(o.key).show().addClass(o.prefix+'last').fadeTo(o.duration, 1);
				else if ('slide' == o.effect) e.items.eq(o.key).addClass(o.prefix+'last').slideDown(o.duration);
				else e.items.eq(o.key).addClass(o.prefix+'last').show();
			};
		},
		
		// do the intro animation
		intro: function() {
		
			if ('fade' == o.effect) {				
				e.items.each(function(i) {
					if (i < o.number) {
						var $li = $(this);
						setTimeout(function() {
							$li.show().fadeTo(o.duration, 1);
						}, i*o.duration);
					}
					else $(this).hide();
				});
			}
			else if ('slide' == o.effect) {				
				e.items.each(function(i) {
					if (i < o.number) {
						var $li = $(this);
						setTimeout(function() {
							$li.slideDown(o.duration);
						}, i*o.duration);
					}
					else $(this).hide();
				});
			}
			else {
				e.items.each(function(i) {
					if (i < o.number) $(this).show();
					else $(this).hide();
				});
			};			
		},
		
		// load the json data and generate the list
		load: function() {
		
			$.getJSON(o.src_tpl.replace('{username}', o.username)+'?callback=?', function(data) {
				o.total = data.length;
				o.key = o.number-1;
				cmd.render(data);
				cmd.intro();
			});
		},
		
		// build the elements required
		build: function() {
			
			// element creation loop
			$.each(els, function(i, v) {

				// some variables
				var 
					bits = v.split(':'),
					tag = bits[0],
					id = bits[1],
					$e = $('<'+tag+'>').attr('id', o.prefix+id);
				
				// add extras to <a>s
				if (tag == 'a') $e.attr('href', '#'+id).attr('title', o[id+'_title']).click(cmd[id]);
				
				// cache element
				e[id] = $e;
			});
			
		},
		
		// build the markup necessary
		markup: function() {
		
			// build markup
			e.root.append(
				e.wrap.append(
					e.header.html(o.header_tpl.replace('{username}', o.username)).css('display', o.header ? 'block' : 'none'),
					e.controls.append(e.more, e.less).css('display', o.controls ? 'block' : 'none'),
					e.loader,
					e.list.hide()
				)
			);
		},
		
		// render the list of tweets
		render: function(tweets) {
		
		  // empty array
		  var arr = [];		  
		  
		  // loop through tweets
		  $.each(tweets, function(i, v) {
			
			// some variables
			var 
				msg = v.text.replace(/((https?|s?ftp|ssh)\:\/\/[^"\s\<\>]*[^.,;'">\:\s\<\>\)\]\!])/g, function(url) {
					  return '<a href="'+url+'">'+url+'</a>';
					}).replace(/\B@([_a-z0-9]+)/ig, function(reply) {
					  return reply.charAt(0)+'<a href="http://twitter.com/'+reply.substring(1)+'">'+reply.substring(1)+'</a>';
					});

			// add li to the array		
			arr.push('<li>'+
				o.li_tpl
					.replace('{msg}', msg)
					.replace('{username}', o.username)
					.replace('{id}', v.id)
					.replace('{time}', cmd.time(v.created_at))
			+'</li>');
		  
		  });
		  
		  // add the <li>s in the <ul>
		  e.items = e.list.html(arr.join('')).children().css({
			display:'none',
			opacity: ('fade' == o.effect) ? 0 : 1
		  });
		  e.items.eq(0).addClass(o.prefix+'first');
		  e.items.eq(o.key).addClass(o.prefix+'last');
		  e.loader.remove();
		  e.list.show();
		  
		},

		// calculate the relative time
		time: function(time_value) {
		  
		  // some variables
		  var 
			values = time_value.split(" "),
			time_value = values[1] + " " + values[2] + ", " + values[5] + " " + values[3],
			parsed_date = Date.parse(time_value),
			relative_to = (arguments.length > 1) ? arguments[1] : new Date(),
			delta = parseInt((relative_to.getTime() - parsed_date) / 1000),
			delta = delta + (relative_to.getTimezoneOffset() * 60);

		  // return the appropriate string for the time
		  if (delta < 60) return 'less than a minute ago';
		  else if(delta < 120) return 'about a minute ago';
		  else if(delta < (60*60)) return (parseInt(delta / 60)).toString() + ' minutes ago';
		  else if(delta < (120*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';
		}
	});
	
	
	// plugin defaults
	$.fn.gtweetsfeed.defaults = {
		prefix: 'gtweetfeed-', // CSS id prefix used to build the elements
		controls: false, // button to alter the number of tweets displayed
		more_title: 'Show more tweet', // title attribute for the more button
		less_title: 'Show less tweet', // title attribute for the less button
		username: '', // the username of the profile to grab the tweets of
		effect: 'fade', // transition effect used to add/remove/generate the tweets list (fade|slide|false)
		duration: 500, // duration in milliseconds used for effect if any used
		header: false, // enable or disable the displaying of a header in the interface
		header_tpl: '{username}\'s Tweets', // header used to build the template string ({username} only}
		maximum: 15, // the number maximum of tweets allowed to be displayed
		minimum: 1, // the minimum of tweets allowed
		number: 5, // the number of tweet to display at startup
		src_tpl: 'http://twitter.com/statuses/user_timeline/{username}.json', // template used to generate the data source URL
		li_tpl: '<span>{msg}</span><a href="http://twitter.com/{username}/statuses/{id}">{time}</a>' // template used to generated the <li>
	};
	

// eof closure
})(jQuery);
