// ************************************************************************************************
// *** Constructs an object that represents a playlist
// ************************************************************************************************

function Playlist() {
	// ************************************************************************************************
	// *** Interface
	// ************************************************************************************************	
	
	// Fields
	// ******
	this.id = 0;
	this.title = '';
	
	// Advertisements parameters
	this.adsFrequency = 0; // Advertisements frequency
	this.maxAdsFirst = 0; // Maximum number of advertisements before the first item (negative number for all ads)
	this.maxAds = 0; // Maximum number of advertisements before the media items (but the first, negative number for all ads)
	
	// Methods
	// *******
	this.appendItem = pl_AppendItem;
	this.clear = pl_Clear;		
	this.insertItem = pl_InsertItem;
	this.getDuration = pl_GetDuration;
	this.getItem = pl_getItem;
	this.getPlaySequence = pl_GetPlaySequence;
	this.getPlaySequenceIndex = pl_GetPlaySequenceIndex;
	this.indexOf = pl_IndexOf;
	this.count = pl_Count;
	this.moveItem = pl_moveItem;
	this.removeAt = pl_RemoveAt;
	this.setShuffle = pl_setShuffle;
	
	// Events
	this.onChange = new EventManager(); // Occurs when the number of items or them order changes
	
	// ************************************************************************************************
	// *** Implementation
	// ************************************************************************************************
	
	// Fields
	// ******
	
	this.curMediaIndex = null; // Used for shuffle
	this.mediaItems = new Array(); // The playlist items
	this.plItemsOrder = new Array();
	this.playSequence;
	this.shuffle = false;
	
	this.plIndexToPlaySequenceIndex = new Array;
	
	// Methods
	// *******
	this.genPlaySequence = genPlaySequence;
	
	// Methods implementation
	// **********************
	
	// Adds a media item to the end of the playlist
	function pl_AppendItem(mediaItem) {
		this.mediaItems.push(mediaItem);
		
		// Generate the play sequence
		this.genPlaySequence();
		
		// Trigger "onChange" event
		this.onChange.exec();
	}
	
	// Clears the playlist
	function pl_Clear() {
		this.mediaItems = new Array();
		
		// Generate the play sequence
		this.genPlaySequence();
		
		// Trigger "onChange" event
		this.onChange.exec();
	}
	
	// Returns the number of items of playlist
	function pl_Count() {
		return this.mediaItems.length;
	}
	
	// Returns the playlist duration 
	function pl_GetDuration() {
		var sum = 0, i;
		
		// Iterate over media items
		for(i = 0; i < this.mediaItems.length; i++)
			sum += this.mediaItems[i].duration;
			
		return sum;
	}
	
	// Returns the media index at passed index
	function pl_getItem(index) {
		return this.mediaItems[index];
	}
	
	// Receives a playlist index and returns the related play sequence index
	function pl_GetPlaySequenceIndex(plIndex) {
		return this.plIndexToPlaySequenceIndex[plIndex];
	}
	
	// Returns the index of passed media item in the playlist (not play sequence)
	function pl_IndexOf(mediaItem) {
		var i, j;
		
		// Search for the passed media item
		for(i = 0; i < this.mediaItems.length; i++) {
			if(mediaItem == this.mediaItems[i]) // Check if the wanted item is the current
				return i;
			else if(this.mediaItems[i].ads != null) { // Search for the item in ads
				for(j = 0; j < this.mediaItems[i].ads.length; j++)
					if(this.mediaItems[i].ads[j] == mediaItem)
						return i;
			}
		}
		
		return -1;
	}
	
	// Returns an array containing the media items of playlist sequence
	function pl_GetPlaySequence() {
		return this.playSequence;
	}
	
	// Inserts a media item into playlist at specified location
	function pl_InsertItem(index, mediaItem) 
	{
		this.mediaItems.splice(index, 0, mediaItem);
		
		// Generate the play sequence
		this.genPlaySequence();
		
		// Trigger "onChange" event
		this.onChange.exec();
	}
	
	// Changes the location of an item in the playlist
	function pl_moveItem(oldIndex, newIndex) {
		var temp;
		
		if(oldIndex == newIndex) return; // Do not exchange the same position
		
		// Removes from old position and save in a temporary variable
		temp = this.mediaItems.splice(oldIndex, 1)[0];
		
		// Insert in the new position
		this.mediaItems.splice(newIndex, 0, temp);
		
		// Generate the play sequence
		this.genPlaySequence();
		
		// Trigger "onChange" event
		this.onChange.exec();
	}
	
	// Removes a media item at the specified position
	function pl_RemoveAt(index) {
		this.mediaItems.splice(index, 1);
		
		// Generate the play sequence
		this.genPlaySequence();
		
		// Trigger "onChange" event
		this.onChange.exec();
	}
	
	// Sets a flag indicating whether we need to suffle the play sequence or not
	function pl_setShuffle(shuffle, curItem) {
		this.shuffle = shuffle;
		this.curMediaIndex = curItem;
		
		// Generate the play sequence
		this.genPlaySequence();

		// Trigger "onChange" event
		this.onChange.exec();
	}
	
	// Internal methods
	// ******************
	
	// Generates the playing sequence from playlist
	function genPlaySequence() {
		var i, j, plItem;
		this.plItemsOrder = new Array();
		this.playSequence = new Array();
		this.plIndexToPlaySequenceIndex = new Array();
		
		// Construct order array
		for(i = 0; i < this.mediaItems.length; i++)
			this.plItemsOrder.push(i);
			
		// Shuffle order array
		if(this.shuffle)
			mixArray(this.mediaItems, this.curMediaIndex);
			
		// Constructs the playing sequence (considering ads)		
		for(i = 0; i < this.plItemsOrder.length; i++) {
			plItem = this.mediaItems[this.plItemsOrder[i]];
			this.plIndexToPlaySequenceIndex.push(this.playSequence.length);
			
			// Advertisements
			// First item
			if(i == 0 && (this.maxAdsFirst > 0 || this.maxAdsFirst == -1)) 
			{
				// Mix ads array
				mixArray(plItem.ads, 0);
				
				for(j = 0; j < plItem.ads.length && (j < this.maxAdsFirst || this.maxAdsFirst == -1); j++)
					this.playSequence.push(plItem.ads[j]);
			} else if(i > 0 && this.adsFrequency > 0 && (i % this.adsFrequency == 0) && (this.maxAds > 0 || this.maxAds == -1)) { // Items but the first
				// Mix ads array
				mixArray(plItem.ads, 0);
				
				for(j = 0; j < plItem.ads.length && (j < this.maxAds || this.maxAds == -1); j++)
					this.playSequence.push(plItem.ads[j]);
			}
			
			
			// Add the media item
			this.playSequence.push(plItem);
			
			// Vignettes in the end of the video
			if(plItem.isPreview)
			{
				for(j = 0; j < plItem.ads.length; j++)
					this.playSequence.push(plItem.ads[j]);								
			}	
		}
	}
	
	// Mix an array, used on shuffle
	// array: array to mix
	// cur: index of array item that will be the first item in the mixed array
	function mixArray(array, cur) {
		var i, j, temp;
		
		// Exchange itens
		if(cur > 0) {
			temp = array[0];
			array[0] = array[cur];
			array[cur] = temp;
		}
		
		for(i = 1; i < array.length - 1; i++) {
			j = Math.round(Math.random() * (array.length - i - 2)) + i + 1;
			temp = array[i];
			array[i] = null;
			array[i] = array[j];
			array[j] = temp;
		}
	}
}