package
{
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	import flash.geom.*;
	import flash.media.*;
	import flash.net.*;
	import flash.system.Security;
	import flash.utils.clearInterval;
	import flash.utils.setInterval;
	import flash.system.Capabilities;
	
	import mx.controls.Button;
	import mx.controls.CheckBox;
	import mx.controls.Text;
	import mx.controls.TextInput;
	import mx.core.Application;
	import mx.core.UIComponent;
	import mx.events.FlexEvent;

	public class RecordingController extends Application
	{
		Security.LOCAL_TRUSTED;
		
		private var nc:NetConnection = null;
		private var nsPublish:NetStream = null;             
		private var nsPlay:NetStream = null;
		private var camera:Camera = null;
		private var microphone:Microphone = null;
		private var serverName:String = "rtmp://localhost/videorecording";
		private var movieName:String = "videorecording";
		private var flushVideoBufferTimer:Number = 0;
		
		
		private var videoCamera:Video;
		private var videoRemote:Video;
		private var isEnabled:Boolean;
		public var videoRemoteContainer:UIComponent;
		public var videoCameraContainer:UIComponent;
		public var doPublish:Button;
		public var doSubscribe:Button;
		public var connectButton:Button;
		public var fpsText:Text;
		public var bufferLenText:Text;
		public var nameStr:TextInput;
		public var connectStr:TextInput;
		public var publishName:TextInput;
		//public var subscribeName:TextInput;
		public var prompt:Text;
		public var playerVersion:Text;
		public var AppendCheckbox:CheckBox;

		
		
		public function RecordingController()
		{
			addEventListener(FlexEvent.APPLICATION_COMPLETE,mainInit); 
		}
		
		private function mainInit(event:FlexEvent):void
		{
			playerVersion.text = Capabilities.version+" (Flex)";
			stage.align = "TL";
			stage.scaleMode = "noScale";
			videoCamera = new Video(240, 180);
			videoCameraContainer.addChild(videoCamera);
			videoRemote = new Video(240, 180);
			videoRemoteContainer.addChild(videoRemote);
			connectStr.text = "rtmp://localhost/videorecording";
			connectButton.addEventListener(MouseEvent.CLICK, doConnect);
			doPublish.addEventListener(MouseEvent.CLICK, doRecordButton);
			doSubscribe.addEventListener(MouseEvent.CLICK, doPlayButton);
			startCamera();
		}
		
		private function startCamera():void
		{	
			// get the default Flash camera and microphone
			camera = Camera.getCamera();
			microphone = Microphone.getMicrophone();
		
			// here are all the quality and performance settings
			camera.setMode(240, 180, 30, false);
			camera.setQuality(0, 88);
			camera.setKeyFrameInterval(30);
			microphone.rate = 11;
			
			nameStr.text = movieName;
			AppendCheckbox.selected = false;
			connectStr.text = serverName;
			connectButton.addEventListener(MouseEvent.CLICK, doConnect);
			
			enablePlayControls(false);
		}
		
		private function ncOnStatus(infoObject:NetStatusEvent):void
		{
			trace("nc: "+infoObject.info.code+" ("+infoObject.info.description+")");
			if (infoObject.info.code == "NetConnection.Connect.Success")
				enablePlayControls(true);
			else if (infoObject.info.code == "NetConnection.Connect.Failed")
				prompt.text = "Connection failed: Try rtmp://[server-ip-address]/videorecording";
			else if (infoObject.info.code == "NetConnection.Connect.Rejected")
				prompt.text = infoObject.info.description;
		}
		
		private function doConnect(event:MouseEvent):void
		{
			// connect to the Wowza Media Server
			if (nc == null)
			{
				// create a connection to the wowza media server
				nc = new NetConnection();
				nc.addEventListener(NetStatusEvent.NET_STATUS, ncOnStatus);
				nc.connect(connectStr.text);
						
				connectButton.label = "Stop";
				
				// uncomment this to monitor frame rate and buffer length
				//setInterval("updateStreamValues", 500);
				
				videoCamera.clear();
				videoCamera.attachCamera(camera);
				
			}
			else
			{
				nsPublish = null;
				nsPlay = null;
		
				videoCamera.attachNetStream(null);
				videoCamera.clear();
				
				videoRemote.attachNetStream(null);
				videoRemote.clear();
				
				nc.close();
				nc = null;
				
				enablePlayControls(false);
		
				doSubscribe.label = 'Play';
				doPublish.label = 'Record';
				AppendCheckbox.selected = false;
				
				connectButton.label = "Connect";
				prompt.text = "";
			}
		}
		
		private function enablePlayControls(isEnable:Boolean):void
		{
			doPublish.enabled = isEnable;
			doSubscribe.enabled = isEnable;
			nameStr.enabled = isEnable;
			AppendCheckbox.enabled = isEnable;
		}
		
		// function to monitor the frame rate and buffer length
		private function updateStreamValues():void
		{
			if (nsPlay != null)
			{
				fpsText.text = (Math.round(nsPlay.currentFPS*1000)/1000)+" fps";
				bufferLenText.text = (Math.round(nsPlay.bufferLength*1000)/1000)+" secs";
			}
			else
			{
				fpsText.text = "";
				bufferLenText.text = "";
			}
		}
		
		private function nsPlayOnStatus(infoObject:NetStatusEvent):void
		{
			trace("nsPlay: onStatus: "+infoObject.info.code+" ("+infoObject.info.description+")");
			if (infoObject.info.code == "NetStream.Play.StreamNotFound" || infoObject.info.code == "NetStream.Play.Failed")
				prompt.text = infoObject.info.description;
		}
		
		private function doPlayStart():void
		{
			trace("doPlayStart");
			
			// each time we play a video create a new NetStream object
			nsPlay = new NetStream(nc);
			nsPlay.addEventListener(NetStatusEvent.NET_STATUS, nsPlayOnStatus);
				
			var nsPlayClient:Object = new Object();
			nsPlay.client = nsPlayClient;
		
			// trace the NetStream play status information
			nsPlayClient.onPlayStatus = function(infoObject:Object):void
			{
				trace("nsPlay: onPlayStatus: "+infoObject.code+" ("+infoObject.description+")");
				if (infoObject.code == "NetStream.Play.Complete")
				{
					doPlayStop();
				}
			}
		
			nsPlayClient.onMetaData = function(infoObject:Object):void
			{
				trace("onMetaData");
				
				// print debug information about the metaData
				for (var propName:String in infoObject)
				{
					trace("  "+propName + " = " + infoObject[propName]);
				}
			};		
		
			// set the buffer time to 2 seconds
			nsPlay.bufferTime = 2;
		
			// attach the NetStream object to the right most video object
			videoRemote.attachNetStream(nsPlay);
			
			// play the movie you just recorded
			nsPlay.play(nameStr.text);
			
			doSubscribe.label = 'Stop';
		}
		
		private function doPlayStop():void
		{
			// when you hit stop disconnect from the NetStream object and clear the video player
			videoRemote.attachNetStream(null);
			videoRemote.clear();
			
			if (nsPlay != null)
				nsPlay.close();
			nsPlay = null;
			
			doSubscribe.label = 'Play';
		}
		
		private function doCloseRecord():void
		{
			// after we have hit "Stop" recording and after the buffered video data has been
			// sent to the Wowza Media Server close the publishing stream
			nsPublish.publish("null");
		}
		
		// this function gets called every 250 ms to monitor the
		// progress of flushing the video buffer. Once the video
		// buffer is empty we close publishing stream
		private function flushVideoBuffer():void
		{
			var buffLen:Number = nsPublish.bufferLength;
			if (buffLen == 0)
			{
				clearInterval(flushVideoBufferTimer);
				flushVideoBufferTimer = 0;
				doCloseRecord();
				doPublish.label = 'Record';
			}
		}
		
		private function nsPublicOnStatus(infoObject:NetStatusEvent):void
		{
			trace("nsPublish: "+infoObject.info.code+" ("+infoObject.info.description+")");
			
			// After calling nsPublish.publish(false); we wait for a status
			// event of "NetStream.Unpublish.Success" which tells us all the video
			// and audio data has been written to the flv file. It is at this time
			// that we can start playing the video we just recorded.
			if (infoObject.info.code == "NetStream.Unpublish.Success")
			{
				doPlayStart();
			}
		
			if (infoObject.info.code == "NetStream.Play.StreamNotFound" || infoObject.info.code == "NetStream.Play.Failed")
				prompt.text = infoObject.info.description;
		}
		
		// Start recording video to the server
		private function doRecordStart():void
		{
			// stop video playback
			doPlayStop();
			
			// create a new NetStream object for publishing
			nsPublish = new NetStream(nc);
			
			var nsPublishClient:Object = new Object();
			nsPublish.client = nsPublishClient;
		
			// trace the NetStream status information
			nsPublish.addEventListener(NetStatusEvent.NET_STATUS, nsPublicOnStatus);
			
			// publish the stream by name
			nsPublish.publish(nameStr.text, (AppendCheckbox.selected?"append":"record"));
			
			// add custom metadata to the header of the .flv file
			var metaData:Object = new Object();
			metaData["description"] = "Recorded using VideoRecording example."
			nsPublish.send("@setDataFrame", "onMetaData", metaData);
		
			// attach the camera and microphone to the server
			nsPublish.attachCamera(camera);
			nsPublish.attachAudio(microphone);
			
			// set the buffer time to 20 seconds to buffer 20 seconds of video
			// data for better performance and higher quality video
			nsPublish.bufferTime = 20;
		
			doPublish.label = 'Stop';
		}
		
		private function doRecordStop():void
		{
			// stop streaming video and audio to the publishing
			// NetStream object
			nsPublish.attachAudio(null);
			nsPublish.attachCamera(null);
		
			// After stopping the publishing we need to check if there is
			// video content in the NetStream buffer. If there is data
			// we are going to monitor the video upload progress by calling
			// flushVideoBuffer every 250ms.  If the buffer length is 0
			// we close the recording immediately.
			var buffLen:Number = nsPublish.bufferLength;
			if (buffLen > 0)
			{
				flushVideoBufferTimer = setInterval(flushVideoBuffer, 250);
				doPublish.label = 'Wait...';
			}
			else
			{
				trace("nsPublish.publish(null)");
				doCloseRecord();		
				doPublish.label = 'Record';
			}
		}
		
		private function doRecordButton(event:MouseEvent):void
		{
			if (doPublish.label == 'Record')
				doRecordStart();
			else
				doRecordStop();
		}
		
		private function doPlayButton(event:MouseEvent):void
		{
			if (doSubscribe.label == 'Play')
				doPlayStart();
			else
				doPlayStop();
		}		
	}
}