package {
	import fl.controls.*;
	import fl.events.SliderEvent;
	
	import flash.display.MovieClip;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.NetStatusEvent;
	import flash.events.TimerEvent;
	import flash.media.SoundTransform;
	import flash.media.Video;
	import flash.net.NetConnection;
	import flash.net.NetStream;
	import flash.net.URLLoader;
	import flash.net.URLRequest;
	import flash.utils.Timer;

	public class VideoJukebox extends Sprite {
		/**
		 * The amount of time between calls to update the playhead timer, in 
		 * milliseconds.
		 */
		private const PLAYHEAD_UPDATE_INTERVAL_MS:uint = 10;

		/**
		 * The path to the XML file containing the video playlist.
		 */
		private const PLAYLIST_XML_URL:String = "playlist.xml";

		/**
		 * The client object to use for the NetStream object.
		 */
		private var client:Object;

		/**
		 * The index of the currently playing video.
		 */
		private var idx:uint = 0;

		/**
		 * A copy of the current video's metadata object.
		 */
		private var meta:Object;
		private var nc:NetConnection;
		private var ns:NetStream;
		private var playlist:XML;
		private var t:Timer;
		private var uldr:URLLoader;
		private var vid:Video;
		private var videosXML:XMLList;
		
		/**
		 * The SoundTransform object used to set the volume for the NetStream.
		 */
		private var volumeTransform:SoundTransform;

		/**
		 * Constructor
		 */
		public function VideoJukebox() {
			// Initialize the uldr variable which will be used to load the external 
			// playlist XML file.
			uldr = new URLLoader();
			uldr.addEventListener(Event.COMPLETE, xmlCompleteHandler);
			uldr.load(new URLRequest(PLAYLIST_XML_URL));
		}

		/**
		 * Once the XML file has loaded, parse the file contents into an XML object, 
		 * and create an XMList for the video nodes in the XML.
		 */
		private function xmlCompleteHandler(event:Event):void {
			playlist = XML(event.target.data);
			videosXML = playlist.video;
			main();
		}

		/**
		 * The main application.
		 */
		private function main():void {
			volumeTransform = new SoundTransform();

			// Create the client object for the NetStream, and set up a callback
			// handler for the onMetaData event.
			client = new Object();
			client.onMetaData = metadataHandler;

			nc = new NetConnection();
			nc.connect(null);

			// Initialize the NetSteam object, add a listener for the netStatus 
			// event, and set the client for the NetStream.
			ns = new NetStream(nc);
			ns.addEventListener(NetStatusEvent.NET_STATUS, netStatusHandler);
			ns.client = client;

			// Initialize the Video object, attach the NetStram, and add the Video
			// object to the display list.
			vid = new Video();
            vid.x = 40;
            vid.y = 110;
			vid.attachNetStream(ns);
			addChild(vid);

			// Begin playback of the first video.
			playVideo();

			// Initialize the Timer object and set the delay to
			// PLAYHEAD_UPDATE_INTERVAL_MS milliseconds. 
			t = new Timer(PLAYHEAD_UPDATE_INTERVAL_MS);
			t.addEventListener(TimerEvent.TIMER, timerHandler);

			// Configure the positionBar ProgressBar instance and set the mode to
			// MANUAL. Progress  bar values will be explicitly set using the
			// setProgress() method.
			positionBar.mode = ProgressBarMode.MANUAL;

			// Configure the volumeSlider Slider component instance. The maximum 
			// value is set to 1 because the volume in the SoundTransform object 
			// is set to a number between 0 and 1. The snapInterval and tickInterval 
			// properties are set to 0.1 which allows users to set the volume to 
			// 0, 0.1 - 0.9, 1.0 which allows users to increment or decrement the 
			// volume by 10%.
			volumeSlider.value = volumeTransform.volume;
			volumeSlider.minimum = 0;
			volumeSlider.maximum = 1;
			volumeSlider.snapInterval = 0.1;
			volumeSlider.tickInterval = volumeSlider.snapInterval;

			// Setting the liveDragging property to true causes the Slider 
			// instance's change event to be dispatched whenever the slider is 
			// moved, rather than when the user releases the slider thumb.
			volumeSlider.liveDragging = true;
			volumeSlider.addEventListener(SliderEvent.CHANGE, volumeChangeHandler);

			// Configure the various Button instances. Each Button instance uses 
			// the same click handler.
			playButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
			pauseButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
			stopButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
			backButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
			forwardButton.addEventListener(MouseEvent.CLICK, buttonClickHandler);
		}

		/**
		 * Event listener for the volumeSlider instance. Called when the user 
		 * changes the value of the volume slider.
		 */
		private function volumeChangeHandler(event:SliderEvent):void {
			// Set the volumeTransform's volume property to the current value of the 
			// Slider and set the NetStream object's soundTransform property.
			volumeTransform.volume = event.value;
			ns.soundTransform = volumeTransform;
		}

		/**
		 * Event listener for the ns object. Called when the net stream's status 
		 * changes.
		 */
		private function netStatusHandler(event:NetStatusEvent):void {
			try {
				switch (event.info.code) {
					case "NetStream.Play.Start" :
						// If the current code is Start, start the timer object.
						t.start();
						break;
					case "NetStream.Play.StreamNotFound" :
					case "NetStream.Play.Stop" :
						// If the current code is Stop or StreamNotFound, stop 
						// the timer object and play the next video in the playlist.
						t.stop();
						playNextVideo();
						break;
				}
			} catch (error:TypeError) {
				// Ignore any errors.
			}
		}

		/**
		 * Event listener for the ns object's client property. This method is called 
		 * when the net stream object receives metadata information for a video.
		 */
		private function metadataHandler(metadataObj:Object):void {
			// Store the metadata information in the meta object.
			meta = metadataObj;
			// Resize the Video instance on the display list with the video's width 
			// and height from the metadata object.
			vid.width = meta.width;
			vid.height = meta.height;
			// Reposition and resize the positionBar progress bar based on the 
			// current video's dimensions.
			positionBar.move(vid.x, vid.y + vid.height);
			positionBar.width = vid.width;
		}

		/**
		 * Retrieve the current video from the playlist XML object.
		 */
		private function getVideo():String {
			return videosXML[idx].@url;
		}

		/**
		 * Play the currently selected video.
		 */
		private function playVideo():void {
			var url:String = getVideo();
			ns.play(url);
		}

		/**
		 * Decrease the current video index and begin playback of the video.
		 */
		private function playPreviousVideo():void {
			if (idx > 0) {
				idx--;
				playVideo();
				// Make sure the positionBar progress bar is visible.
				positionBar.visible = true;
			}
		}

		/**
		 * Increase the current video index and begin playback of the video.
		 */
		private function playNextVideo():void {
			if (idx < (videosXML.length() - 1)) {
				// If this is not the last video in the playlist increase the 
				// video index and play the next video.
				idx++;
				playVideo();
				// Make sure the positionBar progress bar is visible.
				positionBar.visible = true;
			} else {
				// If this is the last video in the playlist increase the video
				// index, clear the contents of the Video object and hide the 
				// positionBar progress bar. The video index is increased so that 
				// when the video ends, clicking the backButton will play the 
				// correct video.
				idx++;
				vid.clear();
				positionBar.visible = false;
			}
		}

		/**
		 * Click handler for each of the video playback buttons.
		 */
private function buttonClickHandler(event:MouseEvent):void {
	// Use a switch statement to determine which button was clicked.
	switch (event.currentTarget) {
		case playButton :
			// If the play button was clicked, resume the video playback. 
			// If the video was already playing, this has no effect.
			ns.resume();
			break;
		case pauseButton :
			// If the pause button was clicked, pause the video playback. 
			// If the video was already playing, the video will be paused. 
			// If the video was already paused, the video will be resumed.
			ns.togglePause();
			break;
		case stopButton :
			// If the stop button was clicked, pause the video playback 
			// and reset the playhead back to the beginning of the video.
			ns.pause();
			ns.seek(0);
			break;
		case backButton :
			// If the back button was clicked, play the previous video in 
			// the playlist.
			playPreviousVideo();
			break;
		case forwardButton :
			// If the forward button was clicked, play the next video in 
			// the playlist.
			playNextVideo();
			break;
	}
}

		/**
		 * Event handler for the timer object. This method is called every 
		 * PLAYHEAD_UPDATE_INTERVAL_MS milliseconds as long as the timer is running.
		 */
		private function timerHandler(event:TimerEvent):void {
			try {
				// Update the progress bar and label based on the amount of video
				// that has played back.
				positionBar.setProgress(ns.time, meta.duration);
				positionLabel.text = ns.time.toFixed(1) + " of " + meta.duration.toFixed(1) + " seconds";
			} catch (error:Error) {
				// Ignore this error.
			}
		}
	}
}
