/*
 * jQuery TweetMachine v0.2.1a
 *
 * Copyright (c) 2013 Ryan Giglio (@ryangiglio)
 */
(function ($) {
    // Plugin body
	$.fn.tweetMachine = function (query, options, callback) {
        // For each instance of the plugin
		$(this).each(function () {
			var settings, tweetMachine;
            // If this.tweetMachine is already initialized, just change the settings
			if (this.tweetMachine) {
                // Overwrite the initialized/default settings
				settings = $.extend(this.tweetMachine.settings, options);
				this.tweetMachine.settings = settings;

                // If a new query has been passed
				if (query) {
                    // Replace the old query
					this.tweetMachine.query = query;
				}
                // If a tweet interval is already set up
				if (this.tweetMachine.interval) {
                    // Refresh now so the new settings can kick in
					this.tweetMachine.refresh();
				}
                // If a new callback was passed
				if (callback) {
                    // Replace the old callback
					this.tweetMachine.callback = callback;
				}
			} else { // It's not initialized, so let's do that
				settings = $.extend({
					backendScript:  'ajax/getFromTwitter.php', // Path to your backend script that holds your Twitter credentials and calls the API
					endpoint:       'search/tweets', // Twitter API endpoint to call. Currently only search/tweets is supported
					rate:           5000, // Rate in ms to refresh the tweets. Any higher than 5000 for search/tweets will get you rate limited
					limit:          5, // Number of tweets to display at a time
					autoRefresh:    true, // CURRENTLY REQUIRED. Auto-refresh the tweets
					animateOut:     false, // NOT YET SUPPORTED. Animate out old tweets.
					animateIn:      true, // Fade in new tweets.
					tweetFormat: "<li class='tweet'><img class='avatar' src=''/><div class='meta'><a href='' class='username'></a><a href='' class='time'></a></div><p class='content'></p></li>", // Format for each tweet
					localization: { // Verbiage to use for timestamps
						seconds:    'seconds ago',
						minute:     'a minute ago',
						minutes:    'minutes ago',
						hour:       'an hour ago',
						hours:      'hours ago',
						day:        'a day ago',
						days:       'days ago'
					},
					filter:         false // Function to filter tweet results.
				}, options);
				this.tweetMachine = {
					settings: settings, // Set the settings object
					query: query, // Set the query to search for
					interval: false, // This will hold the refresh interval when it is created
					container: this, // Set the object that contains the tweets
					lastTweetID: null, // This will hold the ID of the last tweet displayed
					callback: callback, // This callback will run after each refresh

                    /*
                     * Function to generate a relative timestamp from Twitter's time string
                     */
					relativeTime: function (timeString) {
						var delta, parsedDate, r;
                        
                        // Create a Date object
						parsedDate = Date.parse(timeString);

                        // Get the number of seconds ago that the tweet was created
						delta = (Date.parse(Date()) - parsedDate) / 1000;
                        
                        // String to hold the relative time
						r = '';

                        // If it was less than a minute ago
						if (delta < 60) {
							r = delta + " " + settings.localization.seconds;
                        // If it was less than 2 minutes ago
						} else if (delta < 120) {
							r = settings.localization.minute;
                        // If it was less than 45 minutes ago
						} else if (delta < (45 * 60)) {
							r = (parseInt(delta / 60, 10)).toString() + " " + settings.localization.minutes;
                        // If it was less than 90 minutes ago
						} else if (delta < (90 * 60)) {
							r = settings.localization.hour;
                        // If it was less than a day ago
						} else if (delta < (24 * 60 * 60)) {
							r = '' + (parseInt(delta / 3600, 10)).toString() + " " + settings.localization.hours;
                        // If it was less than 2 days ago
						} else if (delta < (48 * 60 * 60)) {
							r = settings.localization.day;
						} else {
							r = (parseInt(delta / 86400, 10)).toString() + " " + settings.localization.days;
						}
						return r;
					},

                    /*
                     * Function to update the timestamps of each tweet
                     */
					updateTimestamps: function () {
						var tweetMachine;
						tweetMachine = this;
                        // Loop over each timestamp
						$(tweetMachine.container).find('.time').each(function () {
							var originalTime, timeElement;

                            // Save a reference to the time element
							timeElement = $(this);
                            
                            // Get the original time from the data stored on the timestamp
							originalTime = timeElement.data('timestamp');

                            // Generate and show a new time based on the original time
							timeElement.html(tweetMachine.relativeTime(originalTime));
						});
					},
					
                    /*
                     * Function to parse the text of a tweet and and add links to links, hashtags, and usernames
                     */
					parseText: function (text) {
                        // Links
						text = text.replace(/[A-Za-z]+:\/\/[A-Za-z0-9-_]+\.[A-Za-z0-9-_:%&\?\/.=]+/g, function (m) {
							return '<a href="' + m + '" target="_blank">' + m + '</a>';
						});
                        // Usernames
						text = text.replace(/@[A-Za-z0-9_]+/g, function (u) {
							return '<a href="http://twitter.com/#!/' + u.replace(/^@/, '') + '" target="_blank">' + u + '</a>';
						});
                        // Hashtags
						text = text.replace(/#[A-Za-z0-9_\-]+/g, function (u) {
							return '<a href="http://twitter.com/#!/search?q=' + u.replace(/^#/, '%23') + '" target="_blank">' + u + '</a>';
						});
						return text;
					},

                    /*
                     * Function to build the tweet as a jQuery object
                     */
					buildTweet: function (tweet) {
						var tweetMachine, tweetObj;
						tweetMachine = this;

                        // Create the tweet from the tweetFormat setting
						tweetObj = $(tweetMachine.settings.tweetFormat);
                        
                        // Set the avatar. NOTE: reasonably_small is Twitter's suffix for the largest square avatar that they store
						//tweetObj.find('.avatar')
                            //.attr('src', tweet.user.profile_image_url.replace("normal", "reasonably_small"));

                        // Set the username
						tweetObj.find('.username')
                            .attr('href', "http://twitter.com/" + tweet.user.screen_name)
                            .attr('target', '_blank')
                            .html("" + tweet.user.screen_name);

                        // Set the timestamp
						tweetObj.find('.time')
                            .attr('href', "http://twitter.com/" + tweetMachine.query + "/status/" + tweet.id_str)
                            .attr('target', '_blank')
                            .html(tweetMachine.relativeTime(tweet.created_at))
                            // Save the created_at time as jQuery data so we can update it later
                            .data('timestamp', tweet.created_at);

                        // Set the text
						tweetObj.find('.text')
                            .html(tweetMachine.parseText(tweet.text));

                        // If we are animating in the new tweets
						if (tweetMachine.settings.animateIn) {
                            // Set the opacity to 0 so it can fade in
							tweetObj.css('opacity', '0');
						}

						return tweetObj;
					},

                    /*
                     * Function to handle the reloading of tweets
                     */
					refresh: function (firstLoad) {
						var queryParams, tweetMachine;
						tweetMachine = this;

                        // If it is the first load or we're refreshing automatically
						if (firstLoad || tweetMachine.settings.autoRefresh) {
                            // Set the query parameters that the endpoint needs
                            /*
                             * TODO These will vary depending on the endpoint
                             */
                            if (tweetMachine.settings.endpoint === "search/tweets") {
                                queryParams = {
                                    screen_name: tweetMachine.query,
                                    count: (this.settings.requestLimit) ? this.settings.requestLimit: this.settings.limit
                                    //since_id: tweetMachine.lastTweetID
                                };
                            }

                            // Call your backend script to get JSON back from Twitter
							$.getJSON(tweetMachine.settings.backendScript, {
                                action: tweetMachine.settings.endpoint,
                                username: tweetMachine.query,
                                count: (this.settings.requestLimit) ? this.settings.requestLimit: this.settings.limit
							}, function (tweets) {
								var tweetsDisplayed;

                                // If we got a response from Twitter
                                if ( tweets[0] ) {
                                    // If there is an error message
                                    if ( tweets[0].message ) {
                                        // If there is already an error displayed
                                        if ( $('.twitter-error').length ) {
                                            // Update the error message
                                            $('.twitter-error').html('<p class="tweet-machine-error">Error  ' + tweets[0].code + ': ' + tweets[0].message + '</p>');
                                        }
                                        else { // There isn't an error displayed yet
                                            // Display an error message above the container
                                            $(tweetMachine.container).before('<p class="twitter-error">Error  ' + tweets[0].code + ': ' + tweets[0].message + '</p>');
                                        }
                                    }
                                    // There are tweets
                                    else {
                                        // If there was an error before
                                        if ( $('.twitter-error').length ) {
                                            // Remove it
                                            $('.twitter-error').remove();
                                        }

                                        // Reverse them so they are added in the correct order
                                        tweets.reverse();

                                        // Count the number of tweets displayed
                                        tweetsDisplayed = 0;

                                        // Loop through each tweet
                                        $.each(tweets, function () {
                                            var tweet, tweetObj;
                                            tweet = this;

                                            // If there is no filter, or this tweet passes the filter
                                            if (!tweetMachine.settings.filter || tweetMachine.settings.filter(this)) {
                                                // Build the tweet as a jQuery object
                                                tweetObj = tweetMachine.buildTweet(tweet);

                                                // If there are already tweets on the screen
                                                if (!firstLoad) {

                                                    // If we are animating out the old tweets
                                                    if (tweetMachine.settings.animateOut) {
                                                        /*
                                                         * TODO Support this feature
                                                         */
                                                    } else { // We are not animating the old tweets
                                                        // Remove them
                                                        $(tweetMachine.container).children(':last-child').remove();
                                                    }
                                                }

                                                // Prepend the new tweet
                                                $(tweetMachine.container).prepend(tweetObj);

                                                // If we are animating in the new tweets
                                                if (tweetMachine.settings.animateIn) {
                                                    // Fade in the new tweet
                                                    /*
                                                     * TODO Figure out why .fadeIn() doesn't work
                                                     */
                                                    $(tweetMachine.container).children(':first-child').animate({
                                                        opacity: 1
                                                    });
                                                }

                                                // Increment the tweets diplayed
                                                tweetsDisplayed++;
                                                
                                                // Save this tweet ID so we only get newer noes
                                                tweetMachine.lastTweetID = tweet.id_str;
                                                
                                                // If we've reached the limit of tweets to display
                                                if (tweetsDisplayed > tweetMachine.settings.limit) {
                                                    // Quit the loop
                                                    return false;
                                                }
                                            }
                                        });
                                    }
                                }
								//Callback function
								if (typeof tweetMachine.callback === "function") {
									if(typeof tweets === 'undefined' || typeof tweetsDisplayed === 'undefined' ) {
										tweets = null;
										tweetsDisplayed = 0;
									}
									tweetMachine.callback(tweets, tweetsDisplayed);
								}
							});
						}
                        /* TODO: Implement an "x new Tweets, click to refresh" link if auto refresh is turned off
                        else {
                        }
                        */
					},

                    // Start refreshing
					start: function () {
						var tweetMachine;
						tweetMachine = this;

                        // If there's no interval yet
						if (!this.interval) {
                            // Create an interval to refresh after the rate has passed
							this.interval = setInterval(function () {
								tweetMachine.refresh();
							}, tweetMachine.settings.rate);
                            // Start refreshing with the firstLoad flag = true
							this.refresh(true);
						}
					},

                    // Stop refreshing
					stop: function () {
						var tweetMachine;
						tweetMachine = this;

                        // If there is an interval
						if (tweetMachine.interval) {
                            // Clear it
							clearInterval(tweetMachine.interval);
                            
                            // Remove the reference to it
							tweetMachine.interval = false;
						}
					},

                    // Clear all tweets
					clear: function () {
						var tweetMachine;
						tweetMachine = this;
                        
                        // Remove all tweets
						$(tweetMachine.container).find('.tweet').remove();

                        // Set the lastTweetID to null so we start clean next time
						tweetMachine.lastTweetID = null;
					}
				};

                // Save a global tweetMachine object
				tweetMachine = this.tweetMachine;

                // Create an interval to update the timestamps
				this.timeInterval = setInterval(function () {
					tweetMachine.updateTimestamps();
				}, tweetMachine.settings.rate);

                // Start the Machine!
				this.tweetMachine.start();
			}
		});
	};
})(jQuery);
