/**
 * Stats
 *
 * Released under MIT license:
 * http://www.opensource.org/licenses/mit-license.php
 *
 * How to use:
 *
 *    addChild( new Stats() );
 *
 *    or
 *
 *    addChild( new Stats( { bg: 0xffffff } );
 *
 * version log:
 *
 *    09.10.22        2.2        Mr.doob            + FlipX of graph to be more logic.
 *                                            + Destroy on Event.REMOVED_FROM_STAGE (thx joshtynjala)
 *    09.03.28        2.1        Mr.doob            + Theme support.
 *    09.02.21        2.0        Mr.doob            + Removed Player version, until I know if it's really needed.
 *                                            + Added MAX value (shows Max memory used, useful to spot memory leaks)
 *                                            + Reworked text system / no memory leak (original reason unknown)
 *                                            + Simplified
 *    09.02.07        1.5        Mr.doob            + onRemovedFromStage() (thx huihuicn.xu)
 *    08.12.14        1.4        Mr.doob            + Code optimisations and version info on MOUSE_OVER
 *    08.07.12        1.3        Mr.doob            + Some speed and code optimisations
 *    08.02.15        1.2        Mr.doob            + Class renamed to Stats (previously FPS)
 *    08.01.05        1.2        Mr.doob            + Click changes the fps of flash (half up increases, half down decreases)
 *    08.01.04        1.1        Mr.doob            + Shameless ripoff of Alternativa's FPS look :P
 *                            Theo            + Log shape for MEM
 *                                            + More room for MS
 *     07.12.13        1.0        Mr.doob            + First version
 **/

package
{
    import flash.display.Bitmap;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Rectangle;
    import flash.system.System;
    import flash.text.StyleSheet;
    import flash.text.TextField;
    import flash.utils.getTimer;

    public class Stats extends Sprite
    {
        protected const WIDTH:uint = 70;
        protected const HEIGHT:uint = 100;

        protected var xml:XML;

        protected var text:flash.text.TextField;
        protected var style:flash.text.StyleSheet;

        protected var timer:uint;
        protected var fps:uint;
        protected var ms:uint;
        protected var ms_prev:uint;
        protected var mem:Number;
        protected var mem_max:Number;

        protected var graph:Bitmap;
        protected var rectangle:Rectangle;

        protected var fps_graph:uint;
        protected var mem_graph:uint;
        protected var mem_max_graph:uint;

        protected var theme:Object = { bg: 0x000033, fps: 0xffff00, ms: 0x00ff00, mem: 0x00ffff, memmax: 0xff0070 };

        /**
         * <b>Stats</b> FPS, MS and MEM, all in one.
         *
         * @param _theme         Example: { bg: 0x202020, fps: 0xC0C0C0, ms: 0x505050, mem: 0x707070, memmax: 0xA0A0A0 }
         */
        public function Stats(_theme:Object = null):void
        {
            if (_theme)
            {
                if (_theme.bg != null) theme.bg = _theme.bg;
                if (_theme.fps != null) theme.fps = _theme.fps;
                if (_theme.ms != null) theme.ms = _theme.ms;
                if (_theme.mem != null) theme.mem = _theme.mem;
                if (_theme.memmax != null) theme.memmax = _theme.memmax;
            }

            mem_max = 0;

            xml = <xml>
                <fps>FPS:</fps>
                <ms>MS:</ms>
                <mem>MEM:</mem>
                <memMax>MAX:</memMax>
            </xml>;

            style = new StyleSheet();
            style.setStyle("xml", {fontSize:'9px', fontFamily:'_sans', leading:'-2px'});
            style.setStyle("fps", {color: hex2css(theme.fps)});
            style.setStyle("ms", {color: hex2css(theme.ms)});
            style.setStyle("mem", {color: hex2css(theme.mem)});
            style.setStyle("memMax", {color: hex2css(theme.memmax)});

            text = new flash.text.TextField();
            text.width = WIDTH;
            text.height = 50;
            text.styleSheet = style;
            text.condenseWhite = true;
            text.selectable = false;
            text.mouseEnabled = false;

            graph = new Bitmap();
            graph.y = 50;

            rectangle = new Rectangle(WIDTH - 1, 0, 1, HEIGHT - 50);

            addEventListener(Event.ADDED_TO_STAGE, init, false, 0, true);
            addEventListener(Event.REMOVED_FROM_STAGE, destroy, false, 0, true);
        }

        private function init(e:Event):void
        {
            graphics.beginFill(theme.bg);
            graphics.drawRect(0, 0, WIDTH, HEIGHT);
            graphics.endFill();

            addChild(text);

            graph.bitmapData = new BitmapData(WIDTH, HEIGHT - 50, false, theme.bg);
            addChild(graph);

            addEventListener(MouseEvent.CLICK, onClick);
            addEventListener(Event.ENTER_FRAME, update);
        }

        private function destroy(e:Event):void
        {
            graphics.clear();

            while (numChildren > 0)
                removeChildAt(0);

            graph.bitmapData.dispose();

            removeEventListener(MouseEvent.CLICK, onClick);
            removeEventListener(Event.ENTER_FRAME, update);
        }

        private function update(e:Event):void
        {
            timer = getTimer();

            if (timer - 1000 > ms_prev)
            {
                ms_prev = timer;
                mem = Number((System.totalMemory * 0.000000954).toFixed(3));
                mem_max = mem_max > mem ? mem_max : mem;

                fps_graph = Math.min(graph.height, ( fps / stage.frameRate ) * graph.height);
                mem_graph = Math.min(graph.height, Math.sqrt(Math.sqrt(mem * 5000))) - 2;
                mem_max_graph = Math.min(graph.height, Math.sqrt(Math.sqrt(mem_max * 5000))) - 2;

                graph.bitmapData.scroll(-1, 0);

                graph.bitmapData.fillRect(rectangle, theme.bg);
                graph.bitmapData.setPixel(graph.width - 1, graph.height - fps_graph, theme.fps);
                graph.bitmapData.setPixel(graph.width - 1, graph.height - ( ( timer - ms ) >> 1 ), theme.ms);
                graph.bitmapData.setPixel(graph.width - 1, graph.height - mem_graph, theme.mem);
                graph.bitmapData.setPixel(graph.width - 1, graph.height - mem_max_graph, theme.memmax);

                xml.fps = "FPS: " + fps + " / " + stage.frameRate;
                xml.mem = "MEM: " + mem;
                xml.memMax = "MAX: " + mem_max;

                fps = 0;
            }

            fps++;

            xml.ms = "MS: " + (timer - ms);
            ms = timer;

            text.htmlText = xml;
        }

        private function onClick(e:MouseEvent):void
        {
            mouseY / height > .5 ? stage.frameRate-- : stage.frameRate++;
            xml.fps = "FPS: " + fps + " / " + stage.frameRate;
            text.htmlText = xml;
        }

        // .. Utils

        private function hex2css(color:int):String
        {
            return "#" + color.toString(16);
        }
    }
}