/*


Main SlideShow Class:

EXAMPLE USAGE:

  new SlideShow('container', { slideDuration: 2 });

*/
var SlideShow = Class.create({
  
  initialize: function(element, options, navElement){
    this.element = element;
    this.navElement = navElement;
    this.suppliedOptions = options;
    this.defaultOptions = $H({
      autoPlay:           true,
      slideDuration:      3,
      transitionDuration: 1,
      loop:               true,
      crossFade:          false,
      pauseOnMouseover:   false,
      slidesSelector:     '> *',
      startHidden:        true,
      events: { init: 'dom:loaded', play: 'window:loaded' },
      beforeStart: Prototype.emptyFunction, afterFinish: Prototype.emptyFunction,
      slideBuilder: Prototype.emptyFunction
    });
    
    // assigning the options to internal variables
    this.options = this.defaultOptions.merge(this.suppliedOptions).each(function(option){
      this[option[0]] = option[1];
    }.bind(this));
    
    this.events = $H(this.defaultOptions.get('events')).merge(this.events).toObject();
    
    if (this.autoPlay) {
      this.initEventFunction = function(){
        this.init();
        // only allow the slideShow to observe one 'dom:loaded' event
        if (this.events.init == 'dom:loaded')
          document.stopObserving(this.events.init, this.initEventFunction);
      }.bind(this);
      document.observe(this.events.init, this.initEventFunction);
    }
  },
  
  init: function(){
    if (!$(this.element)) return;
    this.root = $(this.element);
    this.id = this.root.identify();
    this.fireEvent('initializing', { slideShow: this });
    if (this.slideJson) {
      this.slides = [];
    } else {
      this.slides = $$('#' + String(this.id) + ' ' + String(this.slidesSelector));
    }
    this.loopCount = 0;
    this.slideCount = 0;
    this.resumeTimer;
    this.transitionInProgress = false;
    this.slideIndex = 0;
    this.paused = false;
    this.started = false;
    
    this.prep();
    
    this.initNavigation();
  
    //Bind keylisteners to right arrow and left arrow
    Event.observe(window, 'keydown', function(e) {this.handleKeyInput(e)}.bindAsEventListener(this), false);
    
    this.playEventFunction = function(){
      this.beforeStart();
      //start slideshow with random slide
      var startSlide = 0; //Math.floor(Math.random()*this.slides.length)
      this.play(null, startSlide);
      if (this.pauseOnMouseover)
        this.root.observe('mouseover', this.pause.bind(this)).observe('mouseout', this.checkforMouseout.bind(this));
      
      // only let window:loaded start the slideShow once
      if (this.events.play == 'window:loaded')
        document.stopObserving(this.events.play, this.playEventFunction);
    }.bind(this);
    document.observe(this.events.play, this.playEventFunction);
    
    this.fireEvent('initialized', { slideShow: this });
  },
  
  handleKeyInput: function(e) {
    //switch slides according to keyboard input (left arrow, right arrow)
    if (e.keyCode == 37)
      this.previous();
    else if (e.keyCode == 39)
      this.next();
  },
    
  checkforMouseout : function(event) {
    //We could probably replace the following with Event.element(event), but oh well.
    var target = $(this.element);
    var mouse_over_element;  //What the mouse is currently over...
    //So let's check to see what the mouse is now over, and assign it to mouse_over_element...
    if( event.toElement ) {
      mouse_over_element = event.toElement;
    }
    else if(event.relatedTarget) {
      mouse_over_element = event.relatedTarget;
    }
    //In the event that the mouse is over something outside the DOM (like an alert window)...
    if(mouse_over_element == null) {
      return;
    }
    //Now we just make sure that what the mouse is currently over is NOT a descendant of
    //the dropdown, and that the target is not the current mouse_over_element (I can't
    //remember which case this covers, but it's important)
    if(!mouse_over_element.descendantOf(target) && target != mouse_over_element) {
      this.resume();
    }
  },
  
  initNavigation: function() {
  	for (var i=0; i<this.slideJson.length; i++) {
  	  //bad implementation with onclick-handlers, requires the slideshow.object to be called "slides" on instantiation
  	  var item = new Element('li', { 'onclick': 'slides.transitionToSlide('+i+');'});
  	  
  	  // better attempt: Use Eventhandlers, but in this case i is passed as a reference, not the plain value, 
  	  // so all navigation dots essentially link to the max slide #.
  	  // i needs to be passed as a value, not a reference. use a closure?
  	  /*var item = new Element('li');
  	  Event.observe(item, 'click', function(e) {this.transitionToSlide(i)}.bindAsEventListener(this), false);
  	  $$('#'+this.navElement+' nav ul')[0].insert(item);*/
  	  
  	  $$('section#'+this.navElement+' nav.dots ul')[0].insert(item);
  	}
  	var next_button = $$('section#'+this.navElement+' nav.buttons a.next')[0];
  	var previous_button = $$('section#'+this.navElement+' nav.buttons a.previous')[0];
  	Event.observe(next_button, 'click', function(e) {this.next()}.bindAsEventListener(this), false);
  	Event.observe(previous_button, 'click', function(e) {this.previous()}.bindAsEventListener(this), false);
  	
  	var next_button_ios = $$('section#main-feature-wrapper a.slide_next')[0];
  	var previous_button_ios = $$('section#main-feature-wrapper a.slide_previous')[0];
  	Event.observe(next_button_ios, 'click', function(e) {this.next()}.bindAsEventListener(this), false);
  	Event.observe(previous_button_ios, 'click', function(e) {this.previous()}.bindAsEventListener(this), false);
  	
  	$(this.navElement).show();
  },
  
  prep: function(){
    this.root.makePositioned();
    var firstSlide = true;
    for (var i=0; i < this.slideJson.length; i++) {
      if (i>0) firstSlide = false;
      this.slides[i] = this.slideBuilder(this.slideJson[i], firstSlide);
      this.slides[i].setStyle({ position: 'absolute', zIndex: i });
      if (this.startHidden || (!this.startHidden && i != 0))
        this.prepSlide(this.slides[i]);
    };
    this.fireEvent('prepped', { slideShow: this });
  },
  
  prepSlide: function(slide){
    return slide.setStyle({ display: 'none', opacity: 0 });
  },
  
  resume: function() {
    //resume Slideshow after it was paused with a mouseover event, delay playback for five seconds
    clearTimeout(this.resumeTimer);
    this.resumeTimer = setTimeout("slides.play();", 3500);
    
    //Better implementation: Should work, but doesn't:
    //var t = setTimeout(function() {this.play}.bind(this), 3000);
  },
  
  play: function(e, index){
    //if this is the very start, lazy load all the images
    // prevent against internal mouse movements from triggering a transition
    if (e && this.mouseIsWithinSlideArea(e)) return;
    this.started = true;
    this.paused = false;
    this.fireEvent('started', { slideShow: this });
    this.transition(index);
  },
  
  pause: function(e){
    //clear resume timer, pause has always priority
    clearTimeout(this.resumeTimer);
    // if it's not started playing, or if it's already paused, or if the mouse isn't within the slide area return
    //if (!this.started || this.paused || (e && !this.mouseIsWithinSlideArea(e))) return;
    this.paused = true;
    this.abortNextTransition();
    
    // queue paused test
    // this.setupPausedTest();
    this.fireEvent('paused', { slideShow: this });
    //}
  },
  
  transitionToSlide: function(index) {
    if (!this.transitionInProgress) {
      this.transitionInProgress = true;
      this.abortNextTransition();
      this.pause();
      this.play(null, index);
    }
  },
  
  transition: function(index){
    if (this.paused) return;
    if (this.nextTransition) this.nextTransition.stop();
    
    //if index param is set, we'll jump to this specific slideIndex
    if (index === undefined) {
      this.coming = this.slides[this.slideIndex];
    } else {
      this.coming = this.slides[index];
    }
    
    //if it is the very first transition, load all the other images
    if (this.slideCount == 0)
      for (var i=1; i < this.slideJson.length; i++) {
        var img_url = this.slides[i].down('img').getAttribute('alt');
        this.slides[i].down('img').setAttribute('src', img_url);
      }

    this.root.appendChild(this.coming); // TODO: append the images at this point,
                                        //       to ensure they're lazily loaded.
    this.going = this.coming.previous() || this.slides.last();
    
    var coming = this.coming;
    var going = this.going;
    
    if (this.slideCount > 0 && this.coming == this.slides.first() && this.going == this.slides.last()) {
      this.fireEvent('looped', { slideShow: this });
      this.loopCount++;
      this.afterFinish();
      if (!this.loop) return;
    }
    
    if (index === undefined)
      this.slideIndex++;
    else {
      this.slideIndex = index+1;
    }
        
    this.slideCount++;
    
    this.updateNavigation();
    
    if (this.slideIndex >= this.slides.length) this.slideIndex = 0;    
    
    // if not fresh start, fade
    if (going != coming) {
      if (this.crossFade) {
        going.fade({ duration: this.transitionDuration / 2 });
        coming.appear({ duration: this.transitionDuration / 2, after: function() {
         this.prepSlide(going);
         this.afterTransitionEffect();
        }.bind(this)});
        // FIXME: do we need to use Parallel FX here?
        /*var fx = new S2.FX.Parallel(
          [new S2.FX.Morph(coming, {
            duration: 0.5,
            position: 'parallel',
            style: 'opacity: 1',
            after: function() {
            }
          }),
          new S2.FX.Morph(going, {
            duration: 0.5,
            position: 'parallel',
            style: 'opacity: 0',
            after: function() {
              this.prepSlide(going);
              this.afterTransitionEffect();
            }
          })],
          {
            duration: this.transitionDuration
          }
        );
        fx.play();
        */
      } else {
        going.fade({
          duration: this.transitionDuration / 2,
          after: function(){
            this.prepSlide(going);
            coming.appear({
              duration: this.transitionDuration / 2,
              after: this.afterTransitionEffect.bind(this)
            });
          }.bind(this)
        });
      }
    }
    // fade in the first time
    else {
      coming.appear({
        duration: this.transitionDuration / 2,
        after: this.afterTransitionEffect.bind(this)
      });
    }
    this.fireEvent('transitioned', { slideShow: this, coming: coming, going: going, loopCount: this.loopCount, slideIndex: this.slideIndex });
    if (index !== undefined && index !== null) { 
      this.pause();
      this.resume();
    }
  },
  
  afterTransitionEffect: function() {
    this.transitionInProgress = false;
    this.scheduleNextTransition();
  },
  
  scheduleNextTransition: function(){
      if (this.slideDuration <= 0) return;
      this.nextTransition = new PeriodicalExecuter(function(nextTransition){
        if (this.paused) return;
        this.transition();
      }.bind(this), this.slideDuration);
  },
  
  abortNextTransition: function(){
    if (this.nextTransition) this.nextTransition.stop();
  },
  
  updateNavigation: function(){
	  $$('section#'+this.navElement+' nav ul > li').each(function(element){
	    $(element).removeClassName('active');
	  });
	  $$('section#'+this.navElement+' nav ul > li:nth-child('+(this.slideIndex)+')').each(function(item){
	    $(item).addClassName('active');
	  });
  },
  
  fireEvent: function(name, memo){
    this.root.fire('SlideShow_' + this.root.id + ':' + name, memo);
  },
  
  mouseIsWithinSlideArea: function(e){
    var offsets = this.root.cumulativeOffset();
    var maxX = offsets.left + this.root.getWidth();
    var minX = offsets.left;
    var maxY = offsets.top + this.root.getHeight();
    var minY = offsets.top;
    
    // minX and the maxY need to be checked like this
    if (minX == e.pointerX() || maxY == e.pointerY()) return false;
    if ($R(minX, maxX).include(e.pointerX()) && $R(minY, maxY).include(e.pointerY())) {
      return true; } else { return false; }
  },
  
  end: function(){
    this.pause();
    document.stopObserving(this.events.play, this.playEventFunction);
    document.stopObserving(this.events.init, this.initEventFunction);
  },
  
  remove: function(){
    this.end();
    this.root.remove();
    this.fireEvent('removed');
  },

  previous: function() {
    if (this.slideIndex == 1) {
      this.transitionToSlide(this.slides.length-1);
    } else if (this.slideIndex == 0) {
      this.transitionToSlide(this.slides.length-2);
    } else {
      this.transitionToSlide(this.slideIndex-2);
    }
  },

  next: function() {
    if (this.slideIndex >= this.slides.length) {
      this.transitionToSlide(1);
    } else {
      this.transitionToSlide(this.slideIndex);
    }
  }
});


Event.observe(window, 'load', function(e){
  document.fire('window:loaded');
});
// JavaScript Document
