/**
 * Random
 * 
 * An ActionScript 3 implementation of a Random Number Generator
 * Copyright (c) 2007 Henri Torgemane
 * 
 * Derived from:
 *         The jsbn library, Copyright (c) 2003-2005 Tom Wu
 * 
 * See LICENSE.txt for full license information.
 */
package com.hurlant.crypto.prng
{
    import flash.utils.ByteArray;
    import com.hurlant.util.Memory;
    import flash.system.System;
    import flash.system.Capabilities;
    import flash.accessibility.AccessibilityProperties;
    import flash.display.SWFVersion;
    import flash.display.Stage;
    import flash.utils.getTimer;
    import flash.text.Font;
    
    public class Random
    {
        private var state:IPRNG;
        private var ready:Boolean = false;
        private var pool:ByteArray;
        private var psize:int;
        private var pptr:int;
        private var seeded:Boolean = false;
        
        public function Random(prng:Class = null) {
            if (prng==null) prng = ARC4;
            state = new prng as IPRNG;
            psize= state.getPoolSize();
            pool = new ByteArray;
            pptr = 0;
            while (pptr <psize) {
                var t:uint = 65536*Math.random();
                pool[pptr++] = t >>> 8;
                pool[pptr++] = t&255;
            }
            pptr=0;
            seed();
        }
        
        public function seed(x:int = 0):void {
            if (x==0) {
                x = new Date().getTime();
            }
            pool[pptr++] ^= x & 255;
            pool[pptr++] ^= (x>>8)&255;
            pool[pptr++] ^= (x>>16)&255;
            pool[pptr++] ^= (x>>24)&255;
            pptr %= psize;
            seeded = true;
        }
        
        /**
         * Gather anything we have that isn't entirely predictable:
         *  - memory used
         *  - system capabilities
         *  - timing stuff
         *  - installed fonts
         */
        public function autoSeed():void {
            var b:ByteArray = new ByteArray;
            b.writeUnsignedInt(System.totalMemory);
            b.writeUTF(Capabilities.serverString);
            b.writeUnsignedInt(getTimer());
            b.writeUnsignedInt((new Date).getTime());
            var a:Array = Font.enumerateFonts(true);
            for each (var f:Font in a) {
                b.writeUTF(f.fontName);
                b.writeUTF(f.fontStyle);
                b.writeUTF(f.fontType);
            }
            b.position=0;
            while (b.bytesAvailable>=4) {
                seed(b.readUnsignedInt());
            }
        }
        
        
        public function nextBytes(buffer:ByteArray, length:int):void {
            while (length--) {
                buffer.writeByte(nextByte());
            }
        }
        public function nextByte():int {
            if (!ready) {
                if (!seeded) {
                    autoSeed();
                }
                state.init(pool);
                pool.length = 0;
                pptr = 0;
                ready = true;
            }
            return state.next();
        }
        public function dispose():void {
            for (var i:uint=0;i<pool.length;i++) {
                pool[i] = Math.random()*256;
            }
            pool.length=0;
            pool = null;
            state.dispose();
            state = null;
            psize = 0;
            pptr = 0;
            Memory.gc();
        }
        public function toString():String {
            return "random-"+state.toString();
        }
    }
}