package 
{
    import away3d.cameras.*;
    import away3d.containers.*;
    import away3d.core.clip.RectangleClipping;
    import away3d.core.math.*;
    import away3d.core.render.*;
    import away3d.core.utils.Cast;
    import away3d.core.utils.DofCache;
    import away3d.materials.*;
    import away3d.primitives.*;
    import away3d.sprites.*;
    
    import flash.display.*;
    import flash.events.*;
    import flash.geom.ColorTransform;
    import flash.geom.Matrix;
    
    [SWF(backgroundColor="#000000", frameRate="30", quality="LOW", width="800", height="600")]
    
    public class DofAtom extends Sprite
    {
        //red marble
        [Embed(source="assets/red.png")]
        public var RedMarble:Class;
        
        //green marble
        [Embed(source="assets/green.png")]
        public var GreenMarble:Class;
        
        //blue marble
        [Embed(source="assets/blue.png")]
        public var BlueMarble:Class;
        
        //signature swf
        [Embed(source="assets/signature.swf", symbol="Signature")]
        private var SignatureSwf:Class;
        
        //engine variables
        private var scene:Scene3D;
        private var camera:HoverCamera3D;
        private var view:View3D;
        
        //signature variables
        private var Signature:Sprite;
        private var SignatureBitmap:Bitmap;
        
        //material objects
        private var bitmapProton:BitmapData;
        private var bitmapNeutron:BitmapData;
        private var bitmapElectron:BitmapData;
        private var skyboxMaterial:TransformBitmapMaterial;
        private var skyboxBitmap:BitmapData;
        private var skyboxColorTransform:ColorTransform = new ColorTransform(1, 1, 1, 0.5);
        
        //scene objects
        private var proton:DofSprite2D;
        private var neutron:DofSprite2D;
        private var nucleicContainer:ObjectContainer3D;
        private var electron:DofSprite2D;
        private var electronContainer:ObjectContainer3D;
        private var skybox:Skybox;
        
        //electron variables
        private var electrons:Array = new Array();
        private var electronNum:int = 50;
        private var electronSpeed:int = 5;
        
        //navigation variables
        private var move:Boolean = false;
        private var lastPanAngle:Number;
        private var lastTiltAngle:Number;
        private var lastMouseX:Number;
        private var lastMouseY:Number;
        
        /**
         * Constructor
         */
        public function DofAtom() 
        {
            init();
        }
        
        /**
         * Global initialise function
         */
        private function init():void
        {
            initEngine();
            initMaterials();
            initObjects();
            initListeners();
        }
        
        /**
         * Initialise the engine
         */
        private function initEngine():void
        {
            scene = new Scene3D();
            camera = new HoverCamera3D({zoom:10, focus:100, distance:2000, yfactor:1});
            camera.targetpanangle = camera.panangle = 45;
            camera.targettiltangle = camera.tiltangle = 20;
            
            view = new View3D({scene:scene, camera:camera, session:new BitmapRenderSession(1), clip:new RectangleClipping(-400, -300, 400, 300)});
            view.x = 400;
            view.y = 300;
            view.addSourceURL("srcview/index.html");
            view.render();
            addChild( view );
            
            //add signature
            Signature = Sprite(new SignatureSwf());
            SignatureBitmap = new Bitmap(new BitmapData(Signature.width, Signature.height, true, 0));
            stage.quality = StageQuality.HIGH;
            SignatureBitmap.bitmapData.draw(Signature);
            stage.quality = StageQuality.LOW;
            addChild(SignatureBitmap);
        }
        
        /**
         * Initialise the materials
         */
        private function initMaterials():void
        {
            //create bitmapData for DofSprites
            bitmapProton = Cast.bitmap(RedMarble);
            bitmapNeutron = Cast.bitmap(BlueMarble);
            bitmapElectron = Cast.bitmap(GreenMarble);
            
            //create skybox material from view bitmap
            skyboxBitmap = view.getBitmapData().clone();
            skyboxMaterial = new TransformBitmapMaterial(Cast.bitmap(skyboxBitmap), {scaleX:0.1, scaleY:0.1, repeat:true});
        }
        
        /**
         * Initialise the scene objects
         */
        private function initObjects():void
        {
            //setup depth of field constants
            DofCache.aperture = 100;
            DofCache.usedof = true;
            DofCache.maxblur = 50;
            DofCache.focus = 2000;
            
            //create particles
            var i:int = electronNum;
            while (i--)
            {
                proton = new DofSprite2D(bitmapProton, {scaling:1, x:180, y:0, z:0, ownCanvas:true});
                proton.blendMode = BlendMode.DIFFERENCE;
                neutron = new DofSprite2D(bitmapNeutron, {scaling:1, x:-180, y:0, z:0, ownCanvas:true});
                neutron.blendMode = BlendMode.DIFFERENCE;
                nucleicContainer = new ObjectContainer3D({rotationX:i*360/electronNum, rotationZ:i*180/electronNum, rotationY:i*200/6}, proton, neutron);
                scene.addChild( nucleicContainer );
                
                electron = new DofSprite2D(bitmapElectron, {scaling:1, x:0, y:500, z:0,ownCanvas:true});
                electron.blendMode = BlendMode.DIFFERENCE;
                electronContainer = new ObjectContainer3D({rotationX:i*360/electronNum, rotationZ:i*180/electronNum, rotationY:i*360/6}, electron );
                electrons.push(electronContainer);
                scene.addChild( electronContainer );
            }
            
            skybox = new Skybox(skyboxMaterial, skyboxMaterial, skyboxMaterial, skyboxMaterial, skyboxMaterial, skyboxMaterial);
            scene.addChild(skybox);
        }
        
        /**
         * Initialise the listeners
         */
        private function initListeners():void
        {
            addEventListener( Event.ENTER_FRAME, onEnterFrame );
            stage.addEventListener(MouseEvent.MOUSE_DOWN, onMouseDown);
            stage.addEventListener(MouseEvent.MOUSE_UP, onMouseUp);
            onResize(null);
        }
        
        /**
         * Navigation and render loop
         */
        private function onEnterFrame( e:Event ):void
        {
            if (move) {
                camera.targetpanangle = 0.3*(stage.mouseX - lastMouseX) + lastPanAngle;
                camera.targettiltangle = 0.3*(stage.mouseY - lastMouseY) + lastTiltAngle;
            }
            
            for each (electronContainer in electrons)
                electronContainer.rotationX += electronSpeed;
            
            //copy view bitmap to skybox
            skyboxBitmap.fillRect(skyboxBitmap.rect, 0);
            skyboxBitmap.draw(view.getBitmapData(), null, skyboxColorTransform, BlendMode.DIFFERENCE);
            
            camera.hover();
            view.render();
        }
        
        /**
         * Mouse down listener for navigation
         */
        private function onMouseDown(event:MouseEvent):void
        {
            lastPanAngle = camera.targetpanangle;
            lastTiltAngle = camera.targettiltangle;
            lastMouseX = stage.mouseX;
            lastMouseY = stage.mouseY;
            move = true;
            stage.addEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);
        }
        
        /**
         * Mouse up listener for navigation
         */
        private function onMouseUp(event:MouseEvent):void
        {
            move = false;
            stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);     
        }
        
        /**
         * Mouse stage leave listener for navigation
         */
        private function onStageMouseLeave(event:Event):void
        {
            move = false;
            stage.removeEventListener(Event.MOUSE_LEAVE, onStageMouseLeave);     
        }
        
        /**
         * Stage listener for resize events
         */
        private function onResize(event:Event):void
        {
            view.x = stage.stageWidth / 2;
            view.y = stage.stageHeight / 2;
            SignatureBitmap.y = stage.stageHeight - Signature.height;
        }
    }
}