/**
 * JumbMooSlide - a mootools based multi slideshow, featuring smart preloading and overflow culling
 *
 * @version		0.6
 * @license		MIT License
 * @author		Gonzalo Rubio [gonchuki] <gonzalo.rubio [at] replayful [dot] com>
 * @copyright Replayful 2010
 */

var JumbMooSlide = new Class ({
  Implements: Options,
  
  cache: {},
  cache_length: 0,
  display_queue: [],
  busy: false,
  paused: false,
  load_complete: false,
  
  options: {
    slides: [],
    duration: 750,
    delay: 750,
    base_path: ''
  },
  
  initialize: function(options) {
    this.setOptions(options);
    
    // pointer indicates the next image to be displayed from each stack, the image paths are also concatenated with the provided base path
    this.options.slides.each(function(tile){
      tile.pointer = 0;
      tile.tiles = tile.tiles.map(function(src) { return this.options.base_path + src; }, this);
    }, this);
    
    // total unique different src slides, use [].combine so we avoid including Array.Extras from -more
    this.total_slides = [].combine(this.options.slides.map(function(slide){ return slide.tiles; }).flatten()).length;
    
    // set tween options for existing images in the document
    this.set_tween_options(document.getElements(this.options.slides.map(function(slide){return slide.target + ' img';}).join(', ')));
    
    // load first batch of images, subsequent loads are recursive until all images are preloaded
    this.load_batch();
  },
  
  extract_paths: function(batch) {
    return [].combine(batch.map(function(tile){ return tile.src; }).flatten());
  },
  
  get_visible_targets: function() {
    // STUB
    return this.options.slides; //.filter(function(slide) { return [[[[is_visible]]]; });
  },
  
  is_tile_visible: function(target) {
    var w_scroll = window.getScroll();
    var w_size = window.getSize();
    var t_coords = target.getCoordinates();
    
    return ((t_coords.top > w_scroll.y) && (t_coords.top < (w_size.y + w_scroll.y))) ||
           ((t_coords.bottom > w_scroll.y) && (t_coords.bottom < (w_size.y + w_scroll.y)));
  },
  
  filter_cache: function(batch) {
    return batch.filter(function(src) { return !this.cache[src]; }, this);
  },
  
  get_from_cache: function(src) {
    return this.cache[src] ? this.cache[src].clone() : null;
  },
  
  get_chunk: function() {
    return this.randomize(this.get_visible_targets().map(function(slide){
      var ret =  {
        target: slide.target,
        ptr: slide.pointer,
        src: slide.tiles[slide.pointer]
      };
      slide.pointer = ((slide.pointer + 1) < slide.tiles.length) ? slide.pointer + 1 : 0;
      
      return ret;
    }));
  },
  
  randomize: function(array, inplace) {
    var target = inplace ? array : Array.apply(null, array);
    
    target.length.times(function(i) {
      var j = (Math.random() * this.length).floor();
      var tmp = this[i];
      this[i] = this[j];
      this[j] = tmp;
    }, target);
    return target;
  },
  
  load_batch: function() {
    var self = this;
    
    var chunk = self.get_chunk();
    var paths = this.filter_cache(this.extract_paths(chunk));
    this.display_queue.extend(chunk);
    
    // get images in increments of {#tiles} to ensure we get the full set of "next" tiles before sending more requests to the server.
    new Asset.images(paths, {
      onProgress: function(counter, index) {
        // *this* points to the <img> element
        this.fade('hide');
        
        self.image_loaded(paths[index], this);
      },
      
      onComplete: function() {
        // load next batch if there are still images left, else we finished the preloading task.
        if (self.cache_length < self.total_slides)
          self.load_batch();
        else
          self.load_complete = true;
      }
    });
  },
  
  image_loaded: function(src, img) {
    if (!this.cache[src]) {
      this.cache[src] = img;
      this.cache_length++;
    }
    this.next();
  },
  
  tile_ready: function() {
    this.busy = false;
    this.next();
  },
  
  set_tween_options: function(img) {
    // store tween options to fire ready event on show, and auto-dispose on hide.
    var self = this;
    return img.set('tween', {
      duration: self.options.duration,
      transition: Fx.Transitions.Sine.easeIn,
      onComplete: function(el) {
        if (el.get('opacity')) self.tile_ready();
        else el.destroy();
      }
    });
  },
  
  set_picture: function(tile) {  
    var self = this;
    var target = document.getElement(tile.target + ' a, ' + tile.target);
    var prev_img = target ? document.getElement(tile.target + ' a img, ' + tile.target + ' img') : null;
    var delay = prev_img ? this.options.delay : 0;
    var img = this.get_from_cache(tile.src);
    
    if (!target || !self.is_tile_visible(document.getElement(tile.target))) { self.tile_ready(); return; }
    this.set_tween_options(img).fade('hide');
    
    (function() {
      if (prev_img)
        img.inject(prev_img.fade('out'), 'after').fade('in');
      else
        img.inject(target).fade('in');
    }).delay(delay);
  },
  
  next: function() {
    if (this.busy || this.paused) return;
    
    if (this.display_queue.length && this.cache[this.display_queue[0].src]) {
      this.busy = true;
      this.set_picture(this.display_queue.shift());
    } else if (this.load_complete) {
      
      this.display_queue.extend(this.get_chunk());
      this.next();
    }
  },
  
  pause: function() {
    this.paused = true;
  },
  
  resume: function() {
    this.paused = false;
    this.next();
  }
});
