/* http://www.bowjay.com/jquery.bowjayslideshow.html
   BowJay SlideShow for jQuery v1.5.4.
   Written by Boris FELIX (bf{at}zeropixel.fr) January 2011.
   Please attribute the author if you use it. */

(function( $ ){

  $.fn.bowjaySlideshow = function( aOptions ) {      
    // Create some defaults, extending them with any options that were provided
    var settings = $.extend({
      'width'  : 940,
      'height' : 470,
      'nbLine' : 5,
      'nbCol' : 5,
      'automatic' : false,
      'interval' : 5000,
      'transition' : false,
      'transitionChaos' : false,
      'withBlockTransitionDelay' : true,
      'blockTransitionDelay' : 35,
      'showNav' : true,
      'showDots' : true
    }, aOptions);

    return this.each(function() {              
      var bowjaySlideshow = new BowjaySlideShow($(this), settings);
    });

  };
  
  var BowjaySlideShow = function( aItem, aSettings ){
    var self = this;
    var _block = []; 
    
    this.settings = aSettings; 
    this.args = {  
      blockHeight: self.settings.height / self.settings.nbLine,
      blockWidth : self.settings.width / self.settings.nbCol - 14,
      nbBlock : self.settings.nbLine * self.settings.nbCol,
      indice : 0,
      animationNum : 0,
      posX : 0,
      posY : 0,
      timeOut : 0,
      transitions: [
        function( aBlockA, aBlockB, aCallback ){
          aBlockB.css({opacity:0}).animate({ opacity: 1 }, 1000, 'easeInOutCirc');
          aBlockA.animate({ opacity: 0 }, 1000, 'easeInOutCirc', function(){
            aCallback();
          });
        },
        function( aBlockA, aBlockB, aCallback ){ 
          aBlockB.css({ zIndex: 1, opacity: 1, top: -aBlockB.height(), left: 0 }).animate({ top: 0 }, 600);
          aBlockA.css({ opacity: 1 }).animate({ opacity: 0, top: aBlockA.height() }, 1000, 'easeInOutCirc', function(){
            aCallback();
          });
        },
        
        function( aBlockA, aBlockB, aCallback ){ 
          aBlockB.css({ zIndex: 1, opacity: 1, left: -aBlockB.width(), top: 0 }).animate({ left: 0 }, 600);
          aBlockA.css({ opacity: 1 }).animate({ opacity: 0, left: aBlockA.width() }, 1000, 'easeInOutCirc', function(){
            aCallback();
          });
        },
        
        function( aBlockA, aBlockB, aCallback ){ 
          aBlockB.css({ zIndex: 1, opacity: 1, top: aBlockB.height(), left: 0 }).animate({ top: 0 }, 600);
          aBlockA.css({ opacity: 1 }).animate({ opacity: 0, top: -aBlockA.height() }, 1000, 'easeInOutCirc', function(){
            aCallback();
          });
        },
        
        function( aBlockA, aBlockB, aCallback ){ 
          aBlockB.css({ zIndex: 1, opacity: 1, left: aBlockB.width(), top: 0 }).animate({ left: 0 }, 600);
          aBlockA.css({ opacity: 1 }).animate({ opacity: 0, left: 0 }, 1000, 'easeInOutCirc', function(){
            aCallback();
          });
        }/*,
        
        function( aBlockA, aBlockB, aCallback ){ 
          setTimeout( function(){
            aBlockB.css({ zIndex: 1, opacity: 1, left: -aBlockB.width(), top: -aBlockB.height() }).animate({ left: 0, top: 0 }, 200);
          }, 100);
          
          aBlockA.css({ opacity: 1 }).animate({ opacity: 0, left: aBlockA.width(), top: aBlockA.height() }, 1000, 'easeOutBack', function(){
            aCallback();
          });
        }*/
      ]
    }
    
    /* Init slideshow */
    for( var i=0; i < self.args.nbBlock; i++ )
    {
      //On applique une classe aux différents bloc du slideshow en fonction de leur position
      //afin de leur appliquer ou pas une bordure et de ne pas déformer le slideshow
      var _class = '';
      _class   = ( i + 1 % self.settings.nbCol == i ) ? 'topRight' : _class;      
      _class   = i >= self.args.nbBlock - self.settings.nbCol ? 'bottom' : '';
      _class  += i == self.args.nbBlock - 1 ? ' bottomRight' : '';
      
      //On applique une fois pour toute les positions des background pour chacun des blocs qui composent la grille
      var background = 'background-position-x:'+self.args.posX+'px; background-position-y:'+self.args.posY+'px;';
      
      //On applique les images sur les blocs
      var backgroundA = 'background-image:url('+self.settings.items[self.args.indice].image+');';       
      var backgroundB = 'background-image:url('+self.settings.items[self.args.indice + 1 > self.settings.items.length - 1 ? 0 : self.args.indice + 1].image+');'; 
      
      //On rempli un tableau avec les différents bloc à ensuite rajouter au slideshow
      _block.push('<div id="block'+(i+1)+'" class="block '+_class+'" style="width:'+self.args.blockWidth+'px;height:'+self.args.blockHeight+'px"><div class="inner_b" style="'+backgroundB+background+'"></div><div class="inner_a" style="'+backgroundA+background+'"></div></div>');
      
      //A chaque ligne on diminue la position verticale du background pour simuler l'image
      //sur l'ensemble des blocs
      self.args.posY = i > 0 && i % self.settings.nbCol == self.settings.nbCol - 1 ? self.args.posY - self.args.blockHeight : self.args.posY;
      
      //On avance d'un palier équivalent à la largeur d'un bloc à chaque fois et quand on arrive à la fin
      //de la ligne on reprend à zéro pour afficher le reste de l'image
      self.args.posX = i % self.settings.nbCol == self.settings.nbLine - 1 ? 0 : self.args.posX - self.args.blockWidth; 
    }
    
    //Modification de l'élément choisi en offline pour gagner en performance
    var aItemClone = aItem.clone();
    aItemClone.html('<ul id="content-slider"><li><a href="'+(typeof self.settings.items[self.args.indice].link != 'undefined' ? self.settings.items[self.args.indice].link : '#')+'">'+_block.join('')+'</li></ul>').addClass('bowjayslideshow');
    
    //On adapte les dimensions du slideshow par rapport aux blocs qui ont des bordures
    aItemClone.find('#content-slider').css('width', this.settings.width + this.settings.nbCol - 5 );
    aItemClone.find('#content-slider').css('height', this.settings.height + this.settings.nbLine - 2 );
    aItemClone.css('width', this.settings.width + this.settings.nbCol + 100 );
    aItemClone.css('height', this.settings.height + this.settings.nbLine - 2 + 87 );
    
    aItem.replaceWith(aItemClone);
    self.slideshow = aItemClone;
    
    if( self.settings.automatic )
      self.slideshow.find('li a').hover(
        function(){ self.hover = true; clearInterval(self.interval); clearTimeout(self.interval); clearTimeout(self.timeout); delete(self.interval); },
        function(){ self.hover = false; self.playSlideshow(); }
      );
    
    //Show dots to control the slideshow image by image
    if( self.settings.showDots )
    {
      var dots = $('<ul id="nav-slider"></div>');
      for(var i=0; i<self.settings.items.length; i++)
      {
        var dot = $('<li indice="'+i+'"><a href="#">'+(i+1)+'</a></li>').bind('click', function(ev){ ev.preventDefault(); self.animateSlideshow($(this).attr('indice')); return false; });
        
        if( i == 0 )
          dot.find('a').addClass('current');
        
        dots.append(dot);
      }
      
      self.slideshow.prepend(dots);
    }    
    
    //Show a prev/next navigation
    if( self.settings.showNav )
    {
      var next = $('<a href="#" id="arrow-right" class="next">&gt;</a>').click(function(ev){ ev.preventDefault(); self.getNextImage(); return false; });
      var prev = $('<a href="#" id="arrow-left" class="prev">&lt;</a>').click(function(ev){ ev.preventDefault(); self.getPrevImage(); return false; });
      var nav = $('<div id="nav"></div>').append(next).append(prev);
      
      self.slideshow.prepend(nav);
      self.slideshow.find('#nav a').css('top', self.slideshow.find('#content-slider').height() / 2);
    }  
    
    self.slideshow.append('<div id="shadow"></div>');
    
    self.setTitle(); 
    self.hover = false; 
    
    if( self.settings.automatic )
      self.timeout = setTimeout( function(){
        self.getNextImage();
      }, self.settings.interval);
  }
  
  $.extend( BowjaySlideShow.prototype, {
    
    /* Doing a transition between a block A and a blockB to reach the currentItem in the slideshow
     * @param aBlockA         jQueryDomObject   the first container
     * @param aBlockB         jQueryDomObject   the second container
     * @param aIndice         integer           the index of the current block being manipulated
     * @param aCurrentIndice  integer  the index of the item wich to be reached
     * */
    doTransition: function( aBlockA, aBlockB, aIndice, aCurrentIndice ){
      var self = this;
      
      var backgroundB = 'url('+self.settings.items[aCurrentIndice].image+')';
      aBlockB.css({'backgroundImage': backgroundB, opacity: 1, top: 0, left: 0 });

      self.args.transitions[self.args.animationNum]( aBlockA, aBlockB, function(){
        var backgroundA = aBlockB.css('backgroundImage');  
        aBlockA.css({'backgroundImage': backgroundA, opacity: 1, top: 0, left: 0 });
        
        if( self.settings.transitionChaos )
        {
          if( self.settings.transition === false )
            //On choisi au hasard la prochaine animation qui s'affichera à l'écran
            self.args.animationNum = Math.floor(Math.random()*self.args.transitions.length);  
          else
            self.args.animationNum = typeof self.args.transitions[self.settings.transition] != 'undefined' ? self.settings.transition : 0;
        }
        
        if( aIndice == self.slideshow.find('.block').size() - 1 )
          self.playSlideshow();
      });
    },
    
    /* Setting the slideshow on the current item 
       @param aCurrentIndice  integer  the index of the item wich to be reached
     * */
    animateSlideshow: function( aCurrentIndice ){
      var self = this;
      
      if( self.hover == true )
        return false;
      
      self.args.timeOut = 0;
      self.args.indice = aCurrentIndice;
      self.animated = true;
      
      //Setting the title of the current item
      self.setTitle();
      
      $('#nav-slider li a').removeClass('current');
      $('li[indice="'+aCurrentIndice+'"]').find('a').addClass('current');
      
      this.slideshow.find('#content-slider li a').attr('href', self.settings.items[self.args.indice].link);
      this.slideshow.find('.block').each( function( aIndice, aBlock ){
        var blockA = $(aBlock).find('.inner_a');
        var blockB = $(aBlock).find('.inner_b');
        
        if( self.settings.withBlockTransitionDelay ){
          //Si la somme des timeout est supérieure au temps d'attente entre chaque transition
          //on ramène les temps d'attente entre chaque bloc à une durée plus raisonnable )
          self.settings.blockTransitionDelay = self.settings.blockTransitionDelay * self.args.nbBlock > self.settings.interval ? Math.floor( self.settings.interval / self.args.nbBlock ) - (Math.floor( self.settings.interval / self.args.nbBlock ) / 4) : self.settings.blockTransitionDelay;
          
          //Tous les deux blocs ou à chaque ligne, on augmente le timeout entre chaque animation pour
          //accentuer l'effet de domino entre les lignes du damier
          if( aIndice > 2 || aIndice % self.settings.nbLine == 0 ){
            self.args.timeOut += self.settings.blockTransitionDelay;
          }
          
          //On exécute un timeout a chaque bloc pour créer un "effet domino" sur les blocs
          setTimeout( function(){
            self.doTransition( blockA, blockB, aIndice, aCurrentIndice );           
          }, self.args.timeOut); 
        }
        else
        {
          self.doTransition( blockA, blockB, aIndice, aCurrentIndice );
        }
      });
      
      if( !self.settings.transitionChaos )
      {
        if( self.settings.transition === false )
        {
          //On choisi au hasard la prochaine animation qui s'affichera à l'écran
          self.args.animationNum = Math.floor(Math.random()*self.args.transitions.length);  
        }
        else
          self.args.animationNum = typeof self.args.transitions[self.settings.transition] != 'undefined' ? self.settings.transition : 0;
      }
    },
    
    /* Setting the slideshow title 
     * */
    setTitle: function(){
      var items = this.settings.items;
      
      //Define slideshow title
      var title = typeof items[this.args.indice].title != 'undefined' ? items[this.args.indice].title : false;
      
      if( !title )
        return false;
      
      //Si on passe un argument "title", on crée un block pour contenir celui-ci
      if( this.slideshow.find('#content-slider li .title').size() == 0 )
        this.slideshow.find('#content-slider li').append('<div class="title">'+title+'<div>');
      else
        this.slideshow.find('#content-slider .title').html(title);
    },
    
    /* Play the slideshow and setting automaticaly the items list
     * */
    playSlideshow: function(){
      var self = this;
      clearInterval(self.interval);
      clearTimeout(self.interval);
      if( self.settings.automatic )
        self.interval = setInterval( function(){
          self.getNextImage();
        }, self.settings.interval); 
    },
    
    /* Setting the previous item
     * */
    getPrevImage: function(){
      var self = this;

      var indice = self.args.indice - 1 >= 0 ? self.args.indice - 1 : self.settings.items.length - 1;
      
      self.animateSlideshow(indice);
    },
    
    /* Setting the next item
     * */
    getNextImage: function(){
      var self = this;

      var indice = self.args.indice + 1 > self.settings.items.length - 1 ? 0 : self.args.indice + 1;
      
      self.animateSlideshow(indice); 
    }
  });
})( jQuery );
