package  {
	import flash.display.*;
	import flash.events.*;
	import flash.net.LocalConnection;
	import flash.system.System;
	import flash.ui.*;
	import flash.utils.getTimer;	

	/**
	 * @author shanem
	 */
	public class SWFProfiler {
		private static var itvTime : int;
		private static var initTime : int;
		private static var currentTime : int;
		private static var frameCount : int;
		private static var totalCount : int;

		public static var minFps : Number;
		public static var maxFps : Number;
		public static var minMem : Number;
		public static var maxMem : Number;
		public static var history : int = 60;
		public static var fpsList : Array = [];
		public static var memList : Array = [];

		private static var displayed : Boolean = false;
		private static var started : Boolean = false;
		private static var inited : Boolean = false;
		private static var frame : Sprite;
		private static var stage : Stage;
		private static var content : ProfilerContent;
		private static var ci : ContextMenuItem;

		public static function init(swf : Stage, context : InteractiveObject) : void {
			if(inited) return;
			
			inited = true;
			stage = swf;
			
			content = new ProfilerContent();
			frame = new Sprite();
			
			minFps = Number.MAX_VALUE;
			maxFps = Number.MIN_VALUE;
			minMem = Number.MAX_VALUE;
			maxMem = Number.MIN_VALUE;
			
			var cm : ContextMenu = new ContextMenu();
			cm.hideBuiltInItems();
			ci = new ContextMenuItem("Show Profiler", true);
			addEvent(ci, ContextMenuEvent.MENU_ITEM_SELECT, onSelect);
			cm.customItems = [ci];
			context.contextMenu = cm;
			
			start();
		}

		public static function start() : void {
			if(started) return;
			
			started = true;
			initTime = itvTime = getTimer();
			totalCount = frameCount = 0;
			
			addEvent(frame, Event.ENTER_FRAME, draw);
		}

		public static function stop() : void {
			if(!started) return;
			
			started = false;
			
			removeEvent(frame, Event.ENTER_FRAME, draw);
		}

		public static function gc() : void {
			try {
				new LocalConnection().connect('foo');
				new LocalConnection().connect('foo');
			} catch (e : Error) {
			}
		}

		public static function get currentFps() : Number {
			return frameCount / intervalTime;
		}

		public static function get currentMem() : Number {
			return (System.totalMemory / 1024) / 1000;
		}

		public static function get averageFps() : Number {
			return totalCount / runningTime;
		}

		private static function get runningTime() : Number {
			return (currentTime - initTime) / 1000;
		}

		private static function get intervalTime() : Number {
			return (currentTime - itvTime) / 1000;
		}

		
		private static function onSelect(e : ContextMenuEvent) : void {
			if(!displayed) {
				show();
			} else {
				hide();
			}
		}

		private static function show() : void {
			ci.caption = "Hide Profiler";
			displayed = true;
			addEvent(stage, Event.RESIZE, resize);
			stage.addChild(content);
			updateDisplay();
		}

		private static function hide() : void {
			ci.caption = "Show Profiler";
			displayed = false;
			removeEvent(stage, Event.RESIZE, resize);
			stage.removeChild(content);
		}
		
		private static function resize(e:Event) : void {
			content.update(runningTime, minFps, maxFps, minMem, maxMem, currentFps, currentMem, averageFps, fpsList, memList, history);
		}
		
		private static function draw(e : Event) : void {
			currentTime = getTimer();
			
			frameCount++;
			totalCount++;

			if(intervalTime >= 1) {
				if(displayed) {
					updateDisplay();
				} else {
					updateMinMax();
				}
				
				fpsList.unshift(currentFps);
				memList.unshift(currentMem);
				
				if(fpsList.length > history) fpsList.pop();
				if(memList.length > history) memList.pop();
				
				itvTime = currentTime;
				frameCount = 0;
			}
		}

		private static function updateDisplay() : void {
			updateMinMax();
			content.update(runningTime, minFps, maxFps, minMem, maxMem, currentFps, currentMem, averageFps, fpsList, memList, history);
		}

		private static function updateMinMax() : void {
			minFps = Math.min(currentFps, minFps);
			maxFps = Math.max(currentFps, maxFps);
				
			minMem = Math.min(currentMem, minMem);
			maxMem = Math.max(currentMem, maxMem);
		}

		private static function addEvent(item : EventDispatcher, type : String, listener : Function) : void {
			item.addEventListener(type, listener, false, 0, true);
		}

		private static function removeEvent(item : EventDispatcher, type : String, listener : Function) : void {
			item.removeEventListener(type, listener);
		}
	}
}

import flash.display.*;
import flash.events.Event;
import flash.text.*;

internal class ProfilerContent extends Sprite {

	private var minFpsTxtBx : TextField;
	private var maxFpsTxtBx : TextField;
	private var minMemTxtBx : TextField;
	private var maxMemTxtBx : TextField;
	private var infoTxtBx : TextField;
	private var box : Shape;
	private var fps : Shape;
	private var mb : Shape;

	public function ProfilerContent() : void {
		fps = new Shape();
		mb = new Shape();
		box = new Shape();
			
		this.mouseChildren = false;
		this.mouseEnabled = false;
			
		fps.x = 65;
		fps.y = 45;	
		mb.x = 65;
		mb.y = 90;
			
		var tf : TextFormat = new TextFormat("_sans", 9, 0xAAAAAA);
			
		infoTxtBx = new TextField();
		infoTxtBx.autoSize = TextFieldAutoSize.LEFT;
		infoTxtBx.defaultTextFormat = new TextFormat("_sans", 11, 0xCCCCCC);
		infoTxtBx.y = 98;
			
		minFpsTxtBx = new TextField();
		minFpsTxtBx.autoSize = TextFieldAutoSize.LEFT;
		minFpsTxtBx.defaultTextFormat = tf;
		minFpsTxtBx.x = 7;
		minFpsTxtBx.y = 37;
			
		maxFpsTxtBx = new TextField();
		maxFpsTxtBx.autoSize = TextFieldAutoSize.LEFT;
		maxFpsTxtBx.defaultTextFormat = tf;
		maxFpsTxtBx.x = 7;
		maxFpsTxtBx.y = 5;
			
		minMemTxtBx = new TextField();
		minMemTxtBx.autoSize = TextFieldAutoSize.LEFT;
		minMemTxtBx.defaultTextFormat = tf;
		minMemTxtBx.x = 7;
		minMemTxtBx.y = 83;
			
		maxMemTxtBx = new TextField();
		maxMemTxtBx.autoSize = TextFieldAutoSize.LEFT;
		maxMemTxtBx.defaultTextFormat = tf;
		maxMemTxtBx.x = 7;
		maxMemTxtBx.y = 50;
			
		addChild(box);
		addChild(infoTxtBx);
		addChild(minFpsTxtBx);
		addChild(maxFpsTxtBx);
		addChild(minMemTxtBx);
		addChild(maxMemTxtBx);
		addChild(fps);
		addChild(mb);
		
		this.addEventListener(Event.ADDED_TO_STAGE, added, false, 0, true);
		this.addEventListener(Event.REMOVED_FROM_STAGE, removed, false, 0, true);
	}

	public function update(runningTime : Number, minFps : Number, maxFps : Number, minMem : Number, maxMem : Number, currentFps : Number, currentMem : Number, averageFps : Number, fpsList : Array, memList : Array, history : int) : void {
		if(runningTime >= 1) {
			minFpsTxtBx.text = minFps.toFixed(3) + " Fps";
			maxFpsTxtBx.text = maxFps.toFixed(3) + " Fps";
			minMemTxtBx.text = minMem.toFixed(3) + " Mb";
			maxMemTxtBx.text = maxMem.toFixed(3) + " Mb";
		}
			
		infoTxtBx.text = "Current Fps " + currentFps.toFixed(3) + "   |   Average Fps " + averageFps.toFixed(3) + "   |   Memory Used " + currentMem.toFixed(3) + " Mb";
		infoTxtBx.x = stage.stageWidth - infoTxtBx.width - 20;
		
		var vec : Graphics = fps.graphics;
		vec.clear();
		vec.lineStyle(1, 0x33FF00, 0.7);
			
		var i : int = 0;
		var len : int = fpsList.length;
		var height : int = 35;
		var width : int = stage.stageWidth - 80;
		var inc : Number = width / (history - 1);
		var rateRange : Number = maxFps - minFps;
		var value : Number;
			
		for(i = 0;i < len; i++) {
			value = (fpsList[i] - minFps) / rateRange;
			if(i == 0) {
				vec.moveTo(0, -value * height);
			} else {
				vec.lineTo(i * inc, -value * height);
			}
		}
			
		vec = mb.graphics;
		vec.clear();
		vec.lineStyle(1, 0x0066FF, 0.7);
			
		i = 0;
		len = memList.length;
		rateRange = maxMem - minMem;
		for(i = 0;i < len; i++) {
			value = (memList[i] - minMem) / rateRange;
			if(i == 0) {
				vec.moveTo(0, -value * height);
			} else {
				vec.lineTo(i * inc, -value * height);
			}
		}
	}

	private function added(e : Event) : void {
		resize();
		stage.addEventListener(Event.RESIZE, resize, false, 0, true);
	}

	private function removed(e : Event) : void {
		stage.removeEventListener(Event.RESIZE, resize);
	}

	private function resize(e : Event = null) : void {
		var vec : Graphics = box.graphics;
		vec.clear();
		
		vec.beginFill(0x000000, 0.5);
		vec.drawRect(0, 0, stage.stageWidth, 120);
		vec.lineStyle(1, 0xFFFFFF, 0.2);
			
		vec.moveTo(65, 45);
		vec.lineTo(65, 10);
		vec.moveTo(65, 45);
		vec.lineTo(stage.stageWidth - 15, 45);
			
		vec.moveTo(65, 90);
		vec.lineTo(65, 55);
		vec.moveTo(65, 90);
		vec.lineTo(stage.stageWidth - 15, 90);
			
		vec.endFill();
		
		infoTxtBx.x = stage.stageWidth - infoTxtBx.width - 20;
	}
}