/**
 * Simple Camera Capture application meant to be used where WebRTC is not supported
 * (e.g. Safari, Internet Explorer, Opera). All orchestration is assumed to happen
 * in JavaScript. The only function this application has is to capture a snapshot
 * and allow a 640x480 PNG of that snapshot to be made available to the JS as a
 * base64 encoded data URL.
 * 
 * There are really only three methods:
 *   snap() freezes the video and returns a PNG file as a data URL string. You can
 *          assign this return value to an img's src attribute.
 *   reset() restarts the the video.
 *   imageDataUrl() returns the same thing as snap() -- 
 */

package
{
	import flash.display.BitmapData;
	import flash.display.PNGEncoderOptions;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.StatusEvent;
	import flash.external.ExternalInterface;
	import flash.geom.Rectangle;
	import flash.media.Camera;
	import flash.media.Video;
	import flash.utils.ByteArray;
	
	import mx.utils.Base64Encoder;
	
	[SWF(width="640", height="480")]
	public class CameraCapture extends Sprite
	{
		// We pick these values because that's captured by the WebRTC spec
		private const VIDEO_WIDTH:int = 640;
		private const VIDEO_HEIGHT:int = 480;
		
		private var camera:Camera;
		private var video:Video;
		private var b64EncodedImage:String = null;
		private var permissionGiven:Boolean = false;
		
		public function CameraCapture()
		{
			addEventListener(Event.ADDED_TO_STAGE, init); 
		}
		
		protected function init(e:Event):void {
			camera = Camera.getCamera();
			camera.setMode(VIDEO_WIDTH, VIDEO_HEIGHT, 30);
			camera.addEventListener(StatusEvent.STATUS, statusHandler);
			
			video = new Video(VIDEO_WIDTH, VIDEO_HEIGHT);
			video.attachCamera(camera);
			
			addChild(video);
			
			ExternalInterface.addCallback("snap", snap);
			ExternalInterface.addCallback("reset", reset);
			ExternalInterface.addCallback("imageDataUrl", imageDataUrl);
			ExternalInterface.addCallback("cameraAuthorized", cameraAuthorized);
			ExternalInterface.addCallback("hasCamera", hasCamera);
			
			// Notify the container that the SWF is ready to be called. 
			ExternalInterface.call("setSWFIsReady"); 
		}
		
		public function snap():String {
			// If we already have a b64 encoded image, just return that. The user
			// is calling snap() multiple times in a row without reset()
			if (b64EncodedImage) {
				return imageDataUrl();
			}
			
			var bitmapData:BitmapData = new BitmapData(video.width, video.height);
			bitmapData.draw(video); // Draw a snapshot of the video onto our bitmapData
			video.attachCamera(null); // Stop capturing video
			
			// Convert to PNG
			var pngBytes:ByteArray = new ByteArray();
			bitmapData.encode(
				new Rectangle(0, 0, video.width, video.height),
				new PNGEncoderOptions(),
				pngBytes
			);
			
			// Convert to Base64 encoding of PNG
			var b64Encoder:Base64Encoder = new Base64Encoder();
			b64Encoder.encodeBytes(pngBytes);
			b64EncodedImage = b64Encoder.toString();
			
			return imageDataUrl();
		}

		public function reset():String {
			video.attachCamera(camera);
			b64EncodedImage = null;
			
			return imageDataUrl();
		}

		public function imageDataUrl():String {
			if (b64EncodedImage) {
				return "data:image/png;base64," + b64EncodedImage;
			}
			return "";
		}
		
		public function cameraAuthorized():Boolean {
			return permissionGiven;
		}
		
		public function hasCamera():Boolean {
			return (Camera.names.length != 0);
		}
		
		public function statusHandler(event:StatusEvent):void {
			switch (event.code) 
			{ 
				case "Camera.Muted": 
					// User clicked Deny.
					permissionGiven = false;
					break; 
				case "Camera.Unmuted": 
					// "User clicked Accept.
					permissionGiven = true;
					break; 
			} 			
		}
	}
}

