// Written by Chris Powers - http://containerdiv.wordpress.com/
// Adapted from an article by Chris Coyier, written at CSS-Tricks.com - http://css-tricks.com/moving-boxes/

(function(jQuery) {
  $.extend($.fn, {
    panelSlider: function(options) {
      this.each(function() {
        var ps = new PanelSlider(this, options);
        $(this).data('slider', ps);
      });
    }
  });
  
  function PanelSlider(obj, opts) {
    this.initialize(obj, opts);
  }
  
  PanelSlider.prototype = {
    initialize: function(obj, opts) {
      this.initOptions(opts);
      this.initPanels(obj);
      
      this.calculateOriginalStyle();
      this.addSliderStyles();
            
      if(this.prevButton) this.initPrevButton();
      if(this.nextButton) this.initNextButton();
      if(this.useKeys) this.initKeys();

      this.currentlyMoving = false;
      this.selectPanel($(this.panels[0]));
    },
    
    initOptions: function(opts) {
      if(opts == null) opts = {};

      var options = {
        panelSelector : 'div',
        // Can be CSS selector of existing element, a reference to 
        // an element or null. By default creates
        // its own button and prepends to the slider object
        prevButton     : '.slider_prev',
        // Can be CSS selector of existing element, a reference to 
        // an element or null. By default creates
        // its own button and prepends to the slider object
        nextButton     : '.slider_next',
        onSelect       : null,
        onEndSelect    : null,
        onDeselect     : null,
        onEndDeselect  : null,
        movingDistance : null,
        panelStart     : 0,
        vertical       : false, // defaults to horizontal orientation
        selectStyle    : {},
        itemClickNav   : true,
        useKeys        : false
      };
      
      $.extend(options, opts);
      
      // mass assign options
      for (var method in options) {
        this[method] = options[method];
      }
    },
    
    initPanels: function(obj) {
      this.slider = $(obj);
      this.slider.wrapInner("<div class='scrollContainer'></div>");
      this.container = this.slider.find('.scrollContainer');
      this.panels = this.container.find(this.panelSelector);
      this.panels.wrapInner("<div class='inside'></div>");
    },
    
    calculateOriginalStyle: function() {
      var panel = $(this.panels[0]);
      this.originalStyle = {};

      // Build originalStyle hash from current css of unselected panel
      for(var selector in this.selectStyle) {
        var styles = this.selectStyle[selector];
        var node = selector == 'this' ? panel : panel.find(selector);
        this.originalStyle[selector] = {};
        for(var attribute in styles) {
          this.originalStyle[selector][attribute] = node.css(attribute);
        }
      }
    },
    
    addSliderStyles: function() {
      if(this.vertical) {
        this.panels.css({'display' : 'block','position' : 'relative'});
        this.container
          .css('height', (this.panels[0].offsetHeight * this.panels.length) + 100 )
          .css('top', (this.panelStart) + "px")
          .css('position', 'relative');      
      } else {
        this.panels.css({'float' : 'left','position' : 'relative'});
        this.container
          .css('left', (this.panelStart) + "px")
      }
    },
    
    initNextButton: function() {
      // if CSS selector string, get the collection
      if(this.nextButton == this.nextButton + '') {
        this.nextButton = $(this.nextButton);
      }
      if(this.nextButton[0] == null) {
        this.nextButton = $("<div class='scrollButtons slider_next'></div>");
        this.slider.prepend(this.nextButton);
      }
      
      var _this = this;
      this.nextButton.click(function(){ _this.change(true); return false; }); 
    },
    
    initPrevButton: function() {
      if(this.prevButton == null) return;
      
      // if CSS selector string, get the collection
      if(this.prevButton == this.prevButton + '') {
        this.prevButton = $(this.prevButton);
      }
      if(this.prevButton[0] == null) {
        this.prevButton = $("<div class='scrollButtons slider_prev'></div>");
        this.slider.prepend(this.prevButton);
      }

      var _this = this;
      this.prevButton.click(function(){ _this.change(false); return false; }); 
    },
    
    initKeys: function() {
      var _this = this;
      $(window).keydown(function(event){
        switch (event.keyCode) {
          case 13: //enter
            _this.change(true);
            break;
          case 32: //space
            _this.change(true);
            break;
          case 37: //left arrow
            _this.change(false);
            break;
          case 38: //up arrow
            _this.change(false);
            break;
          case 39: //right arrow
            _this.change(true);
            break;
          case 40: //down arrow
            _this.change(true);
            break;
        }
      });
    },
    
    selectPanel: function(element) {
      if(this.curPanel) this.deselectPanel(this.curPanel);
      this.curPanel = element;
      if(this.itemClickNav) this.updatePanelBindings();
      this.updateArrowButtons();
      
      if(this.onSelect) {
        var result = this.onSelect(element);
        if(result != false) this.doSelect(element);
      }
      else { this.doSelect(element); }
    },
    
    deselectPanel: function(element) {
      if(this.onDeselect) {
        result = this.onDeselect(element);
        if(result != false) this.doDeselect(element);
      }
      else { this.doDeselect(element); }
    },
    
    updatePanelBindings: function() {
      var _this = this;
      this.curPanel.next(this.panelSelector)
                   .unbind()
                   .click(function() { 
                     _this.change(true); 
                     return false; 
                   });
      this.curPanel.unbind();
      this.curPanel.prev(this.panelSelector)
                   .unbind()
                   .click(function() { 
                     _this.change(false); 
                     return false; 
                   });
    },
    
    updateArrowButtons: function() {
      if(this.prevButton) {
        if(this.curPanel[0] == this.panels[0]) {
          $(this.prevButton).addClass('disabled');
        } else {
          $(this.prevButton).removeClass('disabled');
        }
      }
      if(this.nextButton) {
        if(this.curPanel[0] == this.panels[this.panels.length - 1]) {
          $(this.nextButton).addClass('disabled');
        } else {
          $(this.nextButton).removeClass('disabled');
        }
      }
    },
    
    animatePanels: function(direction, next) {
      var _this = this;
      var startValue    = this.container.css(this.vertical ? "top" : "left");
      var movement   = direction ? parseFloat(startValue, 10) - this.movingDistance : parseFloat(startValue, 10) + this.movingDistance;

      this.currentlyMoving = true;      
      animate_hash = this.vertical ? {'top': movement} : {'left': movement};
      this.container
        .stop()
        .animate(animate_hash, function() {
          _this.currentlyMoving = false;
          if(_this.onEndSelect) _this.onEndSelect(_this.curPanel);
          if(_this.onEndDeselect) _this.onEndDeselect(next);
        });
    },
    
    doSelect: function(element) {
      this.doTransform(element, this.selectStyle)
    },

    doDeselect: function(element) {
      this.doTransform(element, this.originalStyle);
    },
        
    doTransform: function(element, styleObj) {
      if(styleObj == {} || styleObj == null) return;
      element = $(element);
      for(var selector in styleObj) {
        var target = selector == 'this' ? element : element.find(selector);
        var styles = styleObj[selector]
        var animate_styles = {};
        var css_styles = {};
        for(var attribute in styles) {
          if(jQuery.inArray(attribute, ['width', 'height', 'font-size']) > -1) {
            var val = styles[attribute];
            if(attribute == 'font-size') attribute = 'fontSize';
            animate_styles[attribute] = val;
          } else {
            css_styles[attribute] = styles[attribute];
          }
        }
        if(animate_styles != {}) target.animate(animate_styles);
        if(css_styles != {}) target.css(css_styles);
      }
    },
  
    //direction true = next(right/down), false = prev(left/up)
    change: function(direction) {
      if(this.currentlyMoving) return;

      var next = direction ? this.curPanel.next(this.panelSelector) : this.curPanel.prev(this.panelSelector);
      // if next doesn't exist, don't do anything
      if(!next[0]) return;
      
      this.animatePanels(direction, next);
      this.selectPanel(next);
      
      if(this.onSlide) this.onSlide(this.curPanel);
    }
  };
})(jQuery);
