package {
    import flash.display.Sprite;
    import flash.events.Event;
    import flash.events.MouseEvent;

    import org.papervision3d.cameras.Camera3D;
    import org.papervision3d.materials.ColorMaterial;
    import org.papervision3d.materials.utils.MaterialsList;
    import org.papervision3d.objects.DisplayObject3D;
    import org.papervision3d.objects.primitives.Cube;
    import org.papervision3d.render.BasicRenderEngine;
    import org.papervision3d.scenes.Scene3D;
    import org.papervision3d.view.Viewport3D;

    [SWF(width=640, height=480, backgroundColor=0xe0e0d0, frameRate=30)]
    public class BoundCube3D extends Sprite
    {
        static public const SCREEN_C:uint = 0xe0e0d0,
                            SCREEN_W:int = 640,
                            SCREEN_H:int = 480,
                            CUBE_SIZE:int = 100,
                            CUBE_XNUM:int = 5,
                            CUBE_ZNUM:int = 5,
                            JUMP_V:int = 200,
                            JUMP_ROT:int = 20,
                            JUMP_G:Number = 9.8,
                            JUMP_T:Number = 0.15,
                            JUMP_E:Number = 0.4;

        private var base:Sprite;
        private var scene:Scene3D;
        private var viewport:Viewport3D;
        private var camera:Camera3D;
        private var render:BasicRenderEngine;

        public function BoundCube3D()
        {
            init3D();
        }

        /**
         * @brief   初期化
         */
        private function init3D():void
        {
            // ベース画面を設定
            this.base = new Sprite();
            this.base.graphics.beginFill( SCREEN_C );
            this.base.graphics.drawRect( 0, 0, SCREEN_W, SCREEN_H );
            this.base.graphics.endFill();
            this.addChild( this.base );

            // シーン設定
            this.scene = new Scene3D();
            // ビューポート設定
            this.viewport = new Viewport3D( SCREEN_W, SCREEN_H );
            this.base.addChild( this.viewport );
            // レンダー設定
            this.render = new BasicRenderEngine();

            // キューブ作成
            var materialList:MaterialsList = new MaterialsList( { all: new ColorMaterial( 0xffffff ),
                                                                  front: new ColorMaterial( 0xe0e050 ),
                                                                  back: new ColorMaterial( 0xe0e050 ),
                                                                  top: new ColorMaterial( 0x5050e0 ),
                                                                  bottom: new ColorMaterial( 0x5050e0 ),
                                                                  left: new ColorMaterial( 0x50e050 ),
                                                                  right: new ColorMaterial( 0x50e050 ) } );
            var resizeLeft:int = ((CUBE_SIZE * 2) * (CUBE_XNUM-1)) / 2; // (0,0,0)を中心に位置調整
            var resizeBack:int = ((CUBE_SIZE * 2) * (CUBE_ZNUM-1)) / 2; // (0,0,0)を中心に位置調整
            for( var i:int = 0; i < CUBE_XNUM; ++i )
            {
                for( var j:int = 0; j < CUBE_ZNUM; ++j )
                {
                    var cube:DisplayObject3D = new Cube( materialList, CUBE_SIZE, CUBE_SIZE, CUBE_SIZE );
                    cube.x = CUBE_SIZE * j * 2 - resizeLeft;
                    cube.z = CUBE_SIZE * i * 2 - resizeBack;
                    cube.rotationY = Math.random() * 360;
                    cube.extra = new Object;
                    cube.extra.isjump = false;
                    this.scene.addChild( cube, 'cube_' + i.toString() + '_' + j.toString() );
                }
            }

            // カメラ設定
            this.camera = new Camera3D();
            this.camera.y = 500;
            this.camera.z = -1000;
            // なるべく真ん中にいるキューブを見るように設定
            this.camera.lookAt( this.scene.getChildByName( 'cube_' + Math.floor( CUBE_XNUM / 2 ).toString() + '_' + Math.floor( CUBE_ZNUM / 2 ).toString() ) );

            // イベント設定
            this.base.addEventListener( MouseEvent.CLICK, clickBound3D );
            this.base.addEventListener( Event.ENTER_FRAME, render3D );
        }

        /**
         * @brief   バウンドパラメータ設定
         */
        private function clickBound3D( ev:MouseEvent ):void
        {
            for( var i:int = 0; i < CUBE_XNUM; ++i )
            {
                for( var j:int = 0; j < CUBE_ZNUM; ++j )
                {
                    var cube:DisplayObject3D = this.scene.getChildByName( 'cube_' + i.toString() + '_' + j.toString() );
                    if( cube.extra.isjump == false )
                    {
                        cube.extra.v = Math.random() * JUMP_V;
                        cube.extra.rot = Math.random() * JUMP_ROT;
                        cube.extra.t = 1.0;
                        cube.extra.isjump = true;
                    }
                }
            }
        }

        /**
         * @brief   更新と描画
         */
        private function render3D( ev:Event ):void
        {
            for( var i:int = 0; i < CUBE_XNUM; ++i )
            {
                for( var j:int = 0; j < CUBE_ZNUM; ++j )
                {
                    var cube:DisplayObject3D = this.scene.getChildByName( 'cube_' + i.toString() + '_' + j.toString() );
                    if( cube.extra.isjump == true )
                    {
                        // 回転
                        cube.rotationY += cube.extra.rot;
                        if( cube.rotationY >= 360 ) cube.rotationY = 0;

                        // 投げ上げ
                        cube.y = cube.extra.v * cube.extra.t - 0.5 * JUMP_G * Math.pow( cube.extra.t, cube.extra.t );
                        cube.extra.t += JUMP_T;
                        if( cube.y <= 0 )
                        {
                            cube.y = 0;
                            cube.extra.v *= JUMP_E;
                            cube.extra.t = 1.0;

                            if( cube.extra.v <= 1 )
                            {
                                cube.extra.isjump = false;
                            }
                        }
                    }
                }
            }
            this.render.renderScene( this.scene, this.camera, this.viewport );
        }
    }
}