/**
* Javascript class for generating dynamic graphical ratings from a simple value of a div.
* <div>value</div> is transformed into something more interesting. If a callback function
* is defined then the resulting div will also support mouse events.
*
* Requires:
* mootools-core
*
* Usage:
* new DynamicRating(div [,{options}])
* where div can either be a an element or the id of an element.
* with inner html on the form <div>value/maxrating/votes/index</div>
*
* Output:
* <div class="ratingsDiv">
* 	(<div><!-- for click events --></div>)
*	<div class="ratingsBar/ratingsBarUnrated">
*		(<img>
*		  .
*		  .
*	   	  .
*		<img>)
*	</div>
* 	<div class="text">
* </div>
*
* Options:
* width: 			width of the image.
* height: 			height of element.
* length: 			amount of times the image will be repeated.
* img: 				url to the image to use. (needs to be transparent)
* start: 			start position of the bar. Can be left, right, top, bottom.
* duration			speed of effects.
* steps				amount of steps for snapping. Can NOT be 0.
* initstring 		string to show when initiated: :value: for value, :votes: for votes, :max: for max, :voted: for what user voted. 
* votedstring		string to show when you have voted.
* mousoverstring	string to show on mouseover. Can also be an array of strings.
*
* callback: 		callback function, is called on a click. Gets {index:, value:, votes:, voted:} as input.
* args: array of arguments for callback
*/

DynamicRating = new Class({
	Implements: [Options,Events],
	options : {
		width : 20,
		height: 20,
		index: 0,
		length : false,
		img : 'star.png',
		horizontal : true,
		inverted : false,
		duration: 640,
		steps: 10,
		initstring: null,
		votedstring : null,
		mouseoverstring: null,
		callback : false,
		cookiename : "starRating",
	},
	barWidth : 0,
	barHeight : 0,
	value : 0,
	rated : false,
	max : 0,
	index : 0,
	voted : 0,
	
	initialize : function(element, options){
		this.setOptions(options);
		element = $(element);	
		this.initVars(element);
		
		element.set('class','starRating');
		
		var rating = new Element('div', {
			'class' : 'bar',
			styles: {
				'width': this.width,
				'height': this.height,
				'position' : 'relative',
				'line-height' : 0,
			}
		});	
		
		/*Load the images*/
		rating.grab(this.loadImages());
		
		var bar = this.loadBar();
			rating.grab(bar, 'top');
		
		var text = this.loadText();
		
		if(!this.checkCookie())
			rating.grab(this.loadEvent(element, bar, text), 'top');		
		else{
			bar.addClass('rated');
			if(this.options.votedstring) text.set('html', this.parseText(this.options.votedstring));
		}
		
		element.grab(rating);
		element.grab(text);
	},
	
	initVars : function(element){
		/*Get the value from the div, reset innerHTML*/
		var input = element.get('html').split('/');
		element.set('html', "");
		this.value = input[0].toFloat();
		this.max = input[1];
		this.votes = input[2];
		this.index = input[3];
		this.options.length = this.options.length || this.max;
		
		/*Check the starting position and set width/height accordingly.*/
		if(this.options.horizontal) {
			this.height = this.options.height;
			this.width = this.options.width * this.options.length;
			this.barWidth = (this.value) ? (this.value/this.max)*this.options.length*this.options.width : 0.000001;
			this.barHeight = this.options.height;
		}
		else{
			this.width = this.options.width;
			this.height = this.options.height * this.options.length;
			this.barHeight = (this.value) ? (this.value/this.max)*this.options.length*this.options.height : 0.0000001;
			this.barWidth = this.options.width;
		}
	},
	
	loadText : function(){
		var text = new Element('div',{
			'class' : 'text',
			'html': this.parseText($pick(this.options.initstring,'')),
			styles : {
			}
		});
		return text;
	},
	
	loadBar : function(){
		var bar = new Element('div',{
			'class' : 'normal',
			styles : {
				'display' : 'inline',
				'position':'absolute',
				'width' : this.barWidth,
				'height' : this.barHeight,
			}
		});
		/*would not fit within the constructor*/
		var style = this.options.horizontal ? this.options.inverted ? 'right':'left'  : this.options.inverted ? 'top' : 'bottom';
		bar.setStyle(style, 0);
		
		bar.set('tween',{duration:this.options.duration});
		return bar;
	},
	
	loadImages : function(){
		var images = Array();
	  		for(i=0;i<this.options.length;i++){
	  		var img = new Element('img', {
	  			src : this.options.img,
	  			styles: {
	  				'height' : this.options.height,
					'width' : this.options.width , 
	  				'position' : 'relative',
	  			},
	  		});
	  		images.push(img);
	  	}
		
		var imageDiv = new Element('div',{'class':'images'});
		
		imageDiv.adopt(images);
	  	return imageDiv;
	},
	
	loadEvent: function(div, bar, text) {
			var eventDiv = new Element('div',{
				styles : {
					'width': this.width,
					'height': this.height,
					'position' : 'absolute',
					'z-index' : 2,
					'cursor' : 'pointer',
				}
			});
							
			eventDiv.addEvents({
				'mousemove': function(event){
					bar.removeClass('normal'); bar.addClass('hover');
					bar.get('tween').cancel();
					
					if(this.options.horizontal){
						var val = Math.floor(this.options.steps*((event.page.x - div.getPosition().x) / this.options.width)+1)*(1.0/this.options.steps)*this.options.width
						if(this.options.inverted)
							var val = this.width - val;
						bar.setStyle('width', val);
						
					}
					else{
						var val = Math.round(this.options.steps*((event.page.y - div.getPosition().y) / this.options.height))*(1.0/this.options.steps)*this.options.height;
						if(this.options.inverted)
							var val = this.width - val;
						bar.setStyle('height', val);
					}
					
					if ($type(this.options.mouseoverstring) == 'array'){
						var size = this.options.horizontal ? this.options.width : this.options.height;
						var index = Math.floor(((val.toFloat()/size)/this.max) * (this.options.mouseoverstring.length-0.5));
						text.set('html', this.parseText(this.options.mouseoverstring[index]));
					}
					else if ($type(this.options.mouseoverstring) == 'string')
						text.set('html', this.parseText(this.options.mouseoverstring));
						
				}.bind(this),
				'mouseout' : function(){
					if(bar.hasClass('hover')){
						bar.removeClass('hover'); bar.addClass('normal');
						
						if(this.options.initstring)
							text.set('html', this.parseText(this.options.initstring));
						
						this.options.horizontal ? bar.tween('width', this.barWidth) : bar.tween('height', this.barHeight);
					}
				}.bind(this),
				'click' : function(event){
					eventDiv.destroy();
					
					this.barWidth = bar.getStyle('width').toInt();
					this.barHeight = bar.getStyle('height').toInt();
					this.voted = this.calculateValue();
					
					this.votes++;
					this.value = ((this.value*(this.votes-1))+this.voted)/(this.votes);
					
					if(this.options.votedstring) text.set('html', this.parseText(this.options.votedstring));
					else if(this.options.initstring) text.set('html', this.parseText(this.options.initstring));
					
					if(this.options.horizontal)
						bar.tween('width', ((this.value/this.max)*this.options.length*this.options.width)-1);
					else
						bar.tween('height', (this.value/this.max)*(this.options.length*this.options.height)-1);
					
					bar.removeClass('hover'); bar.addClass('rated');
					
					this.fireEvent('onClick', [this.index, this.value, this.voted, this.votes]);
					this.writeCookie();
				}.bind(this)
			});
			
			return eventDiv;
	},
	
	parseText :function (text){
		text = text.replace(":value:", this.value.toString().substr(0,3));
		text = text.replace(":max:", this.max);
		text = text.replace(":voted:", (this.voted ? this.voted : 0).toString().substr(0,3));
		text = text.replace(":votes:", this.votes);
		return text;
	},
	
	checkCookie : function() {
		var cookie = Cookie.read(this.options.cookiename); 
		if (cookie != null){
			var ret = cookie.match(new RegExp("s"+this.index+"-[0-9]*\.?[0-9]+es"));
			if(ret){
				value = ret[0].split('-')[1];
				this.voted = value.substr(0,value.length-2);
				return true;
			}
		} 
		return false;
	},
	
	writeCookie : function(){
		val = Cookie.read(this.options.cookiename);
		Cookie.write(this.options.cookiename, (val ? val : "s") + this.index+"-"+this.voted+"es", {duration:30});
	},
	
	calculateValue : function(){
		if(this.options.horizontal)
			return (this.barWidth.toFloat() / this.options.width / this.options.length) * this.max;
		else			
			return (this.barHeight.toFloat() / this.options.height  / this.options.length) * this.max;
	}
});
