var o = jQuery.fn.videoselector = function(settings) 
{
  /*
    Save the player in settings for later,
    there's only one per page so just grab it
  */
  settings = jQuery.extend(
  {
    player: jQuery("#video .article").get(0)
  }
  , settings);
  return this.each(
    function()
    {
      var el = this;
      /*
        A few settings for later use:
        listing = <ul> of 'video items'
        items = actual items (the <li> 's
        videoLinks = any link within the actual items, except if it is the profile link (rel=me)
        pageList = the <ul> that holds the page numbers
        sortBar = the <p> that holds the sorting text and link
      */
      el.cfg = {
        listing:    jQuery(".listing", this),
        items:      jQuery(".listing>li", this),
        videoLinks: jQuery(".listing li a[rel!=me]", this),
        pageList:   jQuery(".pager ul", this),
        sortBar:    jQuery(".sorter", this)
      };
      /*
        Add functionality to al video items (the <li> 's)
        1. Retrieve all the info for each item and save them on 'this' for easy retrieval later on.
        2. Define a method to easily update each item when I receive any json (this.update)
        
      */
      el.cfg.items.each(
        function()
        {
          /*
            A few settings for later use:
            listing = <ul> of 'video items'
            items = actual items (the <li> 's
            pageList = the <ul> that holds the page numbers
            sortBar = the <p> that holds the sorting text and link
          */
          var el = this;
          el.cfg = {
            links:                  jQuery("a[rel!=me]", this),
            img:                    jQuery("img", this),
            date:                   jQuery("div.time", this),
            author:                 jQuery("div.author a", this),
            title:                  jQuery("div.title, div.video-diary", this),
            titleLink:              jQuery("div.title a, div.video-diary a", this),
            description:            jQuery("p", this),
            highquality:            jQuery("li.highquality a", this),
            lowquality:             jQuery("li.lowquality a", this),
            transcript:             jQuery("a[rel=alternate]", this),
            alternateTranscript:    jQuery("a[rel=alternate-transcript]", this),
            bookmark:               jQuery("a[rel=bookmark]", this),
            profile:                jQuery("a[rel=me]", this),
            comment:                jQuery("a[rel=comment]", this),
            castLinks:              jQuery(".cast-links", this),
            topicLink:              jQuery(".topic-link",this)
          };
          
          this.videoTitle             = el.cfg.titleLink.text();
          this.author                 = el.cfg.author.text();
          this.date                   = el.cfg.date.text();
          this.profileHref            = el.cfg.profile.attr("href");
                                      
          this.bookmark               = el.cfg.bookmark.attr("href");
          this.transcript             = el.cfg.transcript.attr("href");
          this.comment               = el.cfg.comment.attr("href");
          
          this.alternateTranscript    = el.cfg.alternateTranscript.attr("href");
          this.highquality            = el.cfg.highquality.attr("href");
          this.lowquality             = el.cfg.lowquality.attr("href");
          this.description            = el.cfg.description.text();
          this.castLinks              = jQuery("li a", el.cfg.castLinks);
          this.topicLink              = el.cfg.topicLink.attr('href');
          
          /*
            This is used to update the data of the item. The
            argument passed in is an object from json.
            It is basically just reseting any tect and hrefs in
            each item, these include the description, video urls,
            transcript urls, bookmarks etc etc
          */
          this.update = function(item)
          {
                        var j = jQuery(this);
                        if(item == null) {
                         j.css("display", "none");
                         return;
                        }

                        j.css("display", "block");
                        this.videoTitle = item.title;
                        this.date = item.date;
                        this.description = item.description;
                        this.bookmark = item.bookmark;
                        this.transcript = item.transcript;
                        this.comment = item.comment;
                        this.alternateTranscript = item.alternateTranscript;
                        this.highquality = item.highquality;
                        this.lowquality = item.lowquality;

                        this.profileHref = item.profileHref;
                        this.topicLink = item.topic;

                        el.cfg.img.attr("src", item.img);
                        el.cfg.links.attr("href", item.href);
                        this.author = item.name;
                        
                        if(el.cfg.profile.length == 1)
                        {
                            el.cfg.profile.attr("href", this.profileHref);
                        }

                        el.cfg.author.text(this.author);

                        el.cfg.castLinks.remove();
                        if(item.links.length > 0) {
                         var str = '<ul class="cast-links">';
                         jQuery(item.links).each(
                             function()
                             {   
                                 str += '<li><a href="' + this.href + '" rel="' + this.rel + '">' + this.label + '</a></li>';
                             }
                         );
                         str += "</ul>"
                        }

                        el.cfg.description.after(str);
                        el.cfg.castLinks = jQuery(".cast-links", el);
                        this.castLinks = jQuery("li a", el.cfg.castLinks);
                        el.cfg.date.text(this.date);

                        if(item.isVideoDiary){
                            el.cfg.title.addClass("video-diary");
                        } else {
                            el.cfg.title.removeClass("video-diary");
                        }

                        el.cfg.titleLink.text(this.videoTitle);
                        el.cfg.description.text(this.description);
                        el.cfg.highquality.attr("href", item.highquality);
                        el.cfg.lowquality.attr("href", item.lowquality);
                        el.cfg.transcript.attr("href", item.transcript);
                        el.cfg.comment.attr("href", item.comment);
                        el.cfg.alternateTranscript.attr("href", item.alternateTranscript);
                        el.cfg.bookmark.attr("href", item.bookmark);
                   };
                    
        }
      );
      
      /*
        updateArticleLinks is called whenever a property of the video module is changed (page number, video selection, sort order)
        1. Collect the new property values from the href of the clicked items by parsing Rajs url scheme
        2. Change the links of any links marked rev="selector" by parsing out the article number and title, then merging in the new propertys, again following Rajs url scheme
      */
      var updateArticleLinks = function(href)
      {
        var videotype, video, page;
        
        var typeIndex = href.indexOf("videotype-");
        var videoIndex = href.indexOf("video-");
        var pageIndex = href.indexOf("page-");
        var orderIndex = href.indexOf("order-");

        videoType = href.substr(typeIndex);
        videoType = videoType.substr(0, videoType.indexOf("/"));
        
        video = href.substr(videoIndex);
        video = video.substr(0, video.indexOf("/"));
        
        page = href.substr(pageIndex);
        page = page.substr(0, page.indexOf("/"));
        
        order = href.substr(orderIndex);
        order = order.substr(0, order.indexOf("/"));
        
        jQuery("a[rev=selector]").each(
          function()
          {
            var j = jQuery(this);
            var href = j.attr("href");
            
            var articleIndex = href.indexOf("article-");
            article = href.substr(articleIndex);
            article = article.substr(0, article.indexOf("/"));
            
            var end = href.substr(href.lastIndexOf("/") + 1);
            var temp = new Array(article, videoType, video, page, order, end);
            href = temp.join("/");
            j.attr("href", href);
            
            
          }
        );
      };
      
      /*
        Add 'click' functionality to all links in the video list (apart from profile links).
        On click:
        1. Update any article links so they have the correct href so the video module maintains state when the page is refreshes
        2. Update the player, sending it the item (the <li>) which has all its data attached to it (see above)
      */
      
      el.cfg.videoLinks.each(
          function()
          {
            var el = this;
            var j = jQuery(el);
            j.bind(
              'click',
              function(event)
              {
                event.preventDefault();
                var item = jQuery(this).parent();
                if(item.get(0).tagName.toLowerCase() != "li") {
                  item = item.parent().parent();
                }
                updateArticleLinks(j.attr("href"));
                settings.player.update(item);
              }
            );
            
          }
      );
      /*
        updateListing is called whenever the entire list needs updating, ie whenever I ask for json (paging and sorting)
        1. Takes the href of the link that is clicked.
        2. Changes the extension from .html to .json
        3. Makes sure any query string is kept
        4. Asks for the json at the resulting href (something/something/something.json?querystring)
      */
      var updateListing = function(event)
      {
        event.preventDefault();
        var href = jQuery(this).attr("href");
        var base = jQuery("base").attr("href");
        if(base != null) {
          if(href.indexOf("http://") != 0) {
            href = base + href;
          }
        }
        // process
        var temp = href.split("?");
        href = temp[0];
        var query = temp[1];
        var temp = href.split(".");
        temp.pop();
        href = temp.join(".");
        
        
        updateArticleLinks(href);
        
        
        href = href + ".json";
        if(query != null) {
          href += "?" + query;
        }
        
        
        
        /*
          Asks for the json and then updates the list using the data sent via json
          1. Updates the sorter bar.
          2. Calls update on each video item (<li>), see above for details (this.update)
          3. Updates the paging list
        */
        jQuery.getJSON(
          href,
          function(data)
          {
            
            var sortcontent = jQuery("span", el.cfg.sortBar);  
            sortcontent.empty();
            var str = "";
                        
            if(data.order[0].href == null)
            {
              str += "<strong>" + data.order[0].content + " </strong>";
              str += '<a href="' + data.order[1].href + '">' + data.order[1].content + '</a>';
            }
            else
            {
              str += "<ul>";
                str += '<li class="first"><a href="' + data.order[0].href + '">' + data.order[0].content + '</a></li>';
                str += '<li class="last"><a href="' + data.order[1].href + '">' + data.order[1].content + '</a></li>';
              str += "</ul>";
            }
            
            sortcontent.append(str);
            
            jQuery("a", sortcontent).bind(
              'click',
              updateListing
            );
            
            el.cfg.items.each(
              function(i)
              {
                this.update(data.items[i]);
              }
            );
            el.cfg.pageList.empty();
            var previousType = -1;
            jQuery(data.pages).each(
              function()
              {
                var PAGE = 0;
                var FIRST = 1;
                var PREVIOUS = 2;
                var NEXT = 3;
                var LAST = 4;
                var li = "<li";
                if(this.type == PAGE) {
                  li += ' class="page-number';
                  if(previousType != 0) {
                    li += " first-page";
                  }
                  if(this.current == true) {
                    li += " current";
                  }
                  li += '"';
                }
                li += '><a href="' + this.href + '"';
                if(this.type != PAGE) {
                  var title;
                  var rel;
                  switch(this.type) {
                    case FIRST:
                      title = "First Page";
                      rel = "start";
                      break;
                    case PREVIOUS:
                      title = "Previous Page Set";
                      rel = "previous-set";
                      break;
                    case NEXT:
                      title = "Next Page Set";
                      rel = "next-set";
                      break;
                    case LAST:
                      title = "Last Page";
                      rel = "last";
                      break;
                  }
                }
                li += ">" + this.content + "</a></li>";
                
                if(this.type != PAGE && previousType == PAGE) {
                  jQuery("li:last", el.cfg.pageList).addClass("last-page");
                }
                
                el.cfg.pageList.append(li);
                previousType = this.type;
              }
            );
            jQuery("a", el.cfg.pageList)
            .each(
              function()
              {
                jQuery(this).bind(
                  'click',
                  updateListing
                );
              }
            );
            
          }
        );
      };
      /*
        Add 'click' functionality to all links in the page list.
        On click:
        1. Call updateListing to request the json, which will in turn refresh the video listing, page list and the sort bar.
      */
      jQuery("a", el.cfg.pageList)
        .each(
          function()
          {
            jQuery(this).bind(
              'click',
              updateListing
            );
          }
      );
      /*
        Add 'click' functionality to the sort bar.
        On click:
        1. Call updateListing to request the json, which will in turn refresh the video listing, page list and the sort bar.
      */
      jQuery("a", el.cfg.sortBar).eq(0).bind(
        'click',
        updateListing
      );
    }
  );
};

