(function($) {
  
  $.AnchorageClass = function () {
    this.initialize(); 
  }
  
  var AnchorageError = function(msg) {
    $('#anchorage-error').remove();
    $('body').append('<div id="anchorage-error"><span>'+msg+'</span><a href="#" id="anchorage-error-close">close</a></div>');
    
    $('#anchorage-error span').
      css({
        'display'   : 'inline-block',
        'margin'    : '10px',
        'width'     : '75%',
        'visibility': 'hidden'
      });
    
    $('#anchorage-error-close').
      click(function() {
        $(this).parent().fadeOut(function() {
          $(this).remove();
        });
        return false;
      }).
      css({
        'float'   : 'right',
        'margin'  : '10px 10px'
      }).
      hide();
      
    $('#anchorage-error').
      css({
        'position'        : 'absolute',
        'top'             : window.pageYOffset,
        'left'            : 0,
        'right'           : 0,
        'background-color': '#ddd',
        'margin'          : '0px',
        'text-align'      : 'center'
      }).
      hide().
      slideDown('slow', function() {
        $('#anchorage-error span').hide().css('visibility', 'visible').fadeIn();
        $('#anchorage-error-close').fadeIn();
      });
  }
  
  $.anchorage = function(opts) {
    var baseurl = window.location.href.match(/^[a-z]+:\/+[\w:.-]+\//);
    var defaults = {
      baseurl : baseurl,
      matcher : "",
      fallback: false
    };
    options = $.extend(defaults, $.anchorage.defaults, opts || {});
    
    $.Anchorage = $.Anchorage || new $.AnchorageClass();
    return this;
  }
  
  $.anchorage.defaults = {
    // TODO provide the rest of the options/methods for ajax for full customization
    ajax_options: {
      async     : true,
      cache     : true,
      data_type : 'script'
    },
    ajax_methods: {
      success   : function(data, textStatus, XMLHttpRequest) {
        eval(data);
      },
      error     : function(XMLHttpRequest, textStatus, errorThrown) {
        AnchorageError(errorThrown);
      },
      complete  : function(XMLHttpRequest, textStatus) {},
      beforeSend: function(XMLHttpRequest) {
        $('#anchorage-error').fadeOut().remove();
      }
    }
  };
  
  $.extend($.AnchorageClass.prototype, {
    initialize: function() {
      anchorage = this; // TODO do i have to do this...? Whish i knew more jquery...
      
      // some default stuff we want to always happen
      // TODO dynamic binding so multiple instance of anchorage can be set
      $('body').
        bind('ajaxSend.Anchorage', function(event, XMLHttpRequest, ajaxOption) {
          if (ajaxOption.url == null || ajaxOption.url == "") {
            XMLHttpRequest.abort();
          }
        }).
        bind('ajaxError.Anchorage', function() {
          if (options.fallback) {
            window.location.href = anchorage.location();
          }
        }).
        bind('ajaxSuccess.Anchorage', function() {
          anchorage.binder();
        });
      
      anchorage.binder();
      
      // process incoming results. all anchorage requests will be #/... 
      // any other request with a #, will most likely be an actual a name call
      if (window.location.href.match(/#\/.+/)) {
        anchorage.request();
      }
    }, 
    
    binder: function() {
      // because this will always be used with a tags, or at least for the time being
      $.each($("a"+options.matcher), function(i, a) {
        var href = $(a).attr('href');
        
        // TODO make / work, it breaks when called on the very first visit, 
        // it can't seem to recall anchorage.binder()
        // it works after other calls are made
        // TODO enable passing of different protocols that might not be 
        // browser driven
        if (typeof href === "undefined" || href == "/" || href.match(/^#/) || href.match(/^mailto|ftp/)) {
          // TODO how to handle actual anchor tags
          // changing window location could be tricky as well, we just want to
          // scroll, but that should be given as a bookmarkable option as well.
          //
        } else {
          // match for those that do not have a protocol or use our domain as the base for the url
          //
          // Matches:
          //
          //  http://currentsitedomain.com/path/to/request
          //  /path/to/request
          //  relative/request
          //  ../relative/up
          //
          if (!href.match(/^[\w]+:\/+/) || href.match(options.baseurl)) {
            // unbind any previous bindings related to Anchorage
            $(a).unbind('click.Anchorage');
          
            // strip off any protocols and baseurls if needed against this.href
            // we want to allow for relative urls to grab the full path
            //
            //  http://example.com/path/to/something  # => path/to/something
            //  /path/to/something                    # => path/to/something
            //
            // For relative routes we want to have the full path to this relative location parsed
            // out, that is why we parse this.href to make our final href
            //
            // base: http://example.com/path/of/where/i/am
            //
            //  relative/to/something # => path/of/where/i/am/relative/to/something
            //
            // All relative paths are relative to the path, and not the file itself. 
            // 
            // base: http://example.com/path/of/where/i/am.php
            //
            //  relative/to/something # will not => path/of/where/i/am.php/relative/to/something
            //
            // if you need extension as well your going to need to set your href as a querystring
            //
            //  ?i=have&params=true # => path/of/where/i/am.php?i=have&params=true
            //
            // TODO allow force extension options to allow for path/of/where/i/am.php/relative/to/something
            href = this.href.
              replace(options.baseurl, ""). // remove the base domain protocol ala our options
              replace(/^[a-z]+:\/+/, "");   // just in case remove protocols up to :///, to resolve issues when testing this locally with file:///
          
            // physically replace the href values; as well as making it absolute
            $(a).attr("href", "/"+href).
              bind('click.Anchorage', anchorage.click); // a little s&m
          }
        }
      });
    },
    
    click: function(evt) {
      anchorage.location($(this)).
        request();
      return false; // disable the clickity
    },
    
    request: function() {
      var url = anchorage.location();
      
      $.ajax({
        context   : $('body'),
        url       : anchorage.remove_anchor(url), // remove any actual anchor options if available
        async     : options.ajax_options.async,
        cache     : options.ajax_options.cache,
        dataType  : options.ajax_options.data_type,
        success   : options.ajax_methods.success,
        error     : options.ajax_methods.error,
        complete  : options.ajax_methods.complete,
        beforeSend: options.ajax_methods.beforeSend
      });
    },
    
    // TODO force prefix of domain option
    // base: http://example.com/some/path/in/
    //
    // Anchorage currently produces
    //
    //  http://example.com/some/path/in/#/anchorage/url/
    //
    // forcing prefix would result in
    //
    //  http://example.com/#/anchorage/url/
    //
    // ** That may cause some issues with relative path options...?
    // TODO distinguish between absolute and relative paths
    location: function(href) {
      if (href) {
        anchorage_href = (typeof href === 'string') ? "#"+href : "#"+href.attr("href");
        window.location.href = anchorage_href;
        return this; // for chaining
      } else {
        var href = window.location.href.match(/#\/.*/) ? window.location.href.replace(/.+#\//, '/') : false;
        return href;
      }
    },
    
    // either a parsable url us supplied or it's false, we don't want replace to get hung up
    remove_anchor: function(url) { return !url ? url : url.replace(/#[^\/].*$/, ''); }
  });
  
})(jQuery);