(function(global) {
  var s150 = global.s150 || (global.s150 = {}), $ = global.jQuery;
  
  var HAS_TRANSFORM_SUPPORT = (function() {
    var result = false, div = document.createElement('div');
    div.innerHTML = '<div style="-webkit-transform:translate(100px);-moz-transform:translate(100px);"></div>';
    result = (typeof div.firstChild.style.webkitTransform != 'undefined') || 
      (typeof div.firstChild.style.MozTransform != 'undefined');
    delete div;
    return result;
  })();  
  
  var HAS_TRANSITION_SUPPORT = (function() {
    var result = false, div = document.createElement('div');
    div.innerHTML = '<div style="-webkit-transition:color 1s linear;-moz-transition:color 1s linear;"></div>';
    result = (typeof div.firstChild.style.webkitTransition != 'undefined') || 
      (typeof div.firstChild.style.MozTransition != 'undefined');
    delete div;
    return result;
  })();
  
  var AUTOPLAY_TIMEOUT_MS = 5000;
  
  function buildPagination() {
    var output = [], i = 1, len = this.items.length;
    
    output.push('<div class="pagination">');
    output.push('<ul>');
    
    for (; i <= len; i++) {
      output.push('<li><a href="#" title="Page ' + i + '" data-index="' + (i - 1) + '">Page ' + i + '</a></li>');
    }
    
    output.push('</ul>');
    output.push('</div>');
    
    return output.join('');  
  }
  
  function setupContainer() {
    var shim, item;
    
    // make sure our container has the carousel class on it
    this.container.addClass('carousel');
    
    // append our content div into the container
    this.container.append((this.content = $('<div class="content"></div>')));
    this.content.append((this.slider = $('<div class="slider"></div>')));
    
    // insert our shim if needed
    if (!(shim = this.container.find('div.shim')).length) {
      this.slider.prepend('<div class="shim"><img src="' + this.options.shimImage + '" /></div>');      
    } else {
      this.slider.append(shim);
    }

    // move the items into the content div
    this.slider.append(this.items);
              
    // append our pagination controls
    this.container.append((this.pagination = $(buildPagination.call(this))));
    
    // finally, append the navigation links
    this.container.append('<a href="#" class="navigation previous" title="Previous">Previous</a>');
    this.container.append('<a href="#" class="navigation next" title="Next">Next</a>');
    
    // start the items with the first item
    this.items.each(function(i) {
      $(this).css({ left: (i * 100) + '%' });
    });
    
    // prevent flicker in on first translate move
    if (HAS_TRANSITION_SUPPORT && HAS_TRANSFORM_SUPPORT) {
      this.slider.css({
        '-webkit-transform': 'translate3d(0, 0, 0)',
        '-moz-transform': 'translate3d(0, 0, 0)'
      });    
    }
    
    // update our pagination to reflect our current state
    updatePagination.call(this);
  }
  
  function setupEvents() {
    this.pagination.delegate('a', 'click', $.proxy(handlePaginationClick, this));
    
    this.container
      .delegate('a.navigation', 'click', $.proxy(handleNavigationClick, this));
    
    var container = this.container[0];
    
    if (container.addEventListener) {   
      container.addEventListener('touchstart', $.proxy(handleTouchStart, this), false);
      container.addEventListener('touchend', $.proxy(handleTouchEnd, this), false);
    }
  }
  
  function updatePagination() {
    this.pagination.find('ul li')
      .removeClass('active')
      .eq(this.selectedIndex)
        .addClass('active');
  }
  
  function scroll(count) {
    var newIndex = this.selectedIndex + count, translate, direction;
    
    if (newIndex < 0 || newIndex >= this.items.length) {
      count = this.items.length - 1;
      
      if (newIndex >= this.items.length) {
        count = -count;
        newIndex = 0;
      } else {
        newIndex = count;
      }
    }  
    
    this.container.trigger('carousel.beforescroll', {carousel: this, index: newIndex, slide: this.items[newIndex] });
    
    if (HAS_TRANSITION_SUPPORT && HAS_TRANSFORM_SUPPORT) {
      translate = -(newIndex * 100) + '%';
      this.slider.css({
        '-webkit-transform': 'translate3d(' + translate + ', 0, 0)',
        '-moz-transform': 'translate3d(' + translate + ', 0, 0)'
      });    
    } else {
      direction = count > 0 ? '-' : '+';
      translate = 100 * count;
      this.slider.animate({ left: direction + '=' + Math.abs(translate) + '%' }, 800);
    }
    
    this.selectedIndex = newIndex;
    
    updatePagination.call(this);   
  }
  
  function scrollToIndex(index) {
    if (this.selectedIndex === index) {
      return;
    }
    
    scroll.call(this, index - this.selectedIndex);
  }
  
  function handleNavigationClick(e) {
    e.preventDefault();
    
    var target = $(e.target), 
      count = target.hasClass('previous') ? -1 : 1;

    stopAutoPlay.call(this);
    
    scroll.call(this, count);    
  }
  
  function handlePaginationClick(e) {
    e.preventDefault();
    
    var target = $(e.target), 
      index = target.attr('data-index') || 0;    
    
    stopAutoPlay.call(this);    
    
    scrollToIndex.call(this, index);
  }  

  function handleTouchStart(e) {
    stopAutoPlay.call(this);    
    this.touchStartX = e.touches.item(0).pageX;
  }
  
  function handleTouchEnd(e) {
    var touchDiff = this.touchStartX - e.changedTouches.item(0).pageX;
    
    if (Math.abs(touchDiff) > 120) {
      e.preventDefault();
      
      if (touchDiff < 0) {
        // swipe right
        scroll.call(this, -1);
      } else {
        // swipe left
        scroll.call(this, 1);
      }
    }  
  }
  
  function startAutoPlay() {
    if (this.timerId) {
      global.clearTimeout(this.timerId);
      this.timerId = null;
    }
        
    scroll.call(this, 1);
    
    if (this.options.autoPlay) {
      this.timerId = global.setTimeout($.proxy(startAutoPlay, this), AUTOPLAY_TIMEOUT_MS);
    }    
  }
  
  function stopAutoPlay() {
    if (this.timerId) {
      global.clearTimeout(this.timerId);
      this.timerId = null;
    }
  }

  function Carousel(container, options) {
    this.container = $(container);
    
    this.options = $.extend({
      shimImage: 'carousel_shim.gif',
      autoPlay: true
    }, options);
    
    this.items = this.container.children(':not(div.shim)');
    this.items.addClass('slide');
    
    this.selectedIndex = 0;

    setupContainer.call(this);
    setupEvents.call(this);
        
    if (HAS_TRANSITION_SUPPORT && HAS_TRANSFORM_SUPPORT) {
      this.slider.css({ 
        '-webkit-transition': '-webkit-transform .8s ease-in-out',
        '-moz-transition': '-moz-transform .8s ease-in-out'
      });
    }
    
    if (this.options.autoPlay) {
      this.timerId = global.setTimeout($.proxy(startAutoPlay, this), AUTOPLAY_TIMEOUT_MS);
    }
  }
  
  s150.Carousel = Carousel;  
})(this);
