//
//  RatePlayerWidget
//
//  Created by Cannon, Ryan on 2009-07-17.
//  Copyright (c) 2009 NFL Enterprises, LLC. All rights reserved.
//
nfl.namespace('GameCenter');
nfl.GameCenter.RatePlayerWidget = {
	/**
	 * define
	 * 
	 * delays definition of the class, its contants and its private
	 * methods until it is ready to be used, decreasing memory footprint
	 * 
	 * @static
	 */
	define: function() {
		if ( Object.isUndefined(RequestBatch) ) {
			throw new Error("This script requires Pluck");
		}
		if ( Object.isUndefined(nfl.sitelife.BatchManager) ) {
			throw new Error("This script requires sitelife.js");
		}
		var c = (function() {
			var PLAYER_ID_ATTR   = "data-player_id",
			    GAME_ID_ATTR     = "data-game_id",
			    CATEGORY_ATTR    = "data-category",
			    TEAM_ABBR_ATTR   = "data-team_abbr",
			    USER_RATED_ATTR  = "data-user_rated",
			    WIDGET_SELECTOR  = 'div.player-ratings-tag',
			    SCORE_SELECTOR   = "span.score",
			    VOTE_SELECTOR    = "p.vote",
			    SECTION_NAME     = "gcplayerratings",
			    RATING_ID        = "gcpr",
			    BM               = nfl.sitelife.BatchManager,
			    keyify           = nfl.sitelife.Keyify,
			    COLLAPSE_FIND    = /\s+/,
			    COLLAPSE_REPLACE = ' ',
			    TITLE_TEMPLATE   = new Template("#{name}||#{position}||#{stats}"),
			    DEFAULT_RATING   = 0;

			// sets the rating for the supplied view
			function setRatingView (_el, _rating) {
				_el.down().setStyle({ width: Math.round( _rating * _el.offsetWidth / 5 ) + 'px' });
			}

			// generates a pluck id from a div.player-ratings-tag
			function getPluckIdFromDiv(div) {
				return [ nfl.global.ENV, RATING_ID, div.getAttribute(GAME_ID_ATTR), div.getAttribute(PLAYER_ID_ATTR), div.getAttribute(CATEGORY_ATTR) ].join('-');
			}

			// sets the rating view to the highest star the mouse is over
			function onMouseMove (_e) {
				var score, scoreX, mouseX, rating;

				score  = _e.element().up();
				scoreX = score.cumulativeOffset().left;
				mouseX = _e.pointerX();
				rating = Math.ceil((mouseX - scoreX) / score.offsetWidth * 5 );
				setRatingView( score, rating );
				return rating;
			}

			// if we've moused over a rating block, start listening for mouse moves
			function onMouseOver (_e) {
				var score, div, from;
				_e.stop();
				div   = _e.findElement(WIDGET_SELECTOR);
				if ( ! div ) { return; }
				score = div.down(VOTE_SELECTOR);
				from  = $( _e.relatedTarget || _e.fromElement );
				if ( (! score) || ( from && Object.isFunction(from.descendantOf) && from.descendantOf( score ) ) ) { return; }
				score.observe('mousemove', onMouseMove);
			}

			// if we've moused out a rating block, stop listening for mouse moves
			function onMouseOut (_e) {
				var score, div, to, from, rating;
				_e.stop();
				div   = _e.findElement(WIDGET_SELECTOR);
				from  = _e.element();
				if (! div ) { return; }
				score = div.down(VOTE_SELECTOR);
				to    = $( _e.relatedTarget || _e.toElement );
				if (
					( !score ) ||
					(! from.descendantOf(score) ) ||
					( to  && ( to === score || ( to.descendantOf && to.descendantOf( score ) ) ) )
				) { return; }
				score.stopObserving('mousemove', onMouseMove);
				rating = parseInt( div.getAttribute(USER_RATED_ATTR) || 0, 10 );
				setRatingView( score, rating );
			}

			// if we've clicked on a rating block, send a rating to pluck
			// and stop listening for mouse moves
			function onClick (_e) {
				var el, div, score, rating;
				el    = _e.element();
				div   = _e.findElement(WIDGET_SELECTOR);
				if ( ! div ) { return; }
				score = div.down(VOTE_SELECTOR);
				if ( ! score || ! ( el === score || el.descendantOf( score ) ) ) { return; }
				rating = onMouseMove(_e);
				div.setAttribute(USER_RATED_ATTR, rating);
				BM.AddToRequest(new RateAction( new ArticleKey(getPluckIdFromDiv(div)), rating));
			}

			// remove event listeners and remove stored references
			function destroy (_e) {
				Event.stopObserving(window, 'unload', this.destroy);
				if ( ! this.el ) { return; }
				this.el.
					stopObserving('mouseover', onMouseOver).
					stopObserving('mouseout', onMouseOut).
					stopObserving('click', onClick);

				this.el = null;
			}

			// returns whether the supplied Section object is the same as a string section 
			function sectionEquivalent (pluckSection, pageSection) {
				return ( pageSection && pluckSection && pluckSection.Name === pageSection ) ||
				       ( Object.isUndefined(pluckSection) && ( Object.isUndefined(pageSection) || pageSection.blank() ) );
			}

			// requests data from pluck about ratings, and corrects it if necessary
			function setUpRatings(div) {
				var gameId   = this.gameId,
				    player   = div.getAttribute(PLAYER_ID_ATTR),
				    statCat  = div.getAttribute(CATEGORY_ATTR),
				    season   = this.season,
				    week     = this.week,
				    url      = this.url,
				    pluckId  = getPluckIdFromDiv(div);

				// Request the player's data from pluck. When we get it back...
				BM.AddToRequest( new ArticleKey( pluckId ), function(_response) {
					var article    = BM.findArticlePage(_response, pluckId),
				        team       = div.getAttribute(TEAM_ABBR_ATTR).toLowerCase(),
					    categories = [ season, week, gameId, player, team, statCat ].invoke('toLowerCase'),
					    title      = TITLE_TEMPLATE.evaluate({
							name:     div.down('.name').text(),
					    	stats:    div.down('.stats').text(),
					    	position: div.down('.position').text()
					    }).replace(COLLAPSE_FIND, COLLAPSE_REPLACE),
					    noArticle  = Object.isUndefined(article),
					    userRating = noArticle ? DEFAULT_RATING : parseFloat(article.Ratings.CurrentUserRating, 10);

					// If pluck data doesn't match what we have
					if (
						// if there's no article from pluck or
						noArticle ||
						// if the titles don't match or
						article.PageTitle !== title ||
						// if the urls don't match or
						article.PageUrl !== url ||
						// if the sections don't match or
						(! sectionEquivalent( article.Section, SECTION_NAME )) ||
						// if there aren't the same number of categories or
						article.Categories.length !== categories.length ||
						// if any of the pluck categories aren't in the local categories
						article.Categories.any(function(cat) { return categories.indexOf(cat.Name) === -1; })
					) {
						console.log('updating ' + pluckId);
						console.log('old', article ? ([ article.PageTitle, article.PageUrl, article.Section ? article.Section.Name : undefined, '(' + article.Categories.inject([], function(acc, cat) { acc.push(cat.Name); return acc; }).join(', ') + ')' ].join(', ')) : undefined);
						console.log('new', [ title, url, SECTION_NAME, '(' + categories.join(', ') + ')' ].join(', '));
						if ( article ) {
							console.log([ 'title: ' + (article.PageTitle !== title), 'url: ' + (article.PageUrl !== url), 'section: ' + (! sectionEquivalent( article.Section, SECTION_NAME )), 'catLength: ' + (article.Categories.length !== categories.length), 'cats: ' + article.Categories.any(function(cat) { return categories.indexOf(cat.Name) === -1; }) ].join(', '));
						}

						// send new data to Pluck
						BM.AddToRequest(
							new UpdateArticleAction(
								new ArticleKey(pluckId),
								url,
								title,
								new Section(SECTION_NAME),
								categories.map(keyify(Category))
							)
						);
					}
					// show the correct view.
					if (article) {
						setRatingView( div.down(SCORE_SELECTOR), parseFloat(article.Ratings.AverageRating, 10) );
						setRatingView( div.down(VOTE_SELECTOR), userRating );
						div.setAttribute(USER_RATED_ATTR, userRating);
					}
				});
			}

			return Class.create({
				/**
				 * Constructor
				 * 
				 * Creates an instance of the rating widget. In order to decrease
				 * memory footprint, this class uses only four event listeners
				 * regardless the number of defiend widgets, delegating mouse 
				 * events to child elements.
				 * 
				 * @param {object config} The configuration options.
				 */
				initialize: function(config) {
					
					this.el          = $(config.el);
					this.gameId      = config.gameId;
					this.season      = config.season;
					this.week        = config.week;
					this.teams       = config.teams.toLowerCase();
					this.url         = window.location.protocol + '//' + window.location.host + '/gamecenter/' + this.gameId + '/' + this.season + '/' + this.week + '/' + this.teams + '/rate';
					this.destroy     = destroy.bind(this);

					this.el.
						observe('mouseover', onMouseOver).
						observe('mouseout', onMouseOut).
						observe('click', onClick).
						select(WIDGET_SELECTOR).
						each(setUpRatings, this);
					Event.observe( window, 'unload', this.destroy );
				},
				destroy: Prototype.emptyFunction
			});
		}());
		// once defined, nfl.GameCenter.RatePlayerWidget.define() should do nothing
		// but it must still exist
		c.define = Prototype.emptyFunction;
		nfl.GameCenter.RatePlayerWidget = c;
	}
};
nfl.GameCenter.getComments = function(_config) {
	var ecm_id = _config.ecm_id,
	    AE = nfl.events.AuthenticationEvent,
	    Authentication = nfl.global.Authentication,
	    article = new nfl.news.Comments.ArticleController({
	       	ecm_id: ecm_id,
	    	headline: _config.headline,
	    	url: _config.url,
	    	section: 'Game Center',
	    	categories: _config.categories,
	        signed_in_messaging: _config.signedIn,
			signed_out_messaging: _config.signedOut,
			onNoComments: _config.onNoComments
	    }),
	    signInEl, onSignIn, onSignOut;
    
	if ( _config.comment_form && (! nfl.global.sitelife.comments.locked(_config.gameId))) {
		article.enableComments({ comment_form: _config.comment_form,
		                         is_comment_page: _config.is_comment_page,
		                         trackingName: 'GC_Comments_Submit' });
	}
	
	// if we have a signInEl, we have to listen for registrations, etc.
	if ( _config.signIn ) {
		nfl.news.Comments.TrackRegistrations();

		onSignIn  = function() { $(_config.signIn).hide(); };
		onSignOut = function() { $(_config.signIn).show(); };
		
		// sign-in links aren't getting the returnTo value
		
		$(_config.signIn).select('a').each(function(_el) {
			_el.href += encodeURIComponent(window.location);
		});
		
		document.observe( AE.SIGN_IN, onSignIn );
		document.observe( AE.SIGN_OUT, onSignOut );
		if ( Authentication.isAuthenticated() ) { onSignIn(); }

		document.observe('tab:content:destroy',function(_e){
			document.stopObserving(AE.SIGN_IN, onSignIn);
			document.stopObserving(AE.SIGN_OUT, onSignOut);
		});
	}
	
	// if we have a sort el, use that
	if (_config.sort) {
		article.showMetaData(_config.sort);
	}
	
	// show the comments
	article.showComments({
		container: _config.container,
		list: _config.list,
		report_form: _config.report_form,
		pagination: _config.pagination,
		batchRequests: true,
		padReportForm: Object.isUndefined(_config.padReportForm) ? true : _config.padReportForm 
	});
	// get the comments
	article.getData();
	
	// prepare to clean up everything
	document.observe('tab:content:destroy',function(_e){
		article.destroy();
		document.stopObserving('tab:content:destroy',arguments.callee);
	});
};
