/* 
A scrolling filmstrip navigator. Organize the elements like so:

[div filmstrip] - the window itself
 [div noop] - the filmstrip that will be scrolled
   [slot]
   [slot]
   [slot]
 [/div noop]
[/div filmstrip]

Afterwards feed the reference to the filmstrip div to the constructor.
*/
Filmstrip = Class.create();
Filmstrip.prototype = {
  filmstrip: false,
  container: false,
  
  // The width of the scrolling element
  containerSize: 0,
  
  // The width of the window itself
  insideSize: 0,
  
  // If there is some space needed by which the filmstrip can be inset
  allowInflow: 40,
  
  // Nothing will work until this is set to true
  initalized: false,
  
  // The element to which the "current" callback will be attached
  slotTag: "a",
  
  // Reference to the button to scroll left
  buttonLeft: false,
  
  // Reference to the button to scroll right
  buttonRight: false,
  
  initialize: function(filmstripContainer) {
    // First refuse to mobiles
    var mobiles = ['Nokia', 'Ericsson', 'iPhone'];
    mobiles.map(function(e){
      if(window.navigator.userAgent.indexOf(e) > -1) {
        throw "I do not work on mobiles";
      }
    });
    
    this.filmstrip = $(filmstripContainer);
    this.container = this.filmstrip.getElementsByTagName('div')[0];
    
    // Because the code needs to calculate geometry of the page.
    // if we try to access the offsets before the load is done we will get a violent FUOC
    // - thus we delay
    // We know that the widths are known beforehand, so we can initialize right away
    this.initFilmstrip();
  },
  
  _expDistance: function() {
    if (!this._movePerUnit) this._movePerUnit = 9;
    this._movePerUnit += 1;
    return this._movePerUnit;
  },
  
  initFilmstrip: function() {
    // Scan all the elements within the noop
    this._computeFilmstripDimension();
    this.initialized = true;
    
    // If the icons sticks out for more than 5 pixels we have to add scrollers
    if((this.containerSize - this.insideSize) > -5) {
      this.container.style.left = '0';
      this.allowInflow = 0;
      this.setShift(0);
    } else {
      var me = this;

      // Inject the left and right buttons
      new Insertion.Before(this.container, '<a href="#" class="page" id="prevClips">Prev</a>');
      new Insertion.After(this.container, '<a href="#" class="page" id="nextClips">Next</a>');
      this.buttonLeft = document.getElementById("prevClips");
      this.buttonRight = document.getElementById("nextClips");

      // When the user hovers over these buttons, the filmstrip will scroll left or right
      // accelerating progressively until the last or the first icon is in the middle
      // of the strip.
      // We use a timeout, because people with Wacom tables who park the cursor just on the
      // outside of the button can experience pointer jitter, and scrolling will be starting
      // and stopping at random
      
      // this is a functor by the way - ain't that cool?
      var generateCallback = function(scrollMethod) {
        return function() {
          if(me.intervalId) window.clearInterval(me.intervalId);
          // Plant a timeout of one third of a second
          if(me.timeoutId) window.clearTimeout(me.timeoutId);
          
          var scrollingCallback = function() {
            me.intervalId = window.setInterval(function() { me[scrollMethod](me._expDistance()) }, 50);
          };
          me.timeoutId = window.setTimeout(scrollingCallback, 150);
        };
      };
      
      this.buttonLeft.onmouseover = generateCallback('scrollForward');
      this.buttonRight.onmouseover = generateCallback('scrollBackward');
      
      this.buttonLeft.onmouseout = this.buttonRight.onmouseout = function(){ 
        if(me.timeoutId) window.clearTimeout(me.timeoutId);
        window.clearInterval(me.intervalId); me._movePerUnit = false;
      };
      this.scrollToCurrent();
    }
  },
  
  scrollToCurrent: function(additionalEffectOptions) {
    if(this.containerSize > (this.insideSize + 5)) { 
      if (additionalEffectOptions.afterFinish) additionalEffectOptions.afterFinish();
      return;
    }
    
    if (!additionalEffectOptions) additionalEffectOptions = {};
    
    // Detect the current element
    var curElement = this.findCurrent();
    
    // shift the filmstrip to center on the element
    var off = curElement.offsetLeft - (this.getShift() * -1);
    var curDimension = this._getDimension(curElement);
    
    // Sweet spot (centerpoint) is all elements before me (since they are variable width) plus half of my own width
    var centre = (this.containerSize / 2) - (curDimension/2);
    
    // Now truncate the moveBy value so that the filmstrip does not scroll out of view
    var moveBy = (off - centre) * -1;
    moveBy = this.clampShift(moveBy);
    
    var absOff = this.getShift() + moveBy;
    var effectOpts = $H(this.getEffectOptions(absOff)).merge(additionalEffectOptions).toObject();
    
    new Effect.Move(this.container, effectOpts);
  },
  
  getEffectOptions: function(absOff) {
    return {x: absOff, y: 0, mode: 'absolute'};
  },
  
  getShiftOfIcon: function(ico) {
    return parseInt(ico.offsetLeft);
  },
  
  findCurrent: function() {
    var others = this.container.getElementsByTagName(this.slotTag);
    for(var i = 0; i < others.length; i++) {
      if ((others[i]).hasClassName("current")) {
        return others[i];
      }
    }
  },
  
  getAfterHiliteFor: function(anchorElement) {
    return function(){};
  },
  
  handleFocusOnIcon: function(evt, anchorElement) {
    Event.stop(evt);
    afterMove = this.getAfterHiliteFor(anchorElement);
    this.findCurrent().removeClassName('current');
    anchorElement.addClassName("current");
    this.scrollToCurrent({afterFinish: afterMove});
  },
  
  _getDimension: function(e) {
    try {
      return Element.getDimensions(e).width;
    } catch(ex) {
      return 0;
    }
  },
  
  _computeFilmstripDimension: function() {
    var coll  = this.container.getElementsByTagName(this.slotTag);
    var cum = 0;
    for (var i = 0; i < coll.length; i++) {
      cum += this._getDimension(coll[i]);
      var curAnchor = coll[i];
      Event.observe(curAnchor, "click", this.handleFocusOnIcon.bindAsEventListener(this, curAnchor));
    }
    this.containerSize = this._getDimension(this.filmstrip);
    this.insideSize = cum;
    this.setContainerSize(cum);
  },
  
  setContainerSize: function(to) {
    /* Firefox rounds to the lower value, so we 5 pix extra */
    this.container.style.width = (to + 5) + 'px';
  },
  
  clampShift: function(pix) {
    var cur = this.getShift(); 
    // How far to the left we are allowed to scroll? This is a bit complicated
    var minLeft =  (this.insideSize + this.allowInflow - this.containerSize) * -1;
    
    // How far to the right we are allowed to scroll? To the (size of filmstrip - inside Width - size of our handle).
    var maxLeft = this.allowInflow;
    var clampedShift = pix;
    
    if ((cur + pix) < minLeft) {
      // Prevent the filmstrip from going too far right
      clampedShift = minLeft - cur;
    } else if ((cur+pix) > maxLeft) {
      // Prevent the filmstrip from going too far right
      clampedShift = maxLeft - cur;
    } else {
      // pix is correct and allowed
    }
    
    return clampedShift;
  },
  
  scrollBy: function(pix) {
    this.setShift(this.getShift() + this.clampShift(pix));
  },
  
  scrollBackward: function(pix) {
    return this.scrollBy(pix * -1);
  },

  scrollForward: function(pix) {
    return this.scrollBy(pix);
  },
  
  setShift: function(intPix) {
    this.container.style.left = intPix + "px";
  },
  
  getShift: function() {
    var p = parseInt(this.container.style.left);
    
    if (isNaN(p)) {
      return 0;
    } else return p;
  }
}

HEFilmstrip = Class.create(Filmstrip, {
  getAfterHiliteFor: function(anchorElement) {
    var url = '/clip-segment/' + anchorElement.href.match(/(\d+)$/)[0];
    var afterMove = function() {
      if (window.pageTracker) {
        window.pageTracker._trackPageview(url);
      }
      new Ajax.Updater( {success: 'two'},  url, 
         {
           method: 'get',
           onFailure: alert,
           evalScripts: true
         });
    };
    return afterMove;
  }
});

VerticalFilmstrip = Class.create(Filmstrip, {
  getShift: function() {
    var p = parseInt(this.container.style.top);
    if (isNaN(p)) {
      return 0;
    } else return p;
  },
  
  setShift: function(intPix) {
    this.container.style.top = intPix + "px";
  },
  
  getAfterHiliteFor: function(anchorElement) {
    var afterMove = function() {
      window.location = anchorElement.href;
    };
    return afterMove;
  },
  
  getEffectOptions: function(absOffset) {
    return $H({x: 0, y: absOffset, mode: 'absolute', transition: Effect.Transitions.spring});
  },
  
  _getDimension: function(e) {
    try {
      return Element.getDimensions(e).height;
    } catch(ex) {
      return 0;
    }
  },
  
  getShiftOfIcon: function(icon) {
    return icon.offsetTop;
  },
  
  setContainerSize: function(to) {
    /* Firefox rounds to the lower value, so we 5 pix extra */
    this.container.style.height = (to + 5) + 'px';
  }
  
});
