package {
	/* Tweener Class [http://code.google.com/p/tweener/] */
	import caurina.transitions.Tweener;
	
	/* FLARManager Framework [http://words.transmote.com/wp/flarmanager/] */ 
	import com.transmote.flar.FLARManager;
	import com.transmote.flar.marker.FLARMarker;
	import com.transmote.flar.marker.FLARMarkerEvent;
	import com.transmote.flar.utils.geom.FLARPVGeomUtils;
	
	/* Built-in Flash components */
	import flash.display.Shape;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.filters.GlowFilter;
	import flash.geom.Point;
	import flash.utils.Dictionary;
	
	/* FLARToolkit [http://www.libspark.org/wiki/saqoosha/FLARToolKit/en] */
	import org.libspark.flartoolkit.support.pv3d.FLARCamera3D;
	
	/* Papervision3D framework [http://blog.papervision3d.org/] */
	import org.papervision3d.lights.PointLight3D;
	import org.papervision3d.materials.shadematerials.FlatShadeMaterial;
	import org.papervision3d.materials.utils.MaterialsList;
	import org.papervision3d.objects.DisplayObject3D;
	import org.papervision3d.objects.primitives.Cube;
	import org.papervision3d.render.LazyRenderEngine;
	import org.papervision3d.scenes.Scene3D;
	import org.papervision3d.view.Viewport3D;
	
	//file loading
	import org.papervision3d.events.FileLoadEvent;
	import org.papervision3d.materials.BitmapFileMaterial;
	
	//quick & dirty audio
	import musica.audio;
	//import musica.FR;
	import flash.media.Sound;
	import flash.net.URLRequest;
	import flash.media.SoundChannel;

	
	/* Change output settings */
	[SWF(width="640", height="480", frameRate="25", backgroundColor="#000000")]
	dynamic public class Multiple_Markers extends Sprite {
		/* FLARManager pointer */
		private var fm:FLARManager;
		/* Papervision Scene3D pointer */
		private var scene3D:Scene3D;
		/* Papervision Viewport3D pointer */
		private var viewport3D:Viewport3D;
		/* FLARToolkit FLARCamera3D pointer */ 
		private var camera3D:FLARCamera3D;
		/* Papervision render engine pointer */
		private var lre:LazyRenderEngine;
		/* Papervision PointLight3D pointer */
		private var pointLight3D:PointLight3D;
		
		/* Initialise glow filter to add a white border around selected objects in our scene */
		private var glow:GlowFilter = new GlowFilter(0xFFFFFF, 1, 7, 7, 30, 1, false, false);
		
		/* Vector storing references to all markers on screen, grouped by pattern id */
		private var markersByPatternId:Vector.<Vector.<FLARMarker>>;
		/* Dictionary storing references to marker containers, indexed by relevant marker object */
		private var containersByMarker:Dictionary;
		
		/* Dictionary storing references to the corner nodes for each marker, indexed by relevant marker object */
		private var nodesByMarker:Dictionary;
		
		//!!!!
		//1. added image and cube materials; 2: line 113
		private var imageMat0:BitmapFileMaterial;
		private var imageMat1:BitmapFileMaterial;
		private var imageMat2:BitmapFileMaterial;
		private var imageMat3:BitmapFileMaterial;
		private var imageMat4:BitmapFileMaterial;
		private var imageMat5:BitmapFileMaterial;
		private var cubeMaterials0:MaterialsList;
		private var cubeMaterials1:MaterialsList;
		private var cubeMaterials2:MaterialsList;
		private var cubeMaterials3:MaterialsList;
		private var cubeMaterials4:MaterialsList;
		private var cubeMaterials5:MaterialsList;
		
		/* Constructor method */
		public function Multiple_Markers() {
			/* Run augmented reality initialisation */
			this.initFLAR();
		}
		
		
				
		//case statement for audio
		//public var audioId:String = "placeholder";
		private var Audio = new audio();
		//private var AudioFR = new FR();
		/** public function getAudioByPatternId(patternId:int, shaded:Boolean = false):String {
			switch (patternId) {
				case 0:
					return audioId = "musica/audioPI.mp3";
				case 1:
					return audioId = "musica/audioFR.mp3";
				case 2:
					return audioId = "musica/audioBR.mp3";
				case 3:
					return audioId = "musica/audioIT.mp3";
				case 4:
					return audioId = "musica/audioDE.mp3";
				case 5:
					return audioId = "musica/audioFI.mp3";
				default:
					return audioId = "could not set";
				//trace(audioId);
			}
		} **/
		
		
		/* Augmented reality initialisation */
		private function initFLAR():void {
			/* Initialise FLARManager */
			this.fm = new FLARManager("flarConfig.xml");
	
			/* Temporary declaration of how many patterns are being used */ 
			var numPatterns:int = 6;
			/* Initialise markerByPatternId vector object */
			this.markersByPatternId = new Vector.<Vector.<FLARMarker>>(numPatterns, true);
			/* Loop through each pattern */
			while (numPatterns--) {
				/* Add empty Vector to each pattern */
				this.markersByPatternId[numPatterns] = new Vector.<FLARMarker>();
			}
	
			/* Initialise empty containersByMarker dictionary object */
			this.containersByMarker = new Dictionary(true);
	
			/* Initialise empty nodesByMarker dictionary object */
			this.nodesByMarker = new Dictionary(true);
	
			/* Event listener for when a new marker is recognised */
			fm.addEventListener(FLARMarkerEvent.MARKER_ADDED, this.onAdded);
			/* Event listener for when a marker is removed */
			fm.addEventListener(FLARMarkerEvent.MARKER_REMOVED, this.onRemoved);
			/* Event listener for when the FLARManager object has loaded */
			fm.addEventListener(Event.INIT, this.onFlarManagerLoad);
			
			//!!!!
			//2. added image material; 3: line 151
			imageMat0 = new BitmapFileMaterial("sqiconParR.png");
			imageMat1 = new BitmapFileMaterial("sqiconParFr.png");
			imageMat2 = new BitmapFileMaterial("sqiconScoFr.png");
			imageMat3 = new BitmapFileMaterial("sqiconUniFrR.png");
			imageMat4 = new BitmapFileMaterial("sqiconUniFr.png");
			imageMat5 = new BitmapFileMaterial("sqiconUniFr.png");
			imageMat0.addEventListener(FileLoadEvent.LOAD_COMPLETE, loadCompleteHandler);
			imageMat1.addEventListener(FileLoadEvent.LOAD_COMPLETE, loadCompleteHandler);
			imageMat2.addEventListener(FileLoadEvent.LOAD_COMPLETE, loadCompleteHandler);
			imageMat3.addEventListener(FileLoadEvent.LOAD_COMPLETE, loadCompleteHandler);
			imageMat4.addEventListener(FileLoadEvent.LOAD_COMPLETE, loadCompleteHandler);
			imageMat5.addEventListener(FileLoadEvent.LOAD_COMPLETE, loadCompleteHandler);
	
			/* Display webcam */
			this.addChild(Sprite(fm.flarSource));
		}
		
		/* Run if FLARManager object has loaded */
		private function onFlarManagerLoad(e:Event):void {
			/* Remove event listener so this method doesn't run again */
			this.fm.removeEventListener(Event.INIT, this.onFlarManagerLoad);
			/* Run Papervision initialisation method */
			this.initPaperVision();
		}
		
		/* Run when a new marker is recognised */
		private function onAdded(e:FLARMarkerEvent):void {
			/* Run method to add a new marker */
			this.addMarker(e.marker);					
			
		}
		/* Run when a marker is removed */
		private function onRemoved(e:FLARMarkerEvent):void {
			/* Run method to remove a marker */
			this.removeMarker(e.marker);	
		}
		
		//!!!! 
		//3. added handler for assigning loaded images; 4: line 181
		private function loadCompleteHandler(event:FileLoadEvent):void {
			cubeMaterials0 = new MaterialsList({front: imageMat0});
			cubeMaterials1 = new MaterialsList({front: imageMat1});
			cubeMaterials2 = new MaterialsList({front: imageMat2});
			cubeMaterials3 = new MaterialsList({front: imageMat3});
			cubeMaterials4 = new MaterialsList({front: imageMat4});
			cubeMaterials5 = new MaterialsList({front: imageMat5});
		}

		/* Add a new marker to the system */
		private function addMarker(marker:FLARMarker):void {
			/* Store reference to list of existing markers with same pattern id */
			var markerList:Vector.<FLARMarker> = this.markersByPatternId[marker.patternId];
			/* Add new marker to the list */
			markerList.push(marker);
			
			/* Initialise the marker container object */
			var container:DisplayObject3D = new DisplayObject3D();
			
			/* Prepare material to be used by the Papervision cube based on pattern id */
			var flatShaderMat:FlatShadeMaterial = new FlatShadeMaterial(pointLight3D, getColorByPatternId(marker.patternId), getColorByPatternId(marker.patternId, true));
			/* Add material to all sides of the cube */
			//var cubeMaterials:MaterialsList = new MaterialsList({all: flatShaderMat});
			//cubeMaterials = new MaterialsList({front: imageMat});
			
			/* Initialise the cube with material and set dimensions of all sides to 40 */
			
			//!!!!
			//4. added cube tracking the pattern; 5: line 363
			var cube:Cube = new Cube(getImageByPatternId(marker.patternId), 80, 80, 80);
			
			/* Shift cube upwards so it sits on top of paper instead of being cut half-way */
			/*cube.z = 0.5 * 80;
			
			/* Scale cube to 0 so it's invisible */
			cube.scale = 0;
			/* Add animation which scales cube to full size */
			Tweener.addTween(cube, {scale: 1, time:0.5, transition:"easeInOutExpo"});
			
			/* Set cube to be individually affected by filters */
			cube.useOwnContainer = true;
			/* Add cellshaded border using glow filter */
			cube.filters = [this.glow];
			
			/* Add finished cube object to marker container */
			container.addChild(cube);
			/* Add marker container to the Papervision scene */
			this.scene3D.addChild(container);
			
			/* Add marker container to containersByMarker Dictionary object */
			this.containersByMarker[marker] = container;
			
			//audio stuff
			//getAudioByPatternId(marker.patternId);		
			switch (marker.patternId)
				{
				case 0:
					Audio.playMusic();
					break;
				/**case 1:
					AudioFR.playMusic();
					break; **/
				Default: break;
				}
		}
		
		/* Remove a marker from the system */
		private function removeMarker(marker:FLARMarker):void {
			/* Store reference to list of existing markers with same pattern id */
			var markerList:Vector.<FLARMarker> = this.markersByPatternId[marker.patternId];
			/* Find index value of marker to be removed */
			var markerIndex:uint = markerList.indexOf(marker);
			/* If marker exists in markerList */
			if (markerIndex != -1) {
				/* Remove marker from markersByPatternId */
				markerList.splice(markerIndex, 1);
			}
			
			/* Store reference to marker container from containersByMarker Dictionary object */
			var container:DisplayObject3D = this.containersByMarker[marker];
			/* If a container exists */
			if (container) {
				/* Remove container from the Papervision scene */
				this.scene3D.removeChild(container);
			}
			/* Remove container reference from containersByMarker Dictionary object */
			delete this.containersByMarker[marker];
			
			/* Clear any corner nodes for this marker from the display */ 
			this.nodesByMarker[marker].graphics.clear();
			/* Remove reference to corner nodes for this marker from nodesByMarker Dictionary object */
			delete this.nodesByMarker[marker];
			Audio.stopMusic();
			//AudioFR.stopMusic();
			
		}
		
		/* Papervision initialisation method */
		private function initPaperVision():void {
			/* Initialise a new Papervision scene */
			this.scene3D = new Scene3D();
			/* Initialise a new FLARCamera3D object to enable full AR goodness */
			this.camera3D = new FLARCamera3D(this.fm.cameraParams);
			
			/* Define a new Papervision viewport object */
			this.viewport3D = new Viewport3D(640, 480, true);
			/* Add viewport to the main scene */
			this.addChild(this.viewport3D);
			
			/* Define a new Papervision point light */
			this.pointLight3D = new PointLight3D(true, false);
			/* Set light position */
			this.pointLight3D.x = 1000;
			this.pointLight3D.y = 1000;
			this.pointLight3D.z = -1000;
			/* Add light to the Papervision scene */
			this.scene3D.addChild(pointLight3D);
			
			/* Initialise the Papervision render engine */
			this.lre = new LazyRenderEngine(this.scene3D, this.camera3D, this.viewport3D);
			
			/* Create event listner to run a method on each frame */
			this.addEventListener(Event.ENTER_FRAME, this.onEnterFrame);
		}
		
		/* Method to run on each frame */
		private function onEnterFrame(e:Event):void {
			/* Loop through corner nodes */
			for (var marker:Object in nodesByMarker) {
				/* Clear any corner nodes for this marker from the display */ 
				nodesByMarker[marker].graphics.clear();
			}
			
			/* Run method to update markers */
			this.updateMarkers();
			
			/* Render the Papervision scene */
			this.lre.render();
		}
		
		/* Update markers method */
		private function updateMarkers():void {
			/* Store reference to amount of patterns being tracked */
			var i:int = this.markersByPatternId.length;
			/* Store reference to list of existing markers */
			var markerList:Vector.<FLARMarker>;
			/* Empty marker variable */
			var marker:FLARMarker;
			/* Empty container variable */
			var container:DisplayObject3D;
			/* Empty integer */
			var j:int;
			
			/* Loop through all tracked patterns */
			while (i--) {
				/* Store reference to all markers with this pattern id */
				markerList = this.markersByPatternId[i];
				/* Amount of markers with this pattern */
				j = markerList.length;
				/* Loop through markers with this pattern */
				while (j--) {
					/* Store reference to current marker */
					marker = markerList[j];
					
					/* Initialise a new Shape object */
					var nodes:Shape = new Shape();
					/* Define line style for corner nodes */
					nodes.graphics.lineStyle(3, getColorByPatternId(marker.patternId));
					/* Store reference to coordinates for each corner of the marker */
					var corners:Vector.<Point> = marker.corners;
					/* Empty coordinate variable */
					var vertex:Point;
					/* Loop through rest of corner coordinates */
					for (var c:uint=0; c<corners.length; c++) {
						/* Store reference to current corner coordinates */
						vertex = corners[c];
						/* Draw a 2D circle at these coordinates */
						nodes.graphics.drawCircle(vertex.x, vertex.y, 5);
					}
					/* Add reference to corner nodes to nodesByMarker Dictionary object */
					this.nodesByMarker[marker] = nodes;
					/* Add corner nodes to the main scene */
					this.addChild(nodes);
					
					/* Find reference to marker container in containersByMarker Dictionary object */
					container = this.containersByMarker[marker];
					/* Transform container to new position in 3d space */
					container.transform = FLARPVGeomUtils.convertFLARMatrixToPVMatrix(marker.transformMatrix);
					
					
				}
			}
		}
		
		/* Get colour values dependent on pattern id */
		private function getColorByPatternId(patternId:int, shaded:Boolean = false):Number {
			switch (patternId) {
				case 0:
					if (!shaded)
						return 0xFF1919;
					return 0x730000;
				case 1:
					if (!shaded)
						return 0xFF19E8;
					return 0x730067;
				case 2:
					if (!shaded)
						return 0x9E19FF;
					return 0x420073;
				case 3:
					if (!shaded)
						return 0x192EFF;
					return 0x000A73;
				case 4:
					if (!shaded)
						return 0x1996FF;
					return 0x003E73;
				case 5:
					if (!shaded)
						return 0x19FDFF;
					return 0x007273;
				default:
					if (!shaded)
						return 0xCCCCCC;
					return 0x666666;
			}
		}
		//!!!!
		//5. added tracking pattern to retrieve image
		private function getImageByPatternId(patternId:int, shaded:Boolean = false):MaterialsList {
			switch (patternId) {
				case 0:
					return cubeMaterials0;
				case 1:
					return cubeMaterials1;
				case 2:
					return cubeMaterials2;
				case 3:
					return cubeMaterials3;
				case 4:
					return cubeMaterials4;
				case 5:
					return cubeMaterials5;
				default:
					return cubeMaterials0;
			}
		}
		
		
	}
}