/**
 * Star rating
 * Add class "readonly" to the root object for cancel click and updates.
 *
 * @param  value     {Numeric}   Rating
 * @param  voters    {Integer}   Number of voters
 * @param  min       {Integer}   Minimum value for rating
 * @param  max       {Integer}   Maximem value for rating
 * @param  callback  {Function}  Callback for tracking user clicks with target rating in args.
 *                               "this" in callback body refers to the "star-rating" object.
 *                               Callback must return {value: xxx, voters: xxx} object for update widget
 *
 * @example
 *		var rate = {value: 0, voters: 0};
 *		function update(r) {
 *			rate.value = (rate.value * rate.voters + r) / (rate.voters + 1);
 * 			rate.voters++;
 * 			return {value: Math.round(rate.value*100)/100, voters: rate.voters};
 *		}
 *		$(document).ready(function () {
 *			$("#rate").starrating($.extend(rate, {callback: update}));
 *		});
 *
 * @version  1.0
 * @author  Mista K.
 */
(function ($) {
	$.fn.starrating = function () {
		var eles = this;
		var opts = {value: 0, voters: 0, min: 1, max: 5, callback: function () {}};
		$.extend(opts, arguments[0]);

		var getWidth = function (data) {
			var w = Math.round(100 * data.value / (data.max - data.min + 1));
			w = (w > 100) ? 100 : (w < 0) ? 0 : w;
			return (w + "%");
		};
		
		var updateValue = function (obj, data) {
			$(".bar", obj).text(data.value).css("width", getWidth(data));
			$(".value", obj).text(data.value);
			$(".voters", obj).text('(' + data.voters + ')');
		};
		
		var clickHandler = function (e) {
			var par = $(this).parent();
			if (!par.hasClass("readonly")) {
				var offset = $(this).offset();
				var x = e.pageX - offset.left;
				var y = e.pageY - offset.top;
				var width = $(this).width();
				
				if (x > 0 && x < width) {
					// catch click
					var data = par.data("starrating");
					if (typeof data.value !== "undefined") {
						var targetRating = Math.floor(x * (data.max - data.min + 1) / width) + data.min;
						targetRating = (targetRating > data.max) ? data.max : (targetRating < data.min) ? data.min : targetRating;
						
						if ($.isFunction(data.callback)) {
							var result = data.callback.call(this, targetRating);
							if (typeof result === "object" && typeof result.value !== "undefined" && typeof result.voters !== "undefined") {
								data.value = parseFloat(result.value);
								data.voters = parseInt(result.voters);
								updateValue(par, data);
								par.data("starrating", data);
							}
						}
					}
					return false;
				}
			}
		};
		
		return (
			eles.each(function () {
				var ele = $(this).addClass("star-rating").empty().append(
					['<div class="star-rating-bar">',
						'<div class="bar">', opts.value, '</div>',
					'</div>',
					'<div class="star-rating-text">',
						'<span class="value">', opts.value, '</span>',
						'<span class="voters">(', opts.voters, ')</span>',
					'</div>'].join(''));
				ele.data("starrating", opts);
				$(".bar", ele).css("width", getWidth(opts));
				$(".star-rating-bar", ele).click(clickHandler);
			})
		);
	};
})(jQuery);