// ************************************************************************************************
// *** Constructs an object that represents a playlist
// ************************************************************************************************

function Playlist() {
	// ************************************************************************************************
	// *** Interface
	// ************************************************************************************************	
	
	// Fields
	// ******
	this.id = 0;
	this.title = '';
	
	// Advertisements parameters
	this.adsFrequency = 3; // Advertisements frequency
	this.maxAdsFirst = -1; // Maximum number of advertisements before the first item (negative number for all ads)
	this.maxAds = -1; // Maximum number of advertisements before the media items (but the first, negative number for all ads)
	this.adsStartAt = 0; // Position of the first ad (zero-based)
	
	// 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.getPlayIndex = pl_GetPlayIndex;
	this.indexOf = pl_IndexOf;
	this.indexOfPlaySequence = pl_IndexOfPlaySequence;
	this.count = pl_Count;
	this.moveItem = pl_moveItem;
	this.removeAt = pl_RemoveAt;
	this.setShuffle = pl_setShuffle;
	this.getShuffle = pl_GetShuffle;
	
	// 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();
	}
	
	// Clears the playlist
	function pl_Clear() {
		this.mediaItems = new Array();
		
		// Generate the play sequence
		this.genPlaySequence();
	}
	
	// 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[this.plItemsOrder[plIndex]];
	}
	
	// Receives a playlist index and returns the related original play sequence index
	function pl_GetPlayIndex(plIndex)
	{
		return this.indexOfPlaySequence(this.mediaItems[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 the index of passed media item in the play sequence
	function pl_IndexOfPlaySequence(mediaItem)
	{
		var i, j;
		
		// Search for the passed media item
		for(i = 0; i < this.playSequence.length; i++)
		{
			if(mediaItem == this.playSequence[i]) // Check if the wanted item is the current
			{
				return i;
			}
			else if(this.playSequence[i].ads != null) // Search for the item in ads
			{
				for(j = 0; j < this.playSequence[i].ads.length; j++)
				{
					if(this.playSequence[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();
	}
	
	// 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();
	}
	
	// Removes a media item at the specified position
	function pl_RemoveAt(index)
	{
		this.mediaItems.splice(index, 1);
		
		// Generate the play sequence
		this.genPlaySequence();
	}
	
	// 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();

	}
	function pl_GetShuffle()
	{
		return this.shuffle;
	}
	
	// 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.plItemsOrder, this.curMediaIndex);
		}

		// Controls if this is the first ad item (may have more or less ads)
		var firstAdItem = true;
		
		// 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);
			
			// Checks if ads should be applied to this item
			var hasAds = false;
			
			// Checks if we've reached the "start at" poing
			if(i>=this.adsStartAt)
			{
				// Checks if we're in a valid ad position
				hasAds = ((i-this.adsStartAt)%this.adsFrequency)==0;
			}

			if(hasAds)
			{
				// Checks how many ads in this position
				var adCount = this.maxAds;
				if(firstAdItem)
				{
					adCount = this.maxAdsFirst; // First position may have more ads
					firstAdItem = false;        // Following items will have less ads
				}
				
				// Mix ads array
				mixArray2(plItem.ads);

				// Inserts the ads
				for(j = 0; j < plItem.ads.length && (j < adCount || adCount == -1); j++)
				{
					if(plItem.ads[j])
					{
						this.playSequence.push(plItem.ads[j]);
					}
				}
			}

			// Add the media item
			this.playSequence.push(plItem);
		}
		
		// Trigger "onChange" event
		this.onChange.exec();		
	}
	
	// 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;
		}
	}
	function mixArray2(array)
	{
		for(var i = 0, rand, temp; i < array.length; i++)
		{
			rand = Random(0, array.length -1);
			temp = array[i];
			
			array[i] = array[rand];
			array[rand] = temp;
		}
	}
	function Random(min, max)
	{
		var seed = Math.random();
		return Math.round(seed * (max - min)) + min;
	}
}