/**
 *  Plugin - rating
 *
 *  Handles the portal rating functionality.
 *
 *  How to apply:
 *
 *  jQuery(document).ready(function () {
 *	    jQuery('span.RatingContainer').rating({
 *			'test' : 'test'
 *	    });
 *  });
 *
 * Notes:
 * 1. That script is based on the jquery framework - make sure the
 *    jquery.js is included when using this script.
 * 2. Make sure the appropriate css stylesheets are included @see rating.css.
 *
 * @author OstrickiDa
 */
(function(){


	// Constructor
	(Rating = function(element, options) {

		this.settings = jQuery.extend({}, this.defaultValues, options || {});

		this.currentRatingContainer =  new Container(element,this.settings);

		// Merge options with default values
		this.init();
	}).prototype = {
		entityUUID : null,
		totalCountContainerElement : null,
		allRelatedContainer : null,
		currentRatingContainer :  null,
		ratingUrl : null,
		defaultValues : {
			'debug' : false,
			'submitSelector':null,
			/**Callback parameter: response*/
			'failedRatingCallback':function(){},
			/**Callback parameter: response*/
			'successfulRatingCallback': function(){}
		},

		init : function() {

			var that = this;
			var ratingContainertId = this.currentRatingContainer.ratingData['rating-id'];

			this.allRelatedContainer = [];
			jQuery('.'+ratingContainertId).each(function() {
				that.allRelatedContainer.push(new Container(this,that.settings));
			});

			// for edge cases like glossary
			jQuery('iframe').contents().find('.'+ratingContainertId).each(function(){
				that.allRelatedContainer.push(new Container(this,that.settings));
			});

			this.currentRatingContainer.on('mouseover', jQuery.proxy(this.eventMouseOver, this));
			this.currentRatingContainer.on('mouseout', jQuery.proxy(this.eventMouseOut, this));
			this.currentRatingContainer.on('click', jQuery.proxy(this.eventOnClick, this));
			this.currentRatingContainer.on('keyup', jQuery.proxy(this.eventOnKeyUp, this));	// accessibility
			
			if(this.settings['submitSelector']){
				jQuery(this.settings['submitSelector']).on('click',function(){
					jQuery.each(that.allRelatedContainer, function(index,value){
						that.sendRatingToServer(value);
					});
				});
			}
			
			this.ensureCorrectUrl();
			this.updateRatingContainer();

		},

		//ie caching problem
		ensureCorrectUrl : function()
		{
			var timestamp=0;
			var latestUrl;

			jQuery.each(this.allRelatedContainer, function(index,value){
				if(value.ratingData['timestamp']>timestamp){
					timestamp = value.ratingData['timestamp'];
					latestUrl = value.ratingData['rating-url'];
				}
			});
			this.currentRatingContainer.ratingData['rating-url'] = latestUrl;
		},


		/*************   event handling   ****************/
		// For accessibilty
		eventOnKeyUp : function (e){

			var container = this.findContainer(e.currentTarget);
			var selectedUnit;
			switch(e.key) {
				case '1':
					alert("20%!");
					selectedUnit = 1;
					break;
				case '2':
					alert("40%!");
					selectedUnit = 2;
					break;
				case '3':
					alert("60%!");
					selectedUnit = 3;
					break;
				case '4':
					alert("80%!");
					selectedUnit = 4;
					break;
				case '5':
					alert("100%");
					selectedUnit = 5;
					break;
				default:
					alert("Please choose a number between 1 and 5!")
				}
			container.currentSelectionInPercent = container.convertRatingUnitToPercent(selectedUnit);
			if(this.settings['submitSelector']){
				var newRatingData = container.collectConfiguration();
				newRatingData['current-user-rating-in-percent'] = container.currentSelectionInPercent;
				container.overwriteConfiguration(newRatingData);
				this.updateRatingContainer();
			}
			else{
				this.sendRatingToServer(container);	
			}
		},		
		
		
		
		eventOnClick : function (e){

			var x = e.pageX;
			e.stopPropagation();

			var container = this.findContainer(e.currentTarget);
			container.on('mousemove', jQuery.proxy(this.eventMouseMove, this));
			x = x - container.offset().left;

			//execution
			var selectedUnit = container.determineClickedRatingUnit(x);
			container.currentSelectionInPercent = container.convertRatingUnitToPercent(selectedUnit);
			if(this.settings['submitSelector']){
				var newRatingData = container.collectConfiguration();
				newRatingData['current-user-rating-in-percent'] = container.currentSelectionInPercent;
				container.overwriteConfiguration(newRatingData);
				this.updateRatingContainer();
			}
			else{
				this.sendRatingToServer(container);	
			}
		},

		eventMouseOver : function (e){

			var container = this.findContainer(e.currentTarget);
			var x = e.pageX;
			x = x - container.offset().left;

			//new
			e.stopPropagation();

			//execution
			container.on('mousemove', jQuery.proxy(this.eventMouseMove, this));
		},

		eventMouseOut : function (e){

			//new
			e.stopPropagation();
			var container = this.findContainer(e.currentTarget);
			container.off('mousemove');
			//execution
			this.updateRatingContainer();
		},

		eventMouseMove : function (e){

			var x = e.pageX;
			//new
			e.stopPropagation();

			var container = this.findContainer(e.currentTarget);
			x = x - container.offset().left;

			//execution
			var selectedUnit = container.determineClickedRatingUnit(x);
			container.highlightUnit(selectedUnit);
		},

		/*************   rating   ****************/
		updateRatingContainer : function (){
			var that=this;
			jQuery.each(this.allRelatedContainer, function(){
				this.updateSpreadingLayer();
				this.updateTooltip();
				this.updateTotalCount();
			});
		},

		findContainer : function(elem)
		{
			var container = null;
			jQuery.each(this.allRelatedContainer, function(){
				if(this.is(elem)){
					container = this; 
				}
			});
			return container;
		},

		/*************   Server Interaction (AJAX)  ****************/
		refreshJSONData : function()
		{
			var responseHandler = this;
			var responseChain ={};

			this.getUserRatingFromServer(responseChain,responseHandler,this.getTotalRatingFromServer);
		},



		getUserRatingFromServer : function(responseChain, responseHandler, nextInChain)
		{
			var ratingUrl = responseHandler.getRatingUrl();

			jQuery.ajax({
				url: ratingUrl,
				type: "get",
				dataType: "json",
				cache: false
			}).done(function(response){
				if(response !==null){
					jQuery.extend(responseChain,response);
				}
				//performance reason (JSON.stringify)
				if(responseHandler.settings['debug'])
					responseHandler.log('getUserRatingFromServer Response: '+JSON.stringify(response));

				nextInChain(responseChain,responseHandler,responseHandler.updateRatingFromServer);

			}).fail(function(responseRating){
				responseHandler.log("getUserRatingFromServer failed");
			});

		},

		getTotalRatingFromServer : function(responseChain, responseHandler, nextInChain)
		{
			var totalRatingUrl = responseHandler.getTotalRatingUrl();

		 	jQuery.ajax({
				url: totalRatingUrl,
				type: "get",
				dataType: "json",
				cache: false
			}).done(function(response)
			{
				//performance reason (JSON.stringify)
				if(responseHandler.settings['debug'])
					responseHandler.log('getTotalRatingFromServer Response: '+JSON.stringify(response));

				jQuery.extend(responseChain,response);
				nextInChain(responseChain,responseHandler);

			}).fail(function(responseTotalRating){
				responseHandler.log("getTotalRatingFromServer failed");
            });
		},


		sendRatingToServer : function(container){
			
			if(!container.currentSelectionInPercent){
				this.settings['successfulRatingCallback'](null);
				return;
			}	
			
			var that = this;
			var url = this.getRatingUrl();
			jQuery.ajax({
				url: url,
				type: "POST",
				dataType: "json",
				data: { value: container.currentSelectionInPercent}
			}).done(function(response){

				if(that.settings['debug']){
					that.log('sendRatingToServer Response: '+JSON.stringify(response));
				}				

				response.value = container.currentSelectionInPercent;
				that.log("rating succeeded: new rating is "+response.totalValue);
				
				jQuery.proxy(this.eventMouseMove, this)
				
				that.updateRatingFromServer(response);
				
				that.settings['successfulRatingCallback'](response);

             }).fail(function(response){
            	 that.log("sendRatingToServer failed");
            	 that.settings['failedRatingCallback'](response);
             });
		},

		updateRatingFromServer : function(serverResponse){
			var that = this;
			jQuery.each(this.allRelatedContainer,function(){
				var newRatingData = this.collectConfiguration();
				newRatingData['current-user-rating-in-percent'] = serverResponse.value;
				newRatingData['total-rating-in-percent'] = serverResponse.totalValue;
				newRatingData['total-count-of-ratings'] = serverResponse.numberOfRatings;
				//ie caching problem
				newRatingData['rating-url']=that.getRatingUrl();
				
				if(that.settings['debug']){
					that.log('Resp: '+JSON.stringify(serverResponse));
				}
				this.overwriteConfiguration(newRatingData);
			});

			this.updateRatingContainer();
		},

		getRatingUrl : function()
		{
			return this.currentRatingContainer.ratingData['rating-url'];
		},

		getTotalRatingUrl : function()
		{
			return this.currentRatingContainer.ratingData['total-rating-url'];
		},

		/************** Logging **************/
		log : function(msg){
			if(this.settings['debug']){
				if(window.console && window.console.log){
					console.log(msg);
				}
			}
		}
	};

	(Container = function(elem,options){
		this.jQueryContainer = jQuery(elem);
		this.settings = jQuery.extend({}, this.defaultValues, options || {});
		this.elem = elem;
		this.ratingData = {};
		this.loadConfiguration();
		this.log('Create Container: ID->'+this.ratingData['rating-id']);

	}).prototype ={
			container : null,
			jQueryContainer : null,
			ratingData : null,
			elem:null,
			currentSelectionInPercent:null,

			defaultValues : {
				'debug' : false
			},

			/*** Configuration ***/
			loadConfiguration : function() {
				jQuery.extend(this.ratingData, this.collectConfiguration());

				//performance reason (JSON.stringify)
				if(this.settings['debug'])
					this.log('Loaded Configuration: '+JSON.stringify(this.ratingData));
			},

			collectConfiguration : function(){
				var data =  this.jQueryContainer.data('rating-json');


				var totalRatingUrl = data['rating-url'];
				totalRatingUrl = totalRatingUrl.slice(0,totalRatingUrl.lastIndexOf("/"));
				data['total-rating-url'] = totalRatingUrl;

				data['timestamp'] =  this.jQueryContainer.data('rating-timestamp');

				return data;
			},

			overwriteConfiguration : function (newRatingData)
			{
				//performance reason (JSON.stringify)
				if(this.settings['debug'])
					this.log('Overwrite Configuration with: '+JSON.stringify(newRatingData));

				this.jQueryContainer.data('rating-json', newRatingData);
				this.loadConfiguration();
			},

			/** UI */
			determineClickedRatingUnit : function(x){
				// max value
				if(x > this.jQueryContainer.width()){
					return this.ratingData['number-of-rating-units'];
				}
				//min value
				if(x<=0){
					return 1;
				}

				return Math.ceil(x / this.getRatingUnitWidth());
			},

			getRatingUnitWidth : function (){
				return  this.jQueryContainer.width() / this.ratingData['number-of-rating-units'];
			},

			convertRatingUnitToPercent : function (selectedUnit){
				var widthOfOnePercent = this.jQueryContainer.width() / 100;
				var widthOfSelectedUnits = selectedUnit * this.getRatingUnitWidth();
				var ratingInPercent = Math.floor(widthOfSelectedUnits / widthOfOnePercent);

				if(ratingInPercent < 0) { ratingInPercent = 0; }
				if(ratingInPercent > 100) { ratingInPercent = 100; }

				return ratingInPercent;
			},

			highlight : function (highlight){
				var spreadLayer = this.findSpreadingLayer();
				if(spreadLayer === undefined || spreadLayer === null){
					return;
				}

				var standardStyleClass = this.ratingData['class-spreading-standard'];
				var highlightedStyleClass =  this.ratingData['class-spreading-highlight'];

				if(highlight){
					if(spreadLayer.hasClass(standardStyleClass)){
						spreadLayer.removeClass(standardStyleClass);
					}
					if(!spreadLayer.hasClass(highlightedStyleClass)){
						spreadLayer.addClass(highlightedStyleClass);
					}
				}else{
					if(spreadLayer.hasClass(highlightedStyleClass)){
						spreadLayer.removeClass(highlightedStyleClass);
					}
					if(!spreadLayer.hasClass(standardStyleClass)){
						spreadLayer.addClass(standardStyleClass);
					}
				}
			},

			highlightUnit : function (selectionUnit){

				var percentPerUnit = 100 / this.ratingData['number-of-rating-units'];
				var widthToHighlightInPercent = percentPerUnit * selectionUnit;

				this.findSpreadingLayer().css({'width' : widthToHighlightInPercent + '%'});
				this.highlight(true);
			},

			/** Find methods */
			findSpreadingLayer : function (){
				return 	this.jQueryContainer.find('.'+this.ratingData['class-spreading']);
			},

			findImageLayer : function (){
				return 	this.jQueryContainer.find('.'+this.ratingData['class-image']);
			},

			findTotalCountLayer : function()
			{
				var entityUUID = this.ratingData['rating-id'].split('_')[1];
				return this.jQueryContainer.parent().find('#rating_totalcount_' + entityUUID);
			},

			/**Update methods*/
			updateSpreadingLayer : function()
			{
				this.highlight(false);
				var valueInPercent = this.determineRatingValueForDisplay();
				var spreadingLayer = this.findSpreadingLayer();
				if(spreadingLayer){
					spreadingLayer.css({'width' : valueInPercent + '%'});
				}
			},

			 determineRatingValueForDisplay : function (){
					showRatingValueMode = this.ratingData['rating-display-mode'];
					if( showRatingValueMode === undefined || showRatingValueMode === 'total'  ){
						return this.ratingData['total-rating-in-percent'];			}
					else if( showRatingValueMode === 'user' ){
						return this.ratingData['current-user-rating-in-percent'];
					}
					return 0;
			},

			updateTooltip : function(){

				var imageLayer = this.findImageLayer();
				var tooltipText = this.ratingData['tooltip-text'];
				var userRating =  this.ratingData['current-user-rating-in-percent'];
				var totalRating = this.ratingData['total-rating-in-percent'];

				if(!userRating || userRating == 0){ userRating = '-'; }
				if(!totalRating || totalRating == 0){ totalRating = '-'; }

				tooltipText = tooltipText.replace('\[user\]', userRating);
				tooltipText = tooltipText.replace('\[total\]', totalRating);
				imageLayer.attr('title',tooltipText);
			},

			updateTotalCount : function(){
				var totalCountContainerElement = this.findTotalCountLayer();
				if(totalCountContainerElement){
					totalCountContainerElement.html(this.ratingData['total-count-of-ratings']);
				}
			},

			/** jQuery-Delegates*/
			on : function (name, func)
			{
				if(!this.ratingData['read-only']) {
					this.jQueryContainer.on(name, func);
				}
			},

			off : function(name)
			{
				this.jQueryContainer.off(name);
			},

			offset : function()
			{
				return this.jQueryContainer.offset();
			},

			is : function(elem)
			{
				return this.jQueryContainer.is(elem);
			},

			/************** Logging **************/
			log : function(msg){
				if(this.settings['debug']){
					if(window.console && window.console.log){
						console.log(msg);
					}
				}
			}
	};


	jQuery.fn.rating = function(options) {
		return this.each(function() {
			var element = jQuery(this);
			// Return early if this element already has a plugin instance
			if (element.data('rating')) {
				return;
			}
			// Store plugin object in this element's data
			element.data('rating', new Rating(this, jQuery.extend({}, options)));
		});
	};
})();
