package  {
	
	import flash.display.Sprite;
	import flash.display.Stage;
	
	import flash.text.TextField;
	import flash.text.TextFormat;
	import flash.text.TextFieldAutoSize;
	import flash.text.TextFormatAlign;
	import flash.text.AntiAliasType;
	import flash.geom.Rectangle;
	
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.system.System;
	import flash.utils.getTimer;
	
	import com.greensock.TweenLite;
	
	/*
	Debug Util - simple memory managment, grawl-like flash notifications, easy button setup
	Author: Chrysto Panayotov ( burnandbass[at]gmail.com )
	
	Dependencies: TweenLite ( http://tweenlite.com/ )
	
	Usage:  Debug.create(this.stage);
	this creates stats winndow on the stage, when you click it outputs more information
	
	public static functions:
	
	Debug.log( message:String ) --> Outputs the message
	Debug.error( message:String ) --> Outputs the message (in red)
	Debug.addButton(name:String, funk:Function);
	
	the this will add new button to the manager and execute the function when the button is clicked
	
	example: 		Debug.addButton("Simple Button", function():void{ trace("Hello from Debug"); });
	
	Debug.destroy()  --> removes the manager and clears all EventListeners
	
	Default: When you click on the manager it will grawl information : current memory, the maximum memory flash player reached
	and the free memory, also the stageWidth and stageHeight
	
	*/
	
	
	public class Debug extends Sprite{
		
		private static var _stage:Stage;
		
		private static var _debug:Sprite;
		private static var _debugText:TextField;
		private static var _time:Number;
		private static var _maxMemory:Number = 0;
		
		private static var _grawlHeight:Number = 0;
		private static var _created:Boolean = false;
		
		private static var _grawlOptions:Object = {
			grawlTextWidth: 350,     //width of the text field
			padding: 10,             // padding - from bottom, right and between notifications
			openTime: 0.7,           // open time
			closeTime: 0.5,         // close time
			stayTime: 4             // time in seconds that the notification stays
		}
		
		public function Debug() {
			throw new Error("Use Debug.create(this.stage) to make instance of debug panel");
		}
		
		/*
		This is how you initialize the manager, just use Debug.create(this.stage) in the main class
		*/
		public static function create(stage:Stage):void{
			if(!_created){
				_created = true;
				_stage = stage;
				_time = getTimer();
				
				createDebug();
				
				_stage.addChild(_debug);
				_debug.x = _debug.y = 5;
				_stage.addEventListener(Event.ADDED, popDebug, false, 0, true);
				_stage.addEventListener(Event.ENTER_FRAME, update);
			} else {
				Debug.error("DebugPanel is already created!");
			}
		}
		
		/*
		Outputs message on the screen 
		*/
		public static function log(message:String):void{
			grawl("log", message);
		}
		
		/*
		Outputs error on the screen
		*/
		public static function error(message:String):void{
			grawl("error", message);
		}
		
		public static function addButton(name:String, onClick:Function):void{
			var btn:Sprite = new Sprite();
			
			var format:TextFormat = new TextFormat();
			format.align = TextFormatAlign.LEFT;
			format.color = 0xFFFFFF;
			format.font = "Helvetica";
			format.size = 12;
			
			var label:TextField = new TextField();
			label.autoSize = TextFieldAutoSize.LEFT;
			label.embedFonts = false;
			label.selectable = false;
			label.defaultTextFormat = format;
			label.x = _debugText.y = 3;
			label.text = name;
			
			btn.graphics.beginFill(0x000000, 1);
			btn.graphics.drawRoundRect(0,0,label.textWidth + 12,20,10,10);
			btn.graphics.endFill();
			btn.buttonMode = true;
			btn.mouseChildren = false;
			btn.addChild(label);
			label.x = label.y = 3;
			
			btn.addEventListener(MouseEvent.CLICK, onClick);
			
			_debug.addChild(btn);
			btn.x = _debug.width + 2;
			
		}
		
		/*
		Internal function, creates the panel and the textfield
		*/
		private static function createDebug():void{
			_debug = new Sprite();
			_debug.graphics.beginFill(0x000000, 1);
			_debug.graphics.drawRoundRect(0,0,110,20,10,10);
			_debug.graphics.endFill();
			_debug.mouseChildren = true;
			
			var format:TextFormat = new TextFormat();
			format.align = TextFormatAlign.LEFT;
			format.color = 0xFFFFFF;
			format.font = "Helvetica";
			format.size = 12;
			
			_debugText = new TextField();
			_debugText.autoSize = TextFieldAutoSize.LEFT;
			_debugText.embedFonts = false;
			_debugText.selectable = false;
			_debugText.defaultTextFormat = format;
			
			_debug.addChild(_debugText);
			_debugText.x = _debugText.y = 3;
			
			_debugText.text = "";
			
			_debug.addEventListener(MouseEvent.ROLL_OVER, onDebugOver, false, 0, true);
			_debug.addEventListener(MouseEvent.ROLL_OUT, onDebugOut, false, 0, true);
			_debugText.addEventListener(MouseEvent.CLICK, onDebugClick, false, 0, true);
		}
		
		
		/*
		internal function called on every "enterFrame" event, calculates the memory and fps
		*/
		private static function update(event:Event):void{
			var memory:Number = Number((System.totalMemory / 1024 / 1024).toFixed(2));
			if(memory > _maxMemory){
				_maxMemory = memory;
			}
			var fps:Number = Number (1000 / (getTimer() - _time));
			_debugText.text = memory + " mb / " + Math.ceil(fps) + " fps";
			_time = getTimer();
		}
		
		/*
		Internal function that actually creates the grawl
		*/
		
		private static function grawl(type:String, text:String):void{
			var padding:Number = 10;
			
			var format:TextFormat = new TextFormat();
			format.align = TextFormatAlign.LEFT;
			format.color = type == "log" ? 0xFFFFFF : 0x000000;
			format.font = "Helvetica";
			format.size = 14;
			
			var grawlText:TextField = new TextField();
			grawlText.width  = _grawlOptions.grawlTextWidth;
			grawlText.multiline = true;
			grawlText.wordWrap = true;
			grawlText.autoSize = TextFieldAutoSize.LEFT;
			grawlText.embedFonts = false;
			grawlText.selectable = true;
			grawlText.defaultTextFormat = format;
			grawlText.text = text;
			
			var bg:Sprite = new Sprite();
			var bgColor:uint = type == "log" ? 0x000000 : 0xFF0000;
			bg.graphics.beginFill(bgColor, 1);
			bg.graphics.drawRoundRect(0,0, grawlText.width + padding*2, grawlText.height + padding*2, 10,10);
			bg.graphics.endFill();
			bg.addChild(grawlText);
			grawlText.x = padding;
			grawlText.y = padding;
			
			_stage.addChild(bg);
			bg.alpha = 0;
			bg.x = _stage.stageWidth - bg.width - _grawlOptions.padding;
			bg.y = _stage.stageHeight - bg.height - _grawlOptions.padding - _grawlHeight;
			_grawlHeight += bg.height + _grawlOptions.padding;
			TweenLite.to(bg, _grawlOptions.openTime, {alpha: 0.9});
			TweenLite.delayedCall(_grawlOptions.stayTime, function():void{
								  TweenLite.to(bg, _grawlOptions.closeTime, {alpha:0, onComplete:function():void{
											_stage.removeChild(bg);
											_grawlHeight -= bg.height + _grawlOptions.padding;
											   }})
								  });
			
		}
		
		/*
		Tween when you roll over the panel
		*/
		private static function onDebugOver(event:MouseEvent):void{
			TweenLite.to(_debug, 0.5, {alpha: 1});
		}
		
		/*
		Tween when you roll out the panel
		*/
		private static function onDebugOut(event:MouseEvent):void{
			TweenLite.to(_debug, 0.5, {alpha: 0.5});
		}
		
		/*
		Shows the current memory, maximum memory and fps
		*/
		private static function onDebugClick(event:MouseEvent):void{
			log("[Memory] : " + Number((System.totalMemory / 1024 / 1024).toFixed(2)) + " max: " + _maxMemory + " free: " + Number((System.freeMemory / 1024 / 1024).toFixed(2))
				+ "\n"
				+ "[Stage ] : width: " + _stage.stageWidth + " height: " + _stage.stageHeight)
			// you can extend this with custom vars, you wanna output
		}
		
		/*
		internal function , sets the panel on the top of stages display list
		*/
		private static function popDebug(event:Event):void{
			_stage.setChildIndex(_debug, _stage.numChildren-1);
		}
		
		//////////////////////////// DESTROY //////////////////////////////////////////////
		
		/*
		Destroys the manager
		*/
		
		public static function destroy():void{
			_stage.removeEventListener(Event.ADDED, popDebug);
			_stage.removeEventListener(Event.ENTER_FRAME, update)
			_debug.removeEventListener(MouseEvent.ROLL_OVER, onDebugOver);
			_debug.removeEventListener(MouseEvent.ROLL_OUT, onDebugOut);
			_debugText.removeEventListener(MouseEvent.CLICK, onDebugClick);
			
			_stage.removeChild(_debug);
			System.gc();
		}
		
	}//end	
}