(function($) {
    
    var settings = {
        queryType : 'query', // query, user, userlist
        query : 'twitter',
        listName : '',
        jsonData : '',
        dataType : 'jsonp',
        count : 20, // returns per page
        ignoreReplies: true,
        template : '<div class="img"><a href="http://twitter.com/#{screenName}/"><img src="#{profileImageUrl}" height="#{avatarSize}" width="#{avatarSize}" alt="#{screenName}" title="#{screenName}" /></a></div><div class="txt"><span class="msg"><a class="screenname" href="http://twitter.com/#{screenName}/">#{screenName}</a>: #{text}</span><br /><span class="footer"><span class="date">#{createdAt}</span> (<a  href="http://twitter.com/intent/tweet?in_reply_to=#{id}">Reply</a>) (<a  href="http://twitter.com/intent/retweet?tweet_id=#{id}">RT</a>)</span></div>',
        avatarSize : 48,
        containerClass : 'twitter_list',
        refresh : 30000,
        refresh_url : '',
        firstRun : true,
        fadeSpeed: 2000,
        timeFormat: 'local', 
        lastId : '0',
        lastLastId : '0'
    };
    
    $.fn.twitterFeed = function(options) {
        if (options) {
            $.extend(settings, options);
        }
        
        return this.each(function() {
            twitterFeed(this);
        });
    };
    
    function twitterFeed(container) {
        getData(container);
        
        if (settings.refresh) {
            setTimeout((function() {
                twitterFeed(container);
            }), settings.refresh);
        }
    }
        
    function getData(container) {
        var url = getUrl();
        
        $.ajax({
            url : url,
            dataType : settings.dataType,
            type : 'GET',
            timeout : 5000,
            error : function() {
                $(container).html('<p>Unable to retrieve Twitter feed.</p>');
            },
            success : function(json) {
                if (settings.firstRun) {
                    $(container).html('');
                }
                generateHtml(container, json);
            }
        });
    }
    
    function generateHtml(container, json) {
        if ((settings.queryType === 'query') && !!!json.results) {
            return;
        }

        var createContainer = true;
        $.each(((settings.queryType == 'query') ? json.results : json), function(i, item) {
            if (i === 0) settings.lastLastId = settings.lastId;
            if (item.id_str.length === settings.lastLastId.length) {
                if (item.id_str <= settings.lastLastId) return true;
            } else if (item.id_str.length > settings.lastLastId.length) {
                // can't be lte, so don't do anything;
            } else {
                // if shorter, must be lte so return true
                return true;
            }
            if ((settings.ignoreReplies === true) && (item.text.charAt(0) === '@')) return true;
            
            if (createContainer) {
                settings.lastId = item.id_str;
                $('<ul></ul>').attr('id', 'ul' + settings.lastId).addClass(settings.containerClass).addClass(settings.queryType).prependTo(container);
                createContainer = false;
            }
            
            var createdAt = (settings.timeFormat === 'local') ? localTime(item.created_at) : relativeTime(item.created_at);
            var profileImageUrl = (settings.queryType === 'query') ? item.profile_image_url : item.user.profile_image_url;
            var screenName = (settings.queryType === 'query') ? item.from_user : item.user.screen_name;
            
            var itemHtml = template(settings.template, {
                screenName : screenName,
                avatarSize : settings.avatarSize,
                profileImageUrl : profileImageUrl,
                createdAt : createdAt,
                id : item.id_str,
                text : formatText(item.text),
                encodedText : encodeURIComponent(item.text)
            });
                
            $('<li></li>').attr('id', 'li' + item.id_str).addClass('clearfix').html(itemHtml).appendTo('#ul' + settings.lastId);

            if (!settings.firstRun) {
                $('#li' + item.id_str).hide().fadeIn(settings.fadeSpeed);
            }
        });
        
        if ($('.' + settings.containerClass + ' li').length > settings.count) {
            $('.' + settings.containerClass + ' li').each(function(i) {
                if (i >= settings.count) {
                    $(this).fadeOut(settings.fadeSpeed);
                }
            });
        }
        
        $('.' + settings.containerClass + ' li a').attr('target', '_new');

        settings.firstRun = false;
        if (settings.queryType === 'query') {
            settings.refreshUrl = json.refresh_url;
        } else if (settings.queryType === 'user') {
            settings.refreshUrl = '?screen_name=' + settings.query + '&since_id=' + settings.lastId + '&count=' + settings.count;
        } else if (settings.queryType === 'userlist') {
            settings.refreshUrl = '?since_id=' + settings.lastId + '&rpp=' + settings.count;
        }
    }

    
    function getUrl() {
        if (settings.jsonData) return settings.jsonData;
        
        var url = '';
        if (settings.queryType === 'query') {
            url = 'http://search.twitter.com/search.json';
        } else if (settings.queryType === 'user') {
            url = 'http://api.twitter.com/1/statuses/user_timeline.json';
        } else if (settings.queryType === 'userlist') {
            url = 'http://api.twitter.com/1/' + settings.query + '/lists/' + settings.listName + '/statuses.json';
        }
        
        if (settings.refreshUrl) {
            url += settings.refreshUrl;
        } else if (settings.queryType === 'user') {
            url += '?screen_name=' + settings.query;
        } else if (settings.queryType === 'query') {
            url += '?q=' + encodeURIComponent(settings.query) + '&rpp=' + settings.count;
        }
        return url;
    }

    
    function formatText(txt) {
        var exp = /(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
        txt = txt.replace(exp, '<a href="$1" class="external">$1</a>');
        exp = /[\@]+([A-Za-z0-9-_]+)/ig;
        txt = txt.replace(exp, '<a href="http://twitter.com/$1" class="profile">@$1</a>');
        exp = /[\#]+([A-Za-z0-9-_]+)/ig;
        
        return txt;
    }
    
    function relativeTime(dateStr) {
        if ($.browser.msie) {
            dateStr = dateStr.replace(' +0000', '') + ' UTC';
        }
        var dt = Date.parse(dateStr);
        var relativeTo = (arguments.length > 1) ? arguments[1] : new Date();
        var delta = parseInt((relativeTo.getTime() - dt) / 1000);

        if (delta < 60) {
            return 'less than a minute ago';
        } else if (delta < 120) {
            return 'about a minute ago';
        } else if (delta < (55*60)) {
            return (parseInt(delta / 60)).toString() + ' minutes ago';
        } else if (delta < (1.6*60*60)) {
            return 'about an hour ago';
        } else if (delta < (24*60*60)) {
            return 'about ' + (Math.round(delta / 3600)).toString() + ' hours ago';
        } else if (delta < (48*60*60)) {
            return '1 day ago';
        } else {
            return (parseInt(delta / 86400)).toString() + ' days ago';
        }
    }
    
    function localTime(dateStr) {
        if ($.browser.msie) {
            dateStr = dateStr.replace(' +0000', '') + ' UTC';
        }
        var dt = new Date(Date.parse(dateStr));
        return strftime('%B %d, %Y %l:%M %p', dt);
    }
    
    function template(string, values) {
        var pattern = /(^|.|\r|\n)(#\{(.*?)\})/;
        while(match = string.match(pattern)) {
            string = string.replace(new RegExp(match[2], 'g'), values[match[3]]);
        }
        return string;
    }

    function strftime(format, date, locale) {
        if(! date ) date = new Date(); // default to today
        if(! locale) locale = 'en';
        var A_values, B_values;
        if( locale == 'en' ) {
            A_values = ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'];
            B_values = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 
                        'September', 'October', 'November', 'December'];
        } else if( locale == 'es' ) {
            A_values = ['Domingo','Lunes','Martes','MiÃ©rcoles','Jueves','Viernes','SÃ¡bado'],
            B_values = ['Enero', 'Febrero', 'Marzo', 'Abril', 'Mayo', 'Junio', 'Julio', 'Agosto',
                        'Septiembre', 'Octubre', 'Noviembre', 'Diciembre'];
        }

        var pattern = /%(a|A|b|B|d|m|M|H|l|p|P|y|Y)/;
        while(match = format.match(pattern)) {
            var repl;
            switch (match[1]) {
                case 'a' : repl = A_values[date.getDay()].substr(0,3); break;
                case 'A' : repl = A_values[date.getDay()]; break;
                case 'b' : repl = B_values[date.getMonth()].substr(0,3); break;
                case 'B' : repl = B_values[date.getMonth()]; break;
                case 'd' : repl = date.getDate(); break;
                case 'H' : repl = date.getHours(); break;
                case 'l' : repl = date.getHours(); if( repl > 12 ) repl = repl % 12; break;
                case 'm' : repl = date.getMonth() + 1; break;
                case 'M' : repl = date.getMinutes(); if( repl < 10 ) repl = '0' + repl; break;
                case 'p' : repl = (date.getHours() >= 12) ? 'pm' : 'am'; break;
                case 'P' : repl = (date.getHours() >= 12) ? 'PM' : 'AM'; break;
                case 'y' : repl = (date.getFullYear() + '').substr(2); break;
                case 'Y' : repl = date.getFullYear(); break;
            }
            format = format.replace(RegExp(match[0], 'g'), repl);
        }
        return format;
    }
    
})(jQuery);


