Calwineries.Carousel = new Class({

  options: {
    period: 10000
  },

  initialize: function(links) {
    this.bound = {
      click: this.click.bindWithEvent(this),
      rotate: this.rotate.bind(this)
    }

    // organize targets by position and setup fx
    var targets = [], options = {
      onStart: function(element) { element.isDisplayed() || element.show() },
      onComplete: function(element) { element.getOpacity() || element.hide() }
    };
    for (var i = 1, l = arguments.length; i < l; i++) {
      arguments[i].each(function(element, j) {
        element.set('opacity', j ? 0 : 1).set('tween', options);
        targets[j] = (targets[j] || []).include(element);
      });
    }

    this.links = links;
    this.links.each(function(element, i) { element.store('carousel:targets', new Elements(targets[i])) });

    this.connect();
    this.start();
  },

  connect: function() {
    this.links.addEvent('click', this.bound.click);
    return this;
  },

  disconnect: function() {
    this.links.removeEvent('click', this.bound.click);
    return this;
  },

  start: function() {
    if (this.links.length <= 1) return;
    this.timer = this.timer || this.bound.rotate.periodical(this.options.period);
    return this;
  },

  stop: function() {
    $clear(this.timer);
    this.timer = null;
    return this;
  },

  click: function(event) {
    event.stop();
    return this.stop().show(event.target);
  },

  rotate: function() {
    var index = $chk(this.rotator) ? this.rotator : 0;
    this.rotator = this.showIndex(++index);
    return this;
  },

  showIndex: function(index) {
    if (!this.links[index]) index = 0;
    this.show(this.links[index]);
    return index;
  },

  show: function(element) {
    element = $(element);

    function show() {
      element.addClass('active').retrieve('carousel:targets').fade('in');
    }

    this.links.some(function(link) {
      if (link.hasClass('active')) {
        link.removeClass('active').retrieve('carousel:targets').fade('out')[0].get('tween').chain(show);
        return true;
      }
    }) || show();

    return this;
  }

});
