/*****************************************************
*  
*  Copyright 2009 Adobe Systems Incorporated.  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 Adobe Systems Incorporated.
*  Portions created by Adobe Systems Incorporated are Copyright (C) 2009 Adobe Systems 
*  Incorporated. All Rights Reserved. 
*  
*****************************************************/
package org.osmf.traits
{
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.utils.Dictionary;
	
	import org.osmf.events.AudioEvent;
	import org.osmf.events.BufferEvent;
	import org.osmf.events.DRMEvent;
	import org.osmf.events.DVREvent;
	import org.osmf.events.DisplayObjectEvent;
	import org.osmf.events.DynamicStreamEvent;
	import org.osmf.events.LoadEvent;
	import org.osmf.events.MediaElementEvent;
	import org.osmf.events.PlayEvent;
	import org.osmf.events.SeekEvent;
	import org.osmf.events.TimeEvent;
	import org.osmf.media.MediaElement;
	
	
	/**
	 * Dispatched when the <code>duration</code> property of the media has changed.
	 * 
	 * @eventType org.osmf.events.TimeEvent.DURATION_CHANGE
	 * 
	 * 	@langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="durationChange", type="org.osmf.events.TimeEvent")]
	 
	/**
	 * Dispatched when the media has completed playback.
	 * 
	 * @eventType org.osmf.events.TimeEvent.COMPLETE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */	 
	[Event(name="complete", type="org.osmf.events.TimeEvent")]
	 	 
	/**
	 * Dispatched when the <code>volume</code> property of the media has changed.
	 * 
	 * @eventType org.osmf.events.AudioEvent.VOLUME_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */	 	 
	[Event(name="volumeChange", type="org.osmf.events.AudioEvent")]   
	 
	/**
	 * Dispatched when the <code>muted</code> property of the media has changed.
	 * 
	 * @eventType org.osmf.events.AudioEvent.MUTED_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */	 
	[Event(name="mutedChange", type="org.osmf.events.AudioEvent")] 
	 
	/**
	 * Dispatched when the <code>pan</code> property of the media has changed.
	 * 
	 * @eventType org.osmf.events.AudioEvent.PAN_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */	 	 
	[Event(name="panChange", type="org.osmf.events.AudioEvent")]

	/**
	 * Dispatched when the <code>playing</code> or <code>paused</code> property of the media has changed.
	 * 
	 * @eventType org.osmf.events.PlayEvent.PLAY_STATE_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */	 	 	 		
	[Event(name="playStateChange", type="org.osmf.events.PlayEvent")]
	
	/**
	 * Dispatched when the <code>canPause</code> property has changed.
	 * 
	 * @eventType org.osmf.events.PlayEvent.CAN_PAUSE_CHANGE
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 * 
	 */
	[Event(name="canPauseChange",type="org.osmf.events.PlayEvent")]	
		
	/**
	 * Dispatched when the <code>displayObject</code> property of the media has changed.
	 * 
	 * @eventType org.osmf.events.DisplayObjectEvent.DISPLAY_OBJECT_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */	 	 	 		
	[Event(name="displayObjectChange", type="org.osmf.events.DisplayObjectEvent")]
	
	/**
	 * Dispatched when the <code>mediaWidth</code> and/or <code>mediaHeight</code> property of the 
	 * media has changed.
	 * 
	 * @eventType org.osmf.events.DisplayObjectEvent.MEDIA_SIZE_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */		
	[Event(name="mediaSizeChange", type="org.osmf.events.DisplayObjectEvent")]
	 
	/**
	 * Dispatched when the <code>seeking</code> property of the media has changed.
	 * 
	 * @eventType org.osmf.events.SeekEvent.SEEKING_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */	 	
	[Event(name="seekingChange", type="org.osmf.events.SeekEvent")]
	    
	/**
	 * Dispatched when a dynamic stream switch change occurs.
	 * 
	 * @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 <code>autoSwitch</code> property has 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")]

	/**
	 * Dispatched when the <code>buffering</code> property has changed.
	 * 
	 * @eventType org.osmf.events.BufferEvent.BUFFERING_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="bufferingChange", type="org.osmf.events.BufferEvent")]
	
	/**
	 * Dispatched when the <code>bufferTime</code> property has changed.
	 * 
	 * @eventType org.osmf.events.BufferEvent.BUFFER_TIME_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="bufferTimeChange", type="org.osmf.events.BufferEvent")]

	/**
	 * Dispatched when the <code>bytesTotal</code> property has changed.
	 *
	 * @eventType org.osmf.events.LoadEvent
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="bytesTotalChange",type="org.osmf.events.LoadEvent")]
	
	/**
	 * Dispatched when the state of the LoadTrait has changed.
	 *
	 * @eventType org.osmf.events.LoadEvent.LOAD_STATE_CHANGE
	 *  
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0
	 */
	[Event(name="loadStateChange", type="org.osmf.events.LoadEvent")]

	/**
	 * Dispatched when the state of the DRMTrait has changed.
	 *
	 * @eventType org.osmf.events.DRMEvent.DRM_STATE_CHANGE
 	 *  
 	 *  @langversion 3.0
 	 *  @playerversion Flash 10.1
 	 *  @playerversion AIR 1.5
 	 *  @productversion OSMF 1.0
 	 */ 
	[Event(name="drmStateChange", type="org.osmf.events.DRMEvent")]
	
	/**
	 * Dispatched when the <code>isRecording</code> property has changed.
	 * 
	 * @eventType org.osmf.events.DVREvent.IS_RECORDING_CHANGE
 	 *  
 	 *  @langversion 3.0
 	 *  @playerversion Flash 10.1
 	 *  @playerversion AIR 1.5
 	 *  @productversion OSMF 1.0
 	 */ 
	[Event(name="isRecordingChange", type="org.osmf.events.DVREvent")]
		
	/**
	 * TraitEventDispatcher is a utility class that exposes a uniform
	 * interface for receiving trait events from a MediaElement.  This
	 * class monitors the MediaElement for traits being added and
	 * removed, and dispatches any events that the MediaElement's traits
	 * dispatch, and for which the client has registered listeners.
	 * 
	 *  @includeExample TraitEventDispatcherExample.as -noswf 
	 * 
	 *  @langversion 3.0
	 *  @playerversion Flash 10
	 *  @playerversion AIR 1.5
	 *  @productversion OSMF 1.0	 
	 */ 
	public class TraitEventDispatcher extends EventDispatcher
	{
		/**
		 * Constructor.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0		 
		 */ 
		public function TraitEventDispatcher()
		{
			super();
			
			if (eventMaps == null)
			{
				eventMaps = new Dictionary();
				eventMaps[TimeEvent.DURATION_CHANGE]					= MediaTraitType.TIME;	
				eventMaps[TimeEvent.COMPLETE]							= MediaTraitType.TIME;	
				eventMaps[PlayEvent.PLAY_STATE_CHANGE]					= MediaTraitType.PLAY;	
				eventMaps[PlayEvent.CAN_PAUSE_CHANGE]					= MediaTraitType.PLAY;	
				eventMaps[AudioEvent.VOLUME_CHANGE]						= MediaTraitType.AUDIO;	
				eventMaps[AudioEvent.MUTED_CHANGE]						= MediaTraitType.AUDIO;
				eventMaps[AudioEvent.PAN_CHANGE]						= MediaTraitType.AUDIO;	
				eventMaps[SeekEvent.SEEKING_CHANGE]						= MediaTraitType.SEEK;	
				eventMaps[DynamicStreamEvent.SWITCHING_CHANGE] 			= MediaTraitType.DYNAMIC_STREAM;	
				eventMaps[DynamicStreamEvent.AUTO_SWITCH_CHANGE] 		= MediaTraitType.DYNAMIC_STREAM;	
				eventMaps[DynamicStreamEvent.NUM_DYNAMIC_STREAMS_CHANGE] = MediaTraitType.DYNAMIC_STREAM;
				eventMaps[DisplayObjectEvent.DISPLAY_OBJECT_CHANGE]		= MediaTraitType.DISPLAY_OBJECT;	
				eventMaps[DisplayObjectEvent.MEDIA_SIZE_CHANGE] 		= MediaTraitType.DISPLAY_OBJECT;	
				eventMaps[LoadEvent.LOAD_STATE_CHANGE]					= MediaTraitType.LOAD;	
				eventMaps[LoadEvent.BYTES_LOADED_CHANGE]				= MediaTraitType.LOAD;	
				eventMaps[LoadEvent.BYTES_TOTAL_CHANGE]					= MediaTraitType.LOAD;	
				eventMaps[BufferEvent.BUFFERING_CHANGE]					= MediaTraitType.BUFFER;
				eventMaps[BufferEvent.BUFFER_TIME_CHANGE]				= MediaTraitType.BUFFER;
				eventMaps[DRMEvent.DRM_STATE_CHANGE]					= MediaTraitType.DRM;
				eventMaps[DVREvent.IS_RECORDING_CHANGE]					= MediaTraitType.DVR;					
			}						
		}
		
		/**
		 * The MediaElement which will be monitored, and whose trait events
		 * will be redispatched.
		 *
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0				 
		 */ 
		public function get media():MediaElement
		{
			return _mediaElement;
		}

		public function set media(value:MediaElement):void
		{
			if (value != _mediaElement)
			{
				var traitType:String;
				if (_mediaElement != null)
				{					
					_mediaElement.removeEventListener(MediaElementEvent.TRAIT_ADD, onTraitAdd);
					_mediaElement.removeEventListener(MediaElementEvent.TRAIT_REMOVE, onTraitRemove);
					
					for each (traitType in _mediaElement.traitTypes)
					{
						onTraitChanged(traitType, false);
					}								
				}	
				_mediaElement = value;
				if (_mediaElement != null)
				{
					_mediaElement.addEventListener(MediaElementEvent.TRAIT_ADD, onTraitAdd);					
					_mediaElement.addEventListener(MediaElementEvent.TRAIT_REMOVE, onTraitRemove);
													
					for each (traitType in _mediaElement.traitTypes)
					{
						onTraitChanged(traitType, true);
					}
				}			
			}
		}
				
		/**
		 * @private
		 **/
		override public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
		{
			var hadEventListener:Boolean = hasEventListener(type);		
			super.addEventListener(type, listener, useCapture, priority, useWeakReference);			
			if (_mediaElement
				&& !hadEventListener
				&& eventMaps[type] != undefined)
			{				
				changeListeners(true, eventMaps[type], type);	
			}			
		}
		
		/**
		 * @private
		 **/
		override public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			super.removeEventListener(type, listener, useCapture);
			if (_mediaElement 
				&& !hasEventListener(type) 
				&& eventMaps[type] != undefined)
			{		
				changeListeners(false, eventMaps[type], type);		
			}			
		}
		
		private function onTraitAdd(event:MediaElementEvent):void
		{				
			onTraitChanged(event.traitType, true);				
		}
		
		private function onTraitRemove(event:MediaElementEvent):void
		{
			onTraitChanged(event.traitType, false);						
		}
		
		private function onTraitChanged(traitType:String, add:Boolean):void
		{				
			switch (traitType)
			{
				case MediaTraitType.TIME:
					changeListeners(add, traitType, TimeEvent.DURATION_CHANGE);							
					changeListeners(add, traitType, TimeEvent.COMPLETE );								
					break;
				case MediaTraitType.PLAY:						
					changeListeners(add, traitType, PlayEvent.PLAY_STATE_CHANGE );		
					changeListeners(add, traitType, PlayEvent.CAN_PAUSE_CHANGE );																	
					break;	
				case MediaTraitType.AUDIO:					
					changeListeners(add, traitType, AudioEvent.VOLUME_CHANGE);		
					changeListeners(add, traitType, AudioEvent.MUTED_CHANGE);
					changeListeners(add, traitType, AudioEvent.PAN_CHANGE);							
					break;
				case MediaTraitType.SEEK:
					changeListeners(add, traitType, SeekEvent.SEEKING_CHANGE);
					break;
				case MediaTraitType.DYNAMIC_STREAM:	
					changeListeners(add, traitType, DynamicStreamEvent.SWITCHING_CHANGE);
					changeListeners(add, traitType, DynamicStreamEvent.AUTO_SWITCH_CHANGE);
					changeListeners(add, traitType, DynamicStreamEvent.NUM_DYNAMIC_STREAMS_CHANGE);
					break;						
				case MediaTraitType.DISPLAY_OBJECT:					
					changeListeners(add, traitType, DisplayObjectEvent.DISPLAY_OBJECT_CHANGE);											
					changeListeners(add, traitType, DisplayObjectEvent.MEDIA_SIZE_CHANGE);
					break;	
				case MediaTraitType.LOAD:					
					changeListeners(add, traitType, LoadEvent.LOAD_STATE_CHANGE);
					changeListeners(add, traitType, LoadEvent.BYTES_TOTAL_CHANGE);		
					changeListeners(add, traitType, LoadEvent.BYTES_LOADED_CHANGE);				
					break;		
				case MediaTraitType.BUFFER:
					changeListeners(add, traitType, BufferEvent.BUFFERING_CHANGE);	
					changeListeners(add, traitType, BufferEvent.BUFFER_TIME_CHANGE);						
					break;	
				case MediaTraitType.DRM:
					changeListeners(add, traitType, DRMEvent.DRM_STATE_CHANGE);
					break;
				case MediaTraitType.DVR:
					changeListeners(add, traitType, DVREvent.IS_RECORDING_CHANGE);	
					break;				
			}		
		}
				
		// Add any number of listeners to the trait, using the given event name.
		private function changeListeners(add:Boolean, traitType:String, event:String):void
		{
			if (_mediaElement.getTrait(traitType) != null)
			{		
				if (add && hasEventListener(event))
				{						
					_mediaElement.getTrait(traitType).addEventListener(event, redispatchEvent);
				}
				else
				{												
					_mediaElement.getTrait(traitType).removeEventListener(event, redispatchEvent);
				}
			}			
		}
		
		// Event Listeners will redispatch all of the ChangeEvents that correspond to trait 
		// properties.  The hasEventListener prevents the event from being cloned.
		private function redispatchEvent(event:Event):void
		{
			dispatchEvent(event.clone());			
		}	
		
		private static var eventMaps:Dictionary;	
		private var _mediaElement:MediaElement;		
	}
}
