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

    /**
     * The SWFProfiler is a degugging tool which allows for monitoring the amount of memory and the frame rate of a SWF.
     */
    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 refreshRate : Number = 1;
        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;

        
        /**
         * Iniates the SWFProfiler to the ContextMenu of the reference passed.
         */
        public static function init(context : InteractiveObject) : void {
            if(inited) return;
            
            inited = true;
            stage = context.stage;
            
            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);
            cm.customItems = [ci];
            context.contextMenu = cm;
            addEvent(ci, ContextMenuEvent.MENU_ITEM_SELECT, onSelect);
            
            start();
        }

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

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

        /**
         * Garbage collect the SWF.
         */
        public static function gc() : void {
            try {
                System.gc();
                new LocalConnection().connect('foo');
                new LocalConnection().connect('foo');
            } catch (e : Error) {
            }
        }

        /**
         * @return The current frames per second.
         */
        public static function get currentFps() : Number {
            return frameCount / intervalTime;
        }

        /**
         * @return The current amount of memory used by the SWF in MB.
         */
        public static function get currentMem() : Number {
            return (System.totalMemory / 1024) / 1000;
        }

        /**
         * @return The average frames per second.
         */
        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();
            }
        }

        public static function show() : void {
            ci.caption = "Hide Profiler";
            displayed = true;
            stage.addChild(content);
            updateDisplay();
        }

        public static function hide() : void {
            ci.caption = "Show Profiler";
            displayed = false;
            stage.removeChild(content);
        }

        private static function draw(e : Event = null) : void {
            currentTime = getTimer();
            
            frameCount++;
            totalCount++;
            
            if(intervalTime >= refreshRate) {
                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 {
            if(!(currentFps > 0)) return;
            
            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;
    }
}