/**
 * jQuery plugin for custom horizontal scroller.
 * Can be used on nodes structure like this:
 * <div class="scroller"><div class="leftArrow"/><div class="scroll"/><div class="rightArrow"/></div>
 * (classnames are locator examples and can be customized).
 * Proceeds scroll dragging and arrows pressing. Uses callback function, passing current scrollbar position
 * as percentage value into it. Also provides simple API.
 *
 * Usage example:
 *  var scrollbar = $.scrollbar($('.globalWrapper .navigationMenu .scroller'), {
 *    leftArrowLocator: '.leftArrow',
 *    scrollLocator: '.scroll',
 *    rightArrowLocator: '.rightArrow',
 *    onScrollMove: function (percentage) {
 *      self.$content.css('margin-left', $.px(-percentage * (self.$content.width() - $(window).width())));
 *    }
 *  });
 *
 * @author Grigory Bezyuk
 * @copyright Grigory Bezyuk (C)2009
 */

$.verticalScroller = function ($scrollbar, params) {

  // passing default values for params
  if (typeof(params) != 'object') {
    params = {};
  }

  if (params.topArrowLocator == undefined) {
    params.topArrowLocator = '.leftArrow';
  }
  if (params.scrollLocator == undefined) {
    params.scrollLocator = '.tracker';
  }
  if (params.bottomArrowLocator == undefined) {
    params.bottomArrowLocator = '.rightArrow';
  }
  if (params.scrollSpeed == undefined) {
    params.scrollSpeed = 0.02;    // percentage step for arrows scrolling
  }
  var scrollSpeed = params.scrollSpeed;

  if (params.scrollAcceleration == undefined) {
    params.scrollAcceleration = 0.002;    // percentage step for arrows scrolling
  }

  var scrollAcceleration = params.scrollAcceleration;

  // nodes in HTML/XHTML
  var $scroll       = $scrollbar.find(params.scrollLocator);
  var $topArrow    = $scrollbar.find(params.topArrowLocator);
  var $bottomArrow   = $scrollbar.find(params.bottomArrowLocator);

  // flags for scroll dragging and arrows scrolling
  var draggingState = false;
  var draggingPoint = 0;
  var scrollingDirection = 0; // -1 for left scrolling, +1 for right scrolling

  // binding action listeners

  $topArrow.mousedown(startTopScroll);
  $(document).mouseup(finishTopScroll);

  $bottomArrow.mousedown(startBottomScroll);
  $(document).mouseup(finishBottomScroll);

  $scroll.mousedown(startScrollDragging);
  $(document).mousemove(proceedScrollDragging);
  $(document).mouseup(finishScrollDragging);

  // starting timer for arrows scrolling.
  // Scrolling starts when mouse is pressed on arrow node and continues until it's released
  setTimeout(proceedScroll, 100);

  // functions for arrow scrolling
  function startTopScroll () {
    scrollingDirection = -1;
    return false;
  }

  function startBottomScroll () {
    scrollingDirection = 1;
    return false;
  }

  function finishTopScroll () {
    scrollingDirection = 0;
    scrollSpeed = params.scrollSpeed;
    scrollAcceleration = params.scrollAcceleration;
    return false;
  }

  function finishBottomScroll () {
    scrollingDirection = 0;
    scrollSpeed = params.scrollSpeed;
    scrollAcceleration = params.scrollAcceleration;
    return false;
  }

  function proceedScroll () {
    if (scrollingDirection == 0 || draggingState) {
      setTimeout(proceedScroll, 100);
      return;
    }
    var scrollPosition = getScrollPosition() + scrollSpeed * scrollingDirection;
    // scrollSpeed += scrollAcceleration;
    // scrollAcceleration *= 2;
    if (scrollPosition < 0 ) {
      scrollPosition = 0;
    }
    if (scrollPosition > 1 ) {
      scrollPosition = 1;
    }
    moveScrollToPosition(scrollPosition);
    setTimeout(proceedScroll, 100);
  }


  // functions for scroll dragging
  function startScrollDragging (e) {
    draggingState = true;
    draggingPoint = e.clientY - $scroll.offset().top;
    return false;
  }
  function finishScrollDragging (e) {
    draggingState = false;
    return false;
  }
  function proceedScrollDragging (e) {
    if (draggingState) {
      var newOffset = parseInt($scroll.css('top')) + e.clientY - $scroll.offset().top - draggingPoint;
      if (newOffset < $topArrow.height()) {
        newOffset = $topArrow.height();
      }
      if (newOffset > /*$bottomArrow.offset().top*/ $scrollbar.height() - $scroll.height() - $bottomArrow.height()) {
        newOffset = $scrollbar.height() - $scroll.height() - $bottomArrow.height();
      }
      var percentage = (newOffset - $topArrow.height()/* - $scrollbar.offset().top*/)
            / ($scrollbar.height() - $topArrow.height() - $bottomArrow.height() - $scroll.height());
      $scroll.css('top', newOffset);
      if (params.onScrollMove) {
        params.onScrollMove(percentage);
      }
    }
    return false;
  }

  function moveScrollToPosition (percentage, animate) {
    if (animate) {
      $scroll.animate({'top': percentageToPixels(percentage)});
    }
    else {
      setPosition(percentage);
    }
    if (params.onScrollMove) {
      params.onScrollMove(percentage);
    }
  }

  // Returns scroll position as percentage
  function getScrollPosition () {
    return pixelsToPercentage(-$scrollbar.offset().top + $scroll.offset().top);
  }

  // Sets scroll position as percentage. No callback function is being called.
  function setPosition (percentage) {
    $scroll.css('top', percentageToPixels(percentage));
  }

  function percentageToPixels (percentage) {
    return $topArrow.height() + percentage * ($scrollbar.height() - $scroll.height() - $topArrow.height() - $bottomArrow.height());
  }

  function pixelsToPercentage (pixels) {
    return (pixels - $topArrow.height())
            / ($scrollbar.height() - $scroll.height() - $topArrow.height() - $bottomArrow.height());
  }

  // Returning API
  return {
    setPosition: setPosition,
    percentageToPixels: percentageToPixels,
    pixelsToPercentage: pixelsToPercentage
  };
};
