var Rotator = Class.create({
  initialize: function(element, images, options) {
    this.element = $(element);
    this.images  = $A(images);
    this.setOptions(options);
    
    this.options.exactDelay = this.options.exactDelay || this.options.delay + this.options.duration;
    
    switch (this.options.order) {
      case 'random':
        this.images.sort(function(image) {
          return 0.5 - Math.random();
        });
        break;
      case 'reverse':
        this.images.reverse();
        break;
    }

    this.slides  = new Hash({});
    this.counter = 0;

    this.swap();

    this.cycle.bind(this).delay(2);

    this.executer = new PeriodicalExecuter(this.cycle.bind(this), this.options.exactDelay);
  },
  setOptions: function(options) {
    this.options = Object.extend(this.options || {},{
      delay:      5,
      duration:   1.0,
      fps:        50,
      crossfade:  true,
      slideClass: 'slide'
    });
    Object.extend(this.options, options || {});
  },
  getSlide: function(slide) {
    if (!this.slides.get(slide)) {
      this.slides.set(slide, Element('div', { className: this.options.slideClass, style: 'background-image: url('+slide+');' }));
    }
    return this.slides.get(slide);
  },
  nextSlide: function() {
    if (this.counter >= this.images.length)
      this.counter = 0;
    return this.getSlide(this.images[this.counter++]);
  },
  swap: function() {
    this.element.up().setStyle('background-image: url('+this.images[this.counter++]+');');
  },
  cycle: function(executer) {
    var nextSlide = this.nextSlide();
    var currentSlide;

    if (this.element.down()) {
      currentSlide = this.element.down();
    } else {
      currentSlide = new Element('div');
      this.element.insert(currentSlide);
    }

    if (!nextSlide.descendantOf(this.element)) {
      this.element.insert(nextSlide);
    } else {
      nextSlide = nextSlide.remove();
      this.element.insert(nextSlide);
    }

    if (this.options.crossfade) {
      nextSlide.setOpacity(0);
      new Effect.Parallel([
        new Effect.Fade(currentSlide, { sync: true }),
        new Effect.Appear(nextSlide,  { sync: true })
      ], {
        duration: this.options.duration,
        fps:      this.options.fps
      });
    } else {
      nextSlide.setOpacity(0);
      new Effect.Fade(currentSlide, {
        duration: this.options.duration / 2,
        fps: this.options.fps,
        afterFinish: function() {
          new Effect.Appear(nextSlide, {
            duration: this.options.duration / 2,
            fps: this.options.fps
          });
        }.bind(this)
      });
    }
  }
});
