/**
 * @author Gary Manfredi
 */
// Genre Profile View
function _GenreProfileView(rootDiv, callback) {
	// create base objects
	this.display = $(rootDiv);
	this.model = null; // deferred to initialize function
	this.callback = callback;
	this.currentGenre = 0;
}

// initialize a new model
_GenreProfileView.prototype.initialize = function() {
	this.model = new GenreProfileModel();
	this.setModel(this.model);
}

// manually set a model
_GenreProfileView.prototype.setModel = function(aModel) {
	this.model = aModel;
	this.model.addObserver(this);
}

// reset the browser
_GenreProfileView.prototype.update = function() {
	// empty previous elements
	this.display.empty().append("<ol class='_gp'></ol>");

	// add title to pane
	// this.display.find("ol").prepend('<li class="title">Your Genres</li>');

	// now sort the new array by alphabetically
	var me = this;
	var genreProfile = this.model.genreProfile.slice(0);
	genreProfile.sort(function(a, b) {
		return (a.name > b.name) * 2 - 1;
	});

	// add to display
	var li;
	$.each(genreProfile, function(i, data) {
		// create genre entry with behaviors
		li = $('<li><a href="javascript:void(0)">' + data.name + '</a></li>')
			.data("id", data.id).css({
				"fontSize" : (85 + (data.weight * 40) + "%")
				})
			.click(function() {
					// remove any previously selected
					$(".selected", me.display).removeClass("selected");

					// select current line
					$(this).addClass("selected");
					
					// set current genre
					me.currentGenre = data.id;

					// do callback
					me.callback({
						id : data.id,
						name : data.name
					});
				})
			.hover(	function() {
				$(this).addClass("highlight");
				}, function() {
					$(this).removeClass("highlight");
				});
					
					
			// retain selected genre from before
			if (li.data("id") == me.currentGenre) li.addClass("selected");

			// append to list
			me.display.find("ol").append(li);

		});

	// if there are not many genres stored, show a message.
	if (genreProfile.length < 10)
		this.display
				.append($('<p><small>(For more "My Genres", vote on more albums.  See <a href="javascript:void(0)">All Genres</a>)</small></p>')
						.find("a").click(function() {
							$("ul.panelTabs").tabsClick(1);
						}).end());
}

// listen for changes from a model
_GenreProfileView.prototype.broadcast = function(eventStr) {
	if (eventStr == "genreProfile") {
		// if My Genres not selected, potentially highlight My Genres tab if new
		// amount >= 7 and previous < 7
		if ($("ul.panelTabs").data('selected.tabs') == 0) {
			if ((this.model.genreProfile.length >= 7)
					&& ($("li", this.display).length < 7))
				$("ul.panelTabs li").eq(1).animate({
							backgroundColor : "#ffffcc"
						}, 1000);
		}
		this.update();
	}
}

// set the genre to be visually selected (does not trigger action)
_GenreProfileView.prototype.setSelectedGenre = function(genreId){
	$("li", this.display).each(function(){
		var item = $(this);
		if(item.data("id") == genreId) item.addClass("selected");
		else item.removeClass("selected");
	});
}


/*
 * GenreProfileView for myVotes -- adds some display functionality for MyVotes tab
 */
function _MyVotesGenreProfileView(rootDiv, callback){
	_MyVotesGenreProfileView.baseConstructor.call(this, rootDiv, callback);
	
}

// subclass _GenreProfileView
OopExtend(_MyVotesGenreProfileView, _GenreProfileView);

_MyVotesGenreProfileView.prototype.update = function(){
	// first call superclass update function
	_MyVotesGenreProfileView.superClass.update.call(this);
	
	// create All Genres entry with behaviors
	var me = this;
	var li = $('<li><a href="javascript:void(0)">All Votes</a></li>')
		.data("id", 0).css({
			"fontSize" : ("110%")
			})
		.click(function() {
				// remove any previously selected
				$(".selected", me.display).removeClass("selected");

				// select current line
				$(this).addClass("selected");
					
				// set current genre
				me.currentGenre = 0;

				// do callback
				me.callback({
					id : 0,
					name : "All Votes"
				});
			})
		.hover(	function() {
			$(this).addClass("highlight");
			}, function() {
				$(this).removeClass("highlight");
			});
			
	// retain selected genre from before
	if (li.data("id") == this.currentGenre) li.addClass("selected");
	/*if (li.text() == $(".selected", this.display).text())
		li.addClass("selected");*/

	// add "All Genres" link to list
	this.display.find("ol").prepend(li); // .prepend('<li class="title">Your Genres</li>');
	
	// remove relative sizing to distinguish from Discover's list
	$("ol._gp li", this.display).each(function(i){
		if(i) $(this).css({ "fontSize": "100%" });
	});
}



/*
 * Genre Profile Model
 */

function GenreProfileModel() {
	this._gProfileWork = {}; // set to object b/c calcGenreProfile is called on stopStepping's
	this._numPreviousGenres = 0;
	this.genreProfile = [];
	this.observers = [];
}

// get Genre Profile from storage if any
GenreProfileModel.prototype.loadGenreProfile = function() {
	// get stored genreProfile, if any
	var me = this;
	Model.loadJSON("genreProfile", function(data) {
		me._finishLoadGenreProfile(data);
	});
}

GenreProfileModel.prototype._finishLoadGenreProfile = function(data) {
	// if no data or empty data or not correct type, reset it all
	if (!data || (data.length == 0) || (data[0] instanceof Array)
			|| !(data[1] instanceof Array)) {
		this.genreProfile = [];
		this._gProfileWork = {};
		Controller.votedirty = false;
		Controller.myVotesBackground();
	} else {
		// decode stored construct into full genreProfile json
		this.genreProfile = [];
		this._gProfileWork = {};
		var me = this;
		var max = data[0]; // used for scaling up items into gProfileWork
		for (var i = 1; i < data.length; i++) {
			var gid = data[i][0];
			var weight = data[i][1];
			var genre = Model.genreBrowserModel.getGenre(gid);

			// recreate genre profile
			if(genre.name != "All Genres"){
				me.genreProfile.push({
					id : gid,
					name : genre.name,
					weight : weight
				});

				// recreate gProfileWork which is used for per vote weighing
				var obj = {
					id : gid,
					name : "",
					weight : 0
				};
				this._gProfileWork[gid] = obj;
				obj.name = genre.name;
				obj.weight = weight * max;
			}
		}

		// if there is a significant genreProfile, show that by default
		if (this.genreProfile.length >= 7)
			$("ul.panelTabs").tabs("select", 1);
	}

	// broadcast change in genreProfile to any listeners (genreProfile.js)
	this.broadcast("genreProfile");
}

// weigh the given genre id into the overall genre profile
GenreProfileModel.prototype.weighGenre = function(gid, vote) {
	// get genre from genre browser
	var genre = Model.genreBrowserModel.getGenre(gid);
	
	// if genre name exists and it's not "General" and its not "All Genres", weigh it in
	if (genre.name && (genre.name.slice(-7) != "General") && (genre.name != "All Genres")) {

		// get/create genre profile entry
		var profileEntry = this._gProfileWork[gid];
		
		if (!profileEntry) {
			profileEntry = {
				id : gid,
				name : "",
				weight : 0
			};
			this._gProfileWork[gid] = profileEntry;

			// set name
			profileEntry.name = genre.name;

		}

		// weigh according to vote, weighing positive votes more than negative
		profileEntry.weight += Math.max(parseFloat(vote) - 2, -1);
	}
}

// called at the end of MyVotes load to calculate the genre profile on all the
// weighted genres
GenreProfileModel.prototype.calcGenreProfile = function(num) {
	// remember old number of my genres
	var numPrevGenres = this.genreProfile.length;

	// convert object into array
	var me = this;
	this.genreProfile = [];

	// copy genres info into a normal array, and track max
	var max = 0
	$.each(this._gProfileWork, function() {
		max = Math.max(this.weight, max);
		me.genreProfile.push($.extend({}, this))
	});

	// normalize weight between (0,1) and rounded to 3 decimal places
	$.each(this.genreProfile, function() {
		this.weight = Math.round(this.weight / max * 1000) / 1000;
	});

	// sort array descending by weight & take top X AMOUNT
	this.genreProfile.sort(function(a, b) {
		return (b.weight - a.weight);
	});
	this.genreProfile = this.genreProfile.slice(0, (num || 24));

	/*
	 * debug var temp = []; $.each(this._gProfileWork, function(){
	 * temp.push($.extend({}, this)) }); temp.sort(function(a, b){ return
	 * (b.weight - a.weight); }); console.log(temp);
	 */

	// store genre profile minus the names to compact storage with max as first
	// entry
	var gp = [max];
	$.each(this.genreProfile, function() {
		gp.push([this.id, this.weight]);
	});
	Model.saveJSON("genreProfile", gp);
	
	// broadcast change in genreProfile to any listeners (genreProfile.js)
	this.broadcast("genreProfile");
}

// public method to add an observer
GenreProfileModel.prototype.addObserver = function(obj) {
	if ($.inArray(obj, this.observers) == -1)
		this.observers.push(obj);
}

// public method to remove a particular observer
GenreProfileModel.prototype.removeObserver = function(obj) {
	var n = $.inArray(obj, this.observers);
	if (n > -1)
		this.observers.splice(n, 1);
}

// broadcast an event to all observers
GenreProfileModel.prototype.broadcast = function(eventStr) {
	$.each(this.observers, function() {
				this.broadcast(eventStr);
			});
}
