package  {
	
 	import flash.display.BitmapData;
 	import flash.filters.BlurFilter;
 	import flash.media.SoundMixer;
 	import flash.utils.ByteArray;
 	import flash.events.Event;
 	import flash.geom.Rectangle;
 	import mx.core.UIComponent;
 	import mx.containers.Canvas;
 	import flash.display.Bitmap;
 	import mx.styles.CSSStyleDeclaration;
 	import mx.styles.StyleManager;
 	import flash.ui.ContextMenuBuiltInItems;
 	import flash.ui.ContextMenuItem;
 	import flash.ui.ContextMenu;
 	import flash.events.ContextMenuEvent;
 	import flash.geom.Point;
 	
	
	[Event("afterVisualization")]
	[Event("beforeVisualization")]
	[Style(name="audioLineColor",type="Number",format="Color",inherit="no")]
	[Style(name="audioFillColor",type="Number",format="Color",inherit="no")]
 	
 	public class actionscript_application_sound_com_fusiox_ui_Visualization extends UIComponent {
 		
 		public var type:String = "line"; // line, wave, bars
  		public var channel:String="mono"; // mono, left, right, stereo
  		public var bars:uint = 256;
  		
  		private var _audioLineColor:uint = 0x000000;
  		private var _audioFillColor:uint = 0x000000;
  		//private var min:uint = 0;
  		//private var max:uint = 255;
  		private var gain:uint = 1;
  		private var myContextMenu:ContextMenu;
  		
 		private var display:UIComponent = new UIComponent();
  		public var bitmapData:BitmapData = new BitmapData(1, 1, true, 0x00000000);
  		public var bitmap:Bitmap = new Bitmap(bitmapData);
  		private var spectrumData:ByteArray = new ByteArray();
  		private var rc:Number = 0; // relativeCenter
  		private var rp:Number = 0; // relativePixel
  		public var peak:Number = 0;
  		
  		
  		public function actionscript_application_sound_com_fusiox_ui_Visualization() {
   			addEventListener(Event.ENTER_FRAME, enterFrameListener);
   			
   			myContextMenu = new ContextMenu();
   			myContextMenu.hideBuiltInItems();
            var defaultItems:ContextMenuBuiltInItems = myContextMenu.builtInItems;
            defaultItems.print = true;
            
            var item1:ContextMenuItem = new ContextMenuItem("Line");
            var item2:ContextMenuItem = new ContextMenuItem("Wave");
            var item3:ContextMenuItem = new ContextMenuItem("Bars");
            myContextMenu.customItems.push(item1);
            myContextMenu.customItems.push(item2);
            myContextMenu.customItems.push(item3);
            
            myContextMenu.customItems.push(item3);
            
            
            item1.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuItemSelectHandler);
            item2.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuItemSelectHandler);
            item3.addEventListener(ContextMenuEvent.MENU_ITEM_SELECT, menuItemSelectHandler);
            this.contextMenu = myContextMenu;
   		}
        
        private function menuItemSelectHandler(event:ContextMenuEvent):void {
            //trace("menuItemSelectHandler: " + event);
            setType(event.target.caption);
        }
        
        private function setType( t:String ):void {
        	type = t.toLowerCase();
        }
        
   		override protected function createChildren():void {
   			//display.addChild(bitmap);
   			addChild(bitmap);
   		}
   		
   		override protected function updateDisplayList(w:Number, h:Number):void {
   			super.updateDisplayList(w, h);
   			var bt:uint = getStyle("borderThickness")*2;
   			if(w-bt>0 && h-bt>1) {bitmapData = new BitmapData(w-bt, h-bt, true, 0x0);}
   			bitmap.bitmapData = bitmapData;
   			_audioLineColor = getStyle("audioLineColor");
   			_audioFillColor = getStyle("audioFillColor");
   			// convenience variables to skip useless calls to getters
   			rc = (h-bt)/2;
   			rp = (w-bt)/256;
   		}
   		
  		private function enterFrameListener(e:Event):void {
  			var rect:Rectangle = bitmapData.rect
   			SoundMixer.computeSpectrum(spectrumData, type=="bars", 0);
			dispatchEvent( new Event("beforeVisualization",true) );
			if(!hasEventListener( "beforeVisualization" )) { bitmapData.fillRect( rect, 0x0); }
			switch (channel) {
				case "mono":
					toMono();
				case "left":
					if(type=="line" || type=="wave") { drawWave(1); }
					if(type=="wave") {
						spectrumData.position=0;
						drawWave(-1);
					}
					if(type=="bars") {
						drawEQ();
					}
					break;
				case "right":
					for (var i:int = 0; i < 256 ; i++) {spectrumData.readFloat();} // using spectrumData.position yields poor results
					if(type=="line" || type=="wave") { drawWave(1); }
					if(type=="wave") {
						spectrumData.position=0;
						for (var i:int = 0; i < 256 ; i++) {spectrumData.readFloat();}
						drawWave(-1);
					}
					if(type=="bars") {
						drawEQ();
					}
					break;
				case "stereo":
					if(type=="line" || type=="wave") {
						drawWave(1);
						drawWave(1);
					}
					if(type=="wave") {
						spectrumData.position=0;
						drawWave(-1);
						drawWave(-1);
					}
					if(type=="bars") {
						drawEQ();
						drawEQ();
					}
					break;
			}
			bitmapData.applyFilter( bitmapData, rect, new Point(0,0), new BlurFilter(bitmapData.width/256,bitmapData.width/256,1));
			dispatchEvent( new Event("afterVisualization",true) );
		}
		
		private function toMono():void {
			spectrumData.position = 0;
			if(spectrumData.length==2048) {
				var leftData:ByteArray = new ByteArray();
   				var rightData:ByteArray = new ByteArray();
   				spectrumData.readBytes(leftData, 0, 1024);
   				spectrumData.readBytes(rightData, 0, 1024);
   				spectrumData = new ByteArray();
   				for (var i:uint = 0; i < 256 ; i++) {
   					spectrumData.writeFloat((leftData.readFloat()+rightData.readFloat())/2);
   				}
   				spectrumData.position = 0;
   			}
		}
		
		/*
		private function narrowEQ():void {
			var tempData:ByteArray = new ByteArray();
			spectrumData.readBytes(tempData, 0, 1024);
			spectrumData = new ByteArray();
			var n:Number = 0;
			if(min>=max) {
				max=255;
				min=0;
			}
			for (var i:uint = 0; i < min; i++) { n = tempData.readFloat(); } // gets to min
   			for (var i:uint = 0; i <= max-min; i++) {
   				n = tempData.readFloat()
   				for (var j:uint = 0; j < Math.floor(255/max-min+1); j++) {
   					spectrumData.writeFloat(n);
   				}
   			}
   			spectrumData.position = 0;
		}
		*/
		
		private function drawWave(modifier:Number=1):void {
			var v:Number;
   			var lastv:Number = rc;
   			peak = 0;
   			for (var i:uint = 0; i < 256; i++) {
   				var rf:Number = spectrumData.readFloat();
			    v = rc+rf*rc*modifier*-1;
		    	if(type=="wave"){bitmapData.fillRect( new Rectangle(i*rp, Math.min(v,rc), rp, Math.abs(rc-v)), 0xFF000000 | _audioFillColor );}
   				bitmapData.fillRect( new Rectangle(i*rp, v, rp, 1+Math.abs(lastv-v)), 0xFF000000 | _audioLineColor );
		    	lastv=v;
		    	if(Math.abs(rf)>peak) { peak = Math.abs(rf); }
		    }
		}
		
		private function drawEQ():void {
			//narrowEQ();
			var v:Number;
   			var n:Number = 0;
   			var nrp:Number = rp*256/bars;
   			for (var i:uint = 0; i < bars; i++) {
   				n = 0;
	   			for (var j:uint = 0; j < Math.floor(256/bars) ; j++) {
					n += spectrumData.readFloat();
					if(Math.abs(n)>peak) { peak = Math.abs(n); }
			    }
			    n = n/Math.floor(256/bars)
			    v = Math.max(rc*2-n*rc*2*gain,5);
			    
		    	bitmapData.fillRect( new Rectangle(nrp/8+i*nrp, v, nrp/2+nrp/4, rc*2-v), 0xFF000000 | _audioFillColor);
   				bitmapData.fillRect( new Rectangle(nrp/8+i*nrp, v, nrp/2+nrp/4, 1), 0xFF000000 | _audioLineColor );
		    }
		}
		
  	}
}