/**
 * A Simple Last.fm application
 *
 * @package  LastFM
 * @author   madari
 */
(function($){
window.LastFM = {
	// templates
	templates: {
		recentItemArtist: new IG.Template('<dt>#{header}</dt>'), // recents - artist name
		recentItemTrack: new IG.Template('<dd class="#{nowplaying}"><span class="value">#{value}</span> #{header}</dd>'), // recents - track name
		itemBox: new IG.Template('<div class="item-container">#{img}<div class="artist-content"><span class="rank">#{rank}</span> <strong>#{header}</strong><p>#{subheader}<span class="value">#{value}</span></p></div></div>'), // right side, single item
		img: new IG.Template('<div class="artist-image"><div>#{img}</div></div>'), // right side - image
		noData: new IG.Template('<p class="nodata"><img src="/img/bashi/lastfm_logo_gray.gif" /><br />#{error}</p>') // when things go bad
	},
	
	// view definitions
	views: {
		recenttracks: 'recenttracks',
		toptracks: 'toptracks',
		topartists: 'topartists'
	},
	
	// animation definitions
	animation: {
		none: 'none',
		swipeLeft: 'left',
		swipeRight: 'right',
		swipeUp: 'up',
		swipeDown: 'down'
	},
	
	// redirs
	redirs: {
		track: '/redir.php?campaign=lastfm&link=lastfmtrack&url=',
		artist: '/redir.php?campaign=lastfm&link=lastfmartist&url='
	},
	
	currentView: null, // holds the current view element
	noImageSrc: '/img/bashi/lastfm_placeholder.gif', // placeholder image when url not provided
	initialized: false, // internal
	rightSize: 6, // items on the right side
	animationLock: false, // internal
	leftSmartHeight: 255, // cut off at this height
	data: [], // holds json data from last.fm
	viewStorage: new Array(), // cache of all  views
	
	// set the outermost wrapper (the elements will be build inside here)
	setContainer: function(el) {
		LastFM.container = $(el);
	},

	// set data for view (see LastFM.views, ie. LastFM.setData(LastFM.views.recenttracks, dataFromLastFm))
	setData: function(view, data) {
		if (data && data.lfm && data.lfm[view] && (data.lfm[view].track || data.lfm[view].artist)) {
			var temp;
			if (data.lfm[view].track && !data.lfm[view].track.indexOf) {
				temp = new Array();
				temp.push(data.lfm[view].track);
				data.lfm[view].track = temp;
			} else if (data.lfm[view].artist && !data.lfm[view].artist.indexOf) {
				temp = new Array();
				temp.push(data.lfm[view].artist);
				data.lfm[view].artist = temp;
			}
			LastFM.data[view] = data.lfm[view];
		}
	},
	
	// must be called after data has been set using setData
	init: function() {
		if (LastFM.initialized) {
			return;
		}
		
		/***** DOM *****/
		
		// create main elements
		LastFM.leftContainer  = $('<div class="left-container" />');
		LastFM.leftHeader     = $('<h4 />').html(IG.translate('Recently played'));
		LastFM.leftItems      = $('<div class="items" />');
		LastFM.rightContainer = $('<div class="clear right-container" />');
		LastFM.rightHeader    = $('<h4 />').html(IG.translate('Favorites'));
		LastFM.rightItems     = $('<div class="items clear" />');

		// create menu "pills"
		LastFM.rightPills = $('<ul />');
		LastFM.rightPills.append($('<li rel="topartists" />').append($('<a />').html(IG.translate('artists'))));
		LastFM.rightPills.append($('<li rel="toptracks" />').append($('<a />').html(IG.translate('tracks'))));
		LastFM.rightPills.find('li[rel=topartists] a').click(function cbClick() {
			if (LastFM.currentView.lastfm_view != LastFM.views.topartists) {
				LastFM.swapView(LastFM.views.topartists, LastFM.animation.swipeRight, 0);
			} return false;
		});
		LastFM.rightPills.find('li[rel=toptracks] a').click(function cbClick() {
			if (LastFM.currentView.lastfm_view != LastFM.views.toptracks) {
				LastFM.swapView(LastFM.views.toptracks, LastFM.animation.swipeLeft, 0);
			}
			return false;
		});
		LastFM.rightPillsContainer = $('<div class="pills" />');

		// inject the pills
		LastFM.rightPillsContainer.append(LastFM.rightPills);
		
		// inject stuff to the main columns
		LastFM.leftContainer.append(LastFM.leftHeader);
		LastFM.leftContainer.append(LastFM.leftItems);
		LastFM.rightContainer.append(LastFM.rightHeader);
		LastFM.rightContainer.append(LastFM.rightPillsContainer);
		LastFM.rightContainer.append(LastFM.rightItems);

		// inject whole stuff to wrapper
		LastFM.container.append(LastFM.leftContainer);
		LastFM.container.append(LastFM.rightContainer);
		LastFM.container.append($('<div style="clear: both" />'));
		
		// render left column (recently played)
		LastFM.renderRecent();
		
		// render right column with top artists
		LastFM.swapView(LastFM.views.topartists, LastFM.animation.none, 0);
		LastFM.updatePagination();
		LastFM.initialized = true;
	},
	
	sanitizeAttr: function(attr) {
		return (typeof attr === 'string') ? escapeHTML(attr).replace(/"/g, '&quot;') : '';
	},
	
	sanitizeImageURL: function(attr) {
		return /^(http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]))?$/.test(attr) ? attr : LastFM.noImageSrc;
		
	},
	
	// creates a html image-element (string) based on img template and given src (img source), url (link), alt and redir
	imgLinkify: function(src, url, alt, redir) {
		var link_url = redir ? redir + encodeURIComponent(url) : encodeURIComponent(url);
		var img = '<img title="' + LastFM.sanitizeAttr(alt) + '" alt="' + LastFM.sanitizeAttr(alt) + '" width="64" src="' + LastFM.sanitizeImageURL(src) + '" />';
		return LastFM.templates.img.evaluate({ 'img': url ? '<a target="_blank" href="' + link_url + '">' + img + '</a>' : img });
	},

	// creates a html link-element (string) based on given text (link text), url (link), alt and redir	
	linkify: function(text, url, alt, redir) {
		if (!text) {
			text = '';
		}
		
		var link_url = redir ? redir + encodeURIComponent(url) : encodeURIComponent(url);
		return url ? '<a title="' + LastFM.sanitizeAttr(alt) + '" target="_blank" href="' + link_url + '">' + escapeHTML(text) + '</a>' : escapeHTML(text);
	},
	
	// converts a UTC time to more human readable version
	formatTime: function(uts) {
		var time = new Date();
		time.setTime(uts * 1000);
		var currTime = new Date();
        
		if(currTime.getDate() == time.getDate() && currTime.getMonth() == time.getMonth() && currTime.getFullYear() == time.getFullYear()) {
			// todo: removed toPaddedStrings generate jQuery replacer
			return time.getHours() + ':' + time.getMinutes();
		}
		
		if(currTime.getFullYear() > time.getFullYear()) {
			return time.getDate() + '.' + (time.getMonth() + 1) + '.' + (new String(time.getFullYear())).substr(2, 2);
		} else {
			return time.getDate() + '.' + (time.getMonth() + 1) + '.';
		}
	},
	
	// renders pagination
	updatePagination: function() {
		if (!LastFM.paginationDrawn) {
			LastFM.paginationWrapper= $('<div id="lastfm-pagination" />');
			LastFM.previousPageButton = $('<a class="up" />').html('&nbsp;');
			LastFM.previousPageButton.click(function cbClick() { LastFM.previousPage(); return false; });
			LastFM.nextPageButton = $('<a class="down" />').html('&nbsp;');
			LastFM.nextPageButton.click(function cbClick() { LastFM.nextPage(); return false; });
			LastFM.paginationWrapper.append(LastFM.previousPageButton);
			LastFM.paginationWrapper.append(LastFM.nextPageButton);
			LastFM.rightContainer.append(LastFM.paginationWrapper);
			LastFM.paginationDrawn = true;
		} else {
			LastFM.nextPageButton.toggleClass('disabled', !LastFM.hasNextPage());
			LastFM.previousPageButton.toggleClass('disabled', !LastFM.hasPreviousPage());
		}
	},
	
	// checks if the current view has any more pages
	hasNextPage: function() {
		return !(!LastFM.currentView || !LastFM.data[LastFM.currentView.lastfm_view] ||
			(LastFM.currentView.lastfm_view == LastFM.views.topartists && LastFM.data[LastFM.currentView.lastfm_view].artist.length <= LastFM.rightSize * (LastFM.currentView.lastfm_page + 1)) ||
			(LastFM.currentView.lastfm_view == LastFM.views.toptracks && LastFM.data[LastFM.currentView.lastfm_view].track.length <= LastFM.rightSize * (LastFM.currentView.lastfm_page + 1))
		);
	},
	
	// checks if the current view has previous pages
	hasPreviousPage: function() {
		return !(!LastFM.currentView || !LastFM.currentView.lastfm_page || LastFM.currentView.lastfm_page == 0);
	},
	
	// go to next page
	nextPage: function() {
		if (!LastFM.hasNextPage()) {
			return;
		}
		LastFM.swapView(LastFM.currentView.lastfm_view, LastFM.animation.swipeUp, LastFM.currentView.lastfm_page + 1);
	},
	
	// go to previous page
	previousPage: function() {
		if (!LastFM.hasPreviousPage()) {
			return;
		}
		LastFM.swapView(LastFM.currentView.lastfm_view, LastFM.animation.swipeDown, LastFM.currentView.lastfm_page - 1);
	},
	
	// renders the recently played box
	renderRecent: function() {
		if (!LastFM.data[LastFM.views.recenttracks] || !LastFM.data[LastFM.views.recenttracks].track || LastFM.data[LastFM.views.recenttracks].track.length < 1) {
			LastFM.leftItems.append(LastFM.templates.noData.evaluate({ error: IG.translate('Wrong username or feed temporarily unavailable') })); // epic fail
			return;
		}

		var last_artist_id = null;
		var dl = $('<dl class="quickstats" />'); // yeah, we're using a definition list here, sorry everyone
		LastFM.leftItems.append(dl);

		$.each(LastFM.data[LastFM.views.recenttracks].track, function itTrack(i, item) { // iterate through recent items
			if (!item.artist || !item.name) {
				return false; // we've had enough
			}
			
			var artist_id = item.artist['@mbid'] || item.artist['#text']; // get the best possible artist id (used for aggregating)

			if (artist_id != last_artist_id) { // seems like previous artist was different than this one, append a new dt-element
				dl.append(LastFM.templates.recentItemArtist.evaluate({
					header: LastFM.linkify(item.artist['#text'] ? item.artist['#text'].smartTruncate(210) : null, item.artist.url, item.artist['#text'], LastFM.redirs.artist ? LastFM.redirs.artist : null)
				})); // append a dt
				last_artist_id = artist_id;
			}

			dl.append(LastFM.templates.recentItemTrack.evaluate({ // append a dd for the track
				header: LastFM.linkify(item.name.smartTruncate(300), item.url ? item.url : null, item.artist['#text'] ? item.artist['#text'] + ' - ' + item.name : item.name, LastFM.redirs.track ? LastFM.redirs.track : null), // track name
				value: ('true' == item['@nowplaying']) ? IG.translate('now') : LastFM.formatTime(item.date['@uts']), // time played
				nowplaying: 'true' == item['@nowplaying'] ? 'nowplaying' : '' // unused, but let's pass it on anyway
			}));

			if (dl.height() > LastFM.leftSmartHeight) {
				$(dl).children().last().remove();
				if ($(dl).children().last()[0].tagName == 'DT') {
					$(dl).children().last().remove();
				}

				return false; // we've had enough
			}
		}.bind(this));
	},
	
	// renders the right column view
	swapView: function(view, animation, page) {
		if (!LastFM.viewStorage[view]) { // make sure the cache is ready
			LastFM.viewStorage[view] = new Array();
		}

		if (LastFM.animationLock) { // if we are already animating something, break
			return;
		}

		LastFM.rightPills.find('li').removeClass('selected'); // deselect all pills (menu)
		LastFM.rightPills.find('li[rel=' + view + ']').addClass('selected'); // select the pill according to this view

		if (LastFM.currentView) {
			if (LastFM.currentView.lastfm_view && LastFM.currentView.lastfm_view == view && LastFM.currentView.lastfm_page && LastFM.currentView.lastfm_page == page) {
				return; // break if the current view is the same as requested
			} else if (LastFM.currentView.lastfm_view) {
				LastFM.viewStorage[LastFM.currentView.lastfm_view][LastFM.currentView.lastfm_page] = LastFM.currentView; // store the current view in cache
			} else {
				LastFM.currentView.remove(); // something weird was in the view, start from scratch
				LastFM.currentView = null;
			}
		}

		var newView;
		if (LastFM.viewStorage[view][page]) {
			newView = LastFM.viewStorage[view][page]; // use cached version of the view if available
		} else {
			newView = $(document.createElement('div')); // create a new view
			newView.lastfm_view = view;
			newView.lastfm_page = page;
			newView.css({ width: LastFM.rightItems.width() + 'px', 'height': LastFM.rightItems.height() + 'px', position: 'absolute' }); // make the view the same size as the container
			
			if ((view == LastFM.views.topartists && LastFM.data[LastFM.views.topartists] && LastFM.data[LastFM.views.topartists].artist) ||
				(view == LastFM.views.toptracks && LastFM.data[LastFM.views.toptracks] && LastFM.data[LastFM.views.toptracks].track)) { // just a lot of sanity checks :-*)
				var data = view == LastFM.views.topartists ? LastFM.data[view].artist : LastFM.data[view].track; // choose right data

				var i;
				for (i = page * LastFM.rightSize; i < data.length && i < (page + 1) * this.rightSize; i++) { // paginate
					var item = data[i]; // single data item (track or artists)
					
					var redir = view == LastFM.views.topartists ? LastFM.redirs.artist : LastFM.redirs.track;
					
					var conversion = { // prepare the conversion from template to view
						img: LastFM.imgLinkify((item.image && item.image[1]) ? item.image[1]['#text'] : null, item.url, item.artist ? item.artist.name + ' - ' + item.name : item.name, redir),
						header: LastFM.linkify(item.name.smartTruncate(200), item.url, item.artist ? item.artist.name + ' - ' + item.name : item.name, redir),
						subheader: item.artist ? escapeHTML(item.artist.name.smartTruncate(270)) : null,
						value: parseInt(item.playcount) + ' ' + (item.playcount < 2 ? IG.translate('play') : IG.translate('plays')),
						rank: item['@rank'] ? parseInt(item['@rank']) + '.' : null
					};
					newView.append(LastFM.templates.itemBox.evaluate(conversion)); // evaluate the item and inject it to the view
				}
			} else {
				newView.append(LastFM.templates.noData.evaluate({ error: IG.translate('Wrong username or feed temporarily unavailable') })); // sanity checks failed, use noData template
			}
			
			LastFM.viewStorage[view][page] = newView; // view has been rendered - store it in the cache
		}
		
		if (!LastFM.currentView || animation == LastFM.animation.none) { // no animation
			if (LastFM.currentView) {
				LastFM.currentView.remove(); // remove the current view
				LastFM.currentView = null;
			}
			LastFM.rightItems.append(newView); // inject the new view
			LastFM.updatePagination(); // refresh pagination
		} else { // animation time!

			newView.css({ left: 0, top: 0 + 'px' }); // move the new view to it's starting position

			LastFM.animationLock = true; // set the animation lock
			LastFM.rightItems.append(newView); // inject the new view

			jQuery(newView).fadeIn('slow');
			jQuery(LastFM.currentView).fadeOut('slow', function cbFadeout() {
				$(this).remove();
				LastFM.updatePagination();
				LastFM.animationLock = false;
			});
		}

		LastFM.currentView = newView; // we're done!
	}
};

})(jQuery);

