/*****************************************************
*  
*  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.net
{
	import flash.events.NetStatusEvent;
	import flash.net.NetStream;
	
	import org.osmf.media.MediaResourceBase;
	import org.osmf.traits.TimeTrait;
	
	[ExcludeClass]
	
	/**
	 * @private
	 * 
	 * The NetStreamTimeTrait class extends TimeTrait to expose NetStream's time properties.
	 * 
	 * @see flash.net.NetStream
	 */ 	
	public class NetStreamTimeTrait extends TimeTrait
	{
		/**
		 * Constructor.
		 *  
		 *  @langversion 3.0
		 *  @playerversion Flash 10
		 *  @playerversion AIR 1.5
		 *  @productversion OSMF 1.0
		 */ 		
		public function NetStreamTimeTrait(netStream:NetStream, resource:MediaResourceBase, defaultDuration:Number=NaN)
		{
			super();
			
			this.netStream = netStream;			
			NetClient(netStream.client).addHandler(NetStreamCodes.ON_META_DATA, onMetaData);
			NetClient(netStream.client).addHandler(NetStreamCodes.ON_PLAY_STATUS, onPlayStatus);
			netStream.addEventListener(NetStatusEvent.NET_STATUS, onNetStatus, false, 0, true);
			this.resource = resource;
			
			if (isNaN(defaultDuration) == false)
			{
				setDuration(defaultDuration);
			}
		}
		
		/**
		 * @private
		 */
		override public function get currentTime():Number
		{
			// If at the end of the video, make sure the duration matches the currentTime.  
			// Work around for FP-3724.  Only apply duration offset at the end - or else the seek(0) doesn't goto 0.
			if (durationOffset == (duration - (netStream.time - audioDelay)))  
			{
				return netStream.time - audioDelay + durationOffset;
			}
			else
			{
				return netStream.time - audioDelay;
			}
		}
		
		private function onMetaData(value:Object):void
		{
			// Determine the start time and duration for the
			// resource.
			var playArgs:Object = NetStreamUtils.getPlayArgsForResource(resource);
			
			//Audio delay is sometimes passed along with the metadata.  The audio delay affects
			//all netsTream.time related calculations, including seek().
			audioDelay = value.hasOwnProperty("audiodelay") ? value.audiodelay : 0;
			
			// Ensure our start time is non-negative, we only use it for
			// calculating the offset.
			var subclipStartTime:Number = Math.max(0, playArgs.start);
			
			// Ensure our duration is non-negative.
			var subclipDuration:Number = playArgs.len;
			if (subclipDuration == NetStreamUtils.PLAY_LEN_ARG_ALL)
			{
				subclipDuration = Number.MAX_VALUE;
			}
						
			// If startTime is unspecified, our duration is everything
			// up to the end of the subclip (or the entire duration, if
			// no subclip end is specified).  Take into account audio delay.
			setDuration(Math.min((value.duration - audioDelay) - subclipStartTime, subclipDuration));
		}
		
		private function onPlayStatus(event:Object):void
		{			
			switch(event.code)
			{
				case NetStreamCodes.NETSTREAM_PLAY_COMPLETE:
					// For streaming, NetStream.Play.Complete means playback
					// has completed.  But this isn't fired for progressive.
					signalComplete();
			}
		}
						
		private function onNetStatus(event:NetStatusEvent):void
		{
			switch (event.info.code)
			{
				case NetStreamCodes.NETSTREAM_PLAY_STOP:
					// For progressive,	NetStream.Play.Stop means playback
					// has completed.  But this isn't fired for streaming.
					if (NetStreamUtils.isStreamingResource(resource) == false)
					{						
						signalComplete();
					}
					break;
				case NetStreamCodes.NETSTREAM_PLAY_UNPUBLISH_NOTIFY:
					// When a live stream is unpublished, we should signal that
					// the stream has stopped.
					signalComplete();
					break;
			}
		}
		
		/**
		 * We have to change the duration , given that audioDelay isn't enough to 
		 * fix that netStream.time has from the detected duration.  This isn't
		 * pre computable, since PLAY_STOP is fired at
		 * non-deterministic intervals when the video is near ending.
		 **/
		override protected function signalComplete():void
		{
			if ((netStream.time - audioDelay) != duration)
			{
				durationOffset = duration - (netStream.time - audioDelay);
			}
			super.signalComplete();
		}
		
		private var durationOffset:Number = 0;
		private var audioDelay:Number = 0;
		private var netStream:NetStream;
		private var resource:MediaResourceBase;
	}
}