/*****************************************************
*  
*  Copyright 2009 Akamai Technologies, Inc.  All Rights Reserved.
*  
*****************************************************
*  The contents of this file are subject to the Mozilla Public License
*  Version 1.1 (the "License"); you may not use this file except in
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*   
*  Software distributed under the License is distributed on an "AS IS"
*  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
*  License for the specific language governing rights and limitations
*  under the License.
*   
*  
*  The Initial Developer of the Original Code is Akamai Technologies, Inc.
*  Portions created by Akamai Technologies, Inc. are Copyright (C) 2009 Akamai 
*  Technologies, Inc. All Rights Reserved. 
*  
*  Contributor(s): Adobe Systems Incorporated.
* 
*****************************************************/
package org.osmf.traits
{
	import flash.errors.IllegalOperationError;
	
	import org.osmf.events.DynamicStreamEvent;
	import org.osmf.utils.OSMFStrings;
	
	/**
	 * Dispatched when a stream switch is requested, completed, or failed.
	 * 
	 * @eventType org.osmf.events.DynamicStreamEvent.SWITCHING_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="switchingChange",type="org.osmf.events.DynamicStreamEvent")]
	
	/**
	 * Dispatched when the number of dynamic streams has changed.
	 * 
	 * @eventType org.osmf.events.DynamicStreamEvent.NUM_DYNAMIC_STREAMS_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="numDynamicStreamsChange",type="org.osmf.events.DynamicStreamEvent")]
	
	/**
	 * Dispatched when the autoSwitch property changed.
	 * 
	 * @eventType org.osmf.events.DynamicStreamEvent.AUTO_SWITCH_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="autoSwitchChange",type="org.osmf.events.DynamicStreamEvent")]
		
	/**
	 * DynamicStreamTrait defines the trait interface for media supporting dynamic stream
	 * switching.  It can also be used as the base class for a more specific DynamicStreamTrait
	 * subclass.
	 * 
	 * <p>Use the <code>MediaElement.hasTrait(MediaTraitType.DYNAMIC_STREAM)</code> method to query
	 * whether a media element has a trait of this type.
	 * If <code>hasTrait(MediaTraitType.DYNAMIC_STREAM)</code> returns <code>true</code>,
	 * use the <code>MediaElement.getTrait(MediaTraitType.DYNAMIC_STREAM)</code> method
	 * to get an object of this type.</p>
	 * 
	 * @see org.osmf.media.MediaElement
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	public class DynamicStreamTrait extends MediaTraitBase
	{
		/**
		 * Constructor.
		 * 
		 * @param autoSwitch The initial autoSwitch state for the trait.  The default is true.
		 * @param currentIndex The initial stream index for the trait.  The default is zero.
		 * @param numDynamicStreams The total number of dynamic streams.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */ 
		public function DynamicStreamTrait(autoSwitch:Boolean=true, currentIndex:int=0, numDynamicStreams:int=1)
		{
			super(MediaTraitType.DYNAMIC_STREAM);
			
			_autoSwitch = autoSwitch;
			_currentIndex = currentIndex;		
			_numDynamicStreams = numDynamicStreams;
			_maxAllowedIndex = numDynamicStreams - 1;

			_switching = false;
		}
		
		/**
		 * Defines whether or not the trait should be in manual 
		 * or auto-switch mode. If in manual mode the <code>switchTo</code>
		 * method can be used to manually switch to a specific stream.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		public function get autoSwitch():Boolean
		{			
			return _autoSwitch;
		}
		
		public final function set autoSwitch(value:Boolean):void
		{
			if (autoSwitch != value)
			{
				autoSwitchChangeStart(value);

				_autoSwitch = value;
				
				autoSwitchChangeEnd();
			}
		}
		
		/**
		 * The total number of dynamic streams.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		public function get numDynamicStreams():int
		{
			return _numDynamicStreams;
		}

		/**
		 * The index of the current dynamic stream.  Uses a zero-based index.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		public function get currentIndex():int
		{
			return _currentIndex;
		}

		/**
		 * The maximum allowed index. This can be set at run-time to 
		 * provide a ceiling for the switching profile, for example,
		 * to keep from switching up to a higher quality stream when 
		 * the current video is too small to handle a higher quality stream.
		 * 
		 * The default is the highest stream index.
		 * 
		 * @throws RangeError If the specified index is less than zero or
		 * greater than the total number of dynamic streams.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		public function get maxAllowedIndex():int
		{
			return _maxAllowedIndex;
		}
		
		public final function set maxAllowedIndex(value:int):void
		{
			if (value < 0 || value > _numDynamicStreams - 1)
			{
				throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
			}

			if (maxAllowedIndex != value)
			{
				maxAllowedIndexChangeStart(value);

				_maxAllowedIndex = value;
				
				maxAllowedIndexChangeEnd();
			}		
		}
		
		/**
		 * Returns the associated bitrate, in kilobits per second, for the specified index.
		 * 
		 * @throws RangeError If the specified index is less than zero or
		 * greater than the highest index available.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */ 
		public function getBitrateForIndex(index:int):Number
		{
			if (index > _numDynamicStreams - 1 || index < 0)
			{
				throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
			}
			
			return 0;
		}

		/**
		 * Indicates whether or not a switch is currently in progress.
		 * This property will return <code>true</code> while a switch has been 
		 * requested and the switch has not yet been acknowledged and no switch failure 
		 * has occurred.  Once the switch request has been acknowledged or a 
		 * failure occurs, the property will return <code>false</code>.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		public function get switching():Boolean
		{			
			return _switching;
		}
		
		/**
		 * Switch to a specific index. To switch up, use the <code>currentIndex</code>
		 * property, such as:<p>
		 * <code>
		 * obj.switchTo(obj.currentIndex + 1);
		 * </code>
		 * </p>
		 * @throws RangeError If the specified index is less than zero or
		 * greater than <code>maxAllowedIndex</code>.
    	 * Note:  If the media is paused, switching will not take place until after play resumes.		 
		 * @throws IllegalOperationError If the stream is not in manual switch mode.
		 * 
		 * @see maxAllowedIndex
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		public function switchTo(index:int):void
		{
			if (autoSwitch)
			{
				throw new IllegalOperationError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_STREAM_NOT_IN_MANUAL_MODE));
			}
			else if (index != currentIndex)
			{
				if (index < 0 || index > maxAllowedIndex)
				{
					throw new RangeError(OSMFStrings.getString(OSMFStrings.STREAMSWITCH_INVALID_INDEX));
				}

				// This method sets the switching state to true.  The processing
				// and completion of the switch are up to the implementing media.
				setSwitching(true, index);
			}			
		}
				
		// Internals
		//

		/**
		 * Invoking this setter will result in the trait's numDynamicStreams
		 * property changing.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */		
		protected final function setNumDynamicStreams(value:int):void
		{
			if (value != _numDynamicStreams)
			{
				_numDynamicStreams = value;
				
				// Only adjust our maxAllowedIndex property if the old value
				// is now out of range.
				if (maxAllowedIndex >= _numDynamicStreams)
				{
					maxAllowedIndex = Math.max(0, _numDynamicStreams - 1);
				}
				
				dispatchEvent(new DynamicStreamEvent(DynamicStreamEvent.NUM_DYNAMIC_STREAMS_CHANGE));
			}			
		}
		
		/**
		 * Invoking this setter will result in the trait's currentIndex
		 * property changing.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */		
		protected final function setCurrentIndex(value:int):void
		{
			_currentIndex = value;
		}
		
		/**
		 * Must be called by the implementing media on completing a switch.
		 * 
		 * Calls the <code>switchingChangeStart()</code> and <code>switchingChangeEnd()</code>
		 * methods.
		 * @param newSwitching New <code>switching</code> value for the trait.
		 * @param index The index to which the switch shall (or did) occur.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */		
		protected final function setSwitching(newSwitching:Boolean, index:int):void
		{
			if (newSwitching != _switching)
			{
				switchingChangeStart(newSwitching, index);
				
				_switching = newSwitching;
				
				// Update the index when a switch finishes.
				if (newSwitching == false)
				{
					setCurrentIndex(index);
				}
				
				switchingChangeEnd(index);
			}
		}

		/**
         * Called immediately before the <code>autoSwitch</code> property is changed.
		 * <p>Subclasses can override this method to communicate the change to the media.</p>
         * @param value New value for the <code>autoSwitch</code> property.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */		
		protected function autoSwitchChangeStart(value:Boolean):void
		{			
		}
				
		/**
		 * Called just after the <code>autoSwitch</code> property has changed.
		 * Dispatches the change event.
		 * 
		 * <p>Subclasses that override should call this method to
		 * dispatch the change event.</p>
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */		
		protected function autoSwitchChangeEnd():void
		{
			dispatchEvent(new DynamicStreamEvent(DynamicStreamEvent.AUTO_SWITCH_CHANGE, false, false, false, _autoSwitch));	
		}
		
		/**
		 * Called immediately before the <code>switching</code> property is changed.
		 * <p>Subclasses can override this method to communicate the change to the media.</p>
         * @param newSwitching New value for the <code>switching</code> property.
         * @param index The index of the stream to switch to.
 		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		protected function switchingChangeStart(newSwitching:Boolean, index:int):void
		{			
		}
		
		/**
		 * Called just after the <code>switching</code> property has changed.
		 * Dispatches the change event.
		 * 
		 * <p>Subclasses that override should call this method to
		 * dispatch the change event.</p>
		 * 
		 * @param index The index of the switched-to stream.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */		
		protected function switchingChangeEnd(index:int):void
		{
			dispatchEvent
				( new DynamicStreamEvent
					( DynamicStreamEvent.SWITCHING_CHANGE
					, false
					, false
					, switching
					)
				);
		}
		
		/**
		 * Called immediately before the <code>maxAllowedIndex</code> property is changed.
		 * <p>Subclasses can override this method to communicate the change to the media.</p>
         * @param newIndex New value for the <code>maxAllowedIndex</code> property.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */
		protected function maxAllowedIndexChangeStart(newIndex:int):void
		{			
		}
		
		/**
		 * Called just after the <code>maxAllowedIndex</code> property has changed.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */		
		protected function maxAllowedIndexChangeEnd():void
		{
		}
		
		private var _autoSwitch:Boolean;
		private var _currentIndex:int = 0;
		private var _maxAllowedIndex:int = 0;
		private var _numDynamicStreams:int;
		private var _switching:Boolean;
	}
}
