var Starboxes = {
  inverse: false,
  locked: false,
  onRate: Prototype.emptyFunction,
  imageSource: '/img/starbox/',
  overlay: 'default.png',
  REQUIRED_Prototype: '1.6.1',

  load: function() {
    this.require('Prototype');
  },

  require: function(library) {
    if ((typeof window[library] == 'undefined') ||
      (this.convertVersionString(window[library].Version) < this.convertVersionString(this['REQUIRED_' + library])))
      throw('Starbox requires ' + library + ' >= ' + this['REQUIRED_' + library]);
  },

  convertVersionString: function(versionString) {
    var r = versionString.split('.');
    return parseInt(r[0])*100000 + parseInt(r[1])*1000 + parseInt(r[2]);
  },

  fixIE: (function(agent) {
    var version = new RegExp('MSIE ([\\d.]+)').exec(agent);
    return version ? (parseFloat(version[1]) <= 6) : false;
  })(navigator.userAgent),

  imagecache: [],
  cacheImage: function(imageInfo) {
    if(!this.getCachedImage(imageInfo.src)) this.imagecache.push(imageInfo);
    return imageInfo;
  },

  getCachedImage: function(src) {
    return this.imagecache.find(function(imageInfo) { return imageInfo.src == src });
  },

  buildQueue: [],
  queueBuild: function(starbox) {
    this.buildQueue.push(starbox);
  },

  processBuildQueue: function() {
    // on empty queue, stop loading as batches
    if (!this.buildQueue[0]) { this.batchLoading = true; return; }
    this.cacheBuildBatch(this.buildQueue[0]);
  },

  cacheBuildBatch: function(starbox) {
    var set = [];
    var overlay = starbox.options.overlay;
    var imageInfo = this.getCachedImage(overlay);

    // create a batch based on images with the same overlay
    this.buildQueue.each(function(s) {
      if (s.options.overlay == overlay) {
        set.push(s);
        this.buildQueue = this.buildQueue.without(s);
      }
    }.bind(this));

    if (!imageInfo) {
      var starImage = new Image();
      starImage.onload=function() {
        var imageInfo = this.cacheImage({ src: overlay, height: starImage.height,
          width: starImage.width, fullsrc: starImage.src });
        this.buildBatch(set, imageInfo);
      }.bind(this);
      starImage.src = Starboxes.imageSource + overlay;
    }
    else { this.buildBatch(set, imageInfo); }
  },

  buildBatch: function(set, imageInfo) {
    set.each(function(s) {
      s.imageInfo = imageInfo;
      s.build();
    });
    this.processBuildQueue();
  }
};

Starboxes.load();
document.observe('dom:loaded', Starboxes.processBuildQueue.bind(Starboxes));

var Starbox = Class.create({
  initialize: function(element) {
    this.element = $(element),
    this.element.rating = 0;

    this.options = Object.extend({
      buttons: 5,
      className : 'default',
      color: false,
      duration: 0.6,
      hoverColor: false,
      hoverClass: 'hover',
      ghostColor: false,
      ghosting: false,
      ratedClass: 'rated',
      identity: false,
      indicator: false,
      inverse: Starboxes.inverse,
      locked: false,
      max: 5,
      onRate: Starboxes.onRate,
      rated: false,
      overlay: Starboxes.overlay,
      stars: 5,
      total : 0
    }, arguments[2] || {});

    this.rated = this.options.rated;
    this.total = this.options.total;
    this.locked = this.options.locked || (this.rated);

    Starboxes.queueBuild(this);
    if (Starboxes.batchLoading) Starboxes.processBuildQueue();
  },

  enable: function() {
    if (!Prototype.Browser.IE) {
      this.onMouseout = this.onMouseout.wrap(function(proceed, event) {
        var rel = event.relatedTarget, cur = event.currentTarget;
        if (rel && rel.nodeType == Node.TEXT_NODE) rel = rel.parentNode;
        if (rel && rel != cur && !(rel.descendantOf(cur)))
          proceed(event);
      });
    }

    $w('mouseout mouseover click').each(function(e) {
      var E = e.capitalize();
      this['on' + E + '_cached'] = this['on' + E].bindAsEventListener(this);
      this.starbar.observe(e, this['on' + E + '_cached']);
    }.bind(this));
    this.buttons.invoke('setStyle', { cursor: 'pointer' });
  },

  disable: function() {
    $w('mouseout mouseover click').each(function(e) {
      this.starbar.stopObserving(e, this['on' + e.capitalize() + '_cached']);
    }.bind(this));

    this.buttons.invoke('setStyle', { cursor: 'auto' });
  },

  build: function() {
    this.starWidth = this.imageInfo.width;
    this.starHeight = this.imageInfo.height;
    this.starSrc = this.imageInfo.fullsrc;
    this.boxWidth = this.starWidth * this.options.stars;
    this.buttonWidth = this.boxWidth / this.options.buttons;
    this.buttonRating = this.options.max / this.options.buttons;

    var styles = {
      absolute: { position: 'absolute', top: 0, left: 0, width: this.boxWidth + 'px', height: this.starHeight + 'px' },
      base: { position: 'relative', width: this.boxWidth + 'px', height: this.starHeight + 'px' },
      star: { position: 'absolute', top: 0, left: 0, width: this.starWidth + 'px', height: this.starHeight + 'px' }
    };

    this.element.addClassName('starbox');
    this.container = new Element('div', { 'class': this.options.className || '' }).setStyle({ position: 'relative' });

    this.status = this.container.appendChild(new Element('div'));
    if (this.rated) this.status.addClassName('rated');
    if (this.locked) this.status.addClassName('locked');

    this.hover = this.status.appendChild(new Element('div'));
    this.wrapper = this.hover.appendChild(new Element('div', { 'class': 'stars' }));
    this.wrapper.setStyle(Object.extend({ overflow: 'hidden' }, styles.base));

    this.colorbar = this.wrapper.appendChild(new Element('div', { 'class': 'colorbar' }).setStyle(styles.absolute));
    if (this.options.color) this.colorbar.setStyle({ background: this.options.color });

    var starWrapper = this.wrapper.appendChild(new Element('div').setStyle(styles.absolute));
    this.starbar = starWrapper.appendChild(new Element('div').setStyle(styles.base));

    this.options.stars.times(function(i) {
      var star = this.starbar.appendChild(new Element('div').setStyle(Object.extend({
        background: 'url(' + this.starSrc + ') top left no-repeat'
      }, styles.star)));
      star.setStyle({ left: this.starWidth * i + 'px' });

      if (Starboxes.fixIE) {
        star.setStyle({
          background: 'none', 'filter' : 'progid:DXImageTransform.Microsoft.AlphaImageLoader(src=\'' +
            this.starSrc + '\'\', sizingMethod=\'scale\')'
        });
      }
    }.bind(this));

    this.buttons = [];
    this.options.buttons.times(function(i) {
      var leftPos = this.options.inverse ? this.boxWidth - this.buttonWidth * (i + 1) : this.buttonWidth * i;
      var button = this.starbar.appendChild(new Element('div', { href: 'javascript:;' }).setStyle({
        position: 'absolute',
        top: 0,
        left: leftPos + 'px',
        width: this.buttonWidth + (Prototype.Browser.IE ? 1 : 0) + 'px',
        height: this.starHeight + 'px'
      }));
      button.rating = this.buttonRating * i + this.buttonRating;
      this.buttons.push(button);
    }.bind(this));

    this.setBarPosition(this.colorbar, 0);
    this.element.update(this.container);

    if (!this.locked) this.enable();
  },

  getBarPosition : function(rating) {
    var position = (this.boxWidth - (rating/this.buttonRating) * this.buttonWidth);
    return parseInt(this.options.inverse ? position.ceil() : -1 * position.floor());
  },

  setBarPosition: function(element, rating) {
    var left = this.getBarPosition(rating);
    element.setStyle({ left: left + 'px' });
  },

  onClick: function(event) {
    var element = event.element();
    if (!element.rating) return;

    if (!this.rated) this.status.addClassName('rated');
    this.rated = element.rating;

    this.disable();
    this.status.addClassName('locked');
    this.onMouseout(event);

    var info = {
      identity: this.options.identity,
      max: this.options.max,
      rated: element.rating,
      total: this.total
    };
    this.options.onRate(this.element, info);
    this.element.fire('starbox:rated');

    this.element.rating = element.rating;
  },

  onMouseout: function(event) {
    var element = event.element();
    if (!element.rating) return;

    this.setBarPosition(this.colorbar, element.rating);

    this.hovered = false;

    if (this.options.hoverClass){
     this.hover.removeClassName(this.options.hoverClass);
    }
    if (this.options.hoverColor){
     this.colorbar.setStyle({ background: this.options.color });
    }

    this.element.rating = element.rating;
  },

  onMouseover: function(event) {
    var element = event.element();
    if (!element.rating) return;
    this.setBarPosition(this.colorbar, element.rating);

    if(!this.hovered && this.options.hoverClass){
     this.hover.addClassName(this.options.hoverClass);
    }

    this.hovered = true;

    if (this.options.hoverColor){
     this.colorbar.setStyle({ background: this.options.hoverColor });
    }

    this.element.rating = element.rating;
  }
});
