// http://blog.allanbishop.com/box2d-2-1a-tutorial-part-1/

        public ApplicationSprite()
        {

            _world = new b2World(new b2Vec2(0, 10), true);

            var groundBodyDef = new b2BodyDef();
            groundBodyDef.position.Set(SWF_HALF_WIDTH / PIXELS_TO_METRE,
                          SWF_HEIGHT / PIXELS_TO_METRE - 20 / PIXELS_TO_METRE);

            var groundBody = _world.CreateBody(groundBodyDef);

            var groundBox = new b2PolygonShape();
            groundBox.SetAsBox(SWF_HALF_WIDTH / PIXELS_TO_METRE,
                           20 / PIXELS_TO_METRE);

            var groundFixtureDef = new b2FixtureDef();
            groundFixtureDef.shape = groundBox;
            groundFixtureDef.density = 1;
            groundFixtureDef.friction = 1;
            groundBody.CreateFixture(groundFixtureDef);

            var bodyDef = new b2BodyDef();
            bodyDef.type = b2Body.b2_dynamicBody;
            bodyDef.position.Set(SWF_HALF_WIDTH / PIXELS_TO_METRE, 4);
            var body = _world.CreateBody(bodyDef);

            var dynamicBox = new b2PolygonShape();
            dynamicBox.SetAsBox(1, 1);

            var fixtureDef = new b2FixtureDef();
            fixtureDef.shape = dynamicBox;
            fixtureDef.density = 1;
            fixtureDef.friction = 0.3;

            body.CreateFixture(fixtureDef);

            var debugSprite = new Sprite();
            addChild(debugSprite);
            var debugDraw = new b2DebugDraw();
            debugDraw.SetSprite(debugSprite);
            debugDraw.SetDrawScale(PIXELS_TO_METRE);
            debugDraw.SetLineThickness(1.0);
            debugDraw.SetAlpha(1);
            debugDraw.SetFillAlpha(0.4);
            debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
            _world.SetDebugDraw(debugDraw);


            // Add event for main loop

            this.stage.enterFrame +=
                delegate
                {
                    var timeStep = 1 / 30.0;
                    var velocityIterations = 6;
                    var positionIterations = 2;

                    _world.Step(timeStep, velocityIterations, positionIterations);
                    _world.ClearForces();
                    _world.DrawDebugData();

                };


        }
        public ApplicationSprite()
        {

            #region AtInitializeConsoleFormWriter

            var w = new __OutWriter();
            var o = Console.Out;
            var __reentry = false;

            var __buffer = new StringBuilder();

            w.AtWrite =
                x =>
                {
                    __buffer.Append(x);
                };

            w.AtWriteLine =
                x =>
                {
                    __buffer.AppendLine(x);
                };

            Console.SetOut(w);

            this.AtInitializeConsoleFormWriter = (
                Action<string> Console_Write,
                Action<string> Console_WriteLine
            ) =>
            {

                try
                {


                    w.AtWrite =
                        x =>
                        {
                            o.Write(x);

                            if (!__reentry)
                            {
                                __reentry = true;
                                Console_Write(x);
                                __reentry = false;
                            }
                        };

                    w.AtWriteLine =
                        x =>
                        {
                            o.WriteLine(x);

                            if (!__reentry)
                            {
                                __reentry = true;
                                Console_WriteLine(x);
                                __reentry = false;
                            }
                        };

                    Console.WriteLine("flash Console.WriteLine should now appear in JavaScript form!");
                    Console.WriteLine(__buffer.ToString());
                }
                catch
                {

                }
            };
            #endregion




            var SCALE = 15;      //how many pixels in a meter
            var WIDTH_M = DefaultWidth / SCALE; //world width in meters. for this example, world is as large as the screen
            var HEIGHT_M = DefaultHeight / SCALE; //world height in meters

            //initialize font to draw text with
            //var font=new gamejs.font.Font('16px Sans-serif');

            //key bindings
            //var BINDINGS={accelerate:gamejs.event.K_UP, 
            //              brake:gamejs.event.K_DOWN,      
            //              steer_left:gamejs.event.K_LEFT, 
            //               steer_right:gamejs.event.K_RIGHT}; 









            //initialize display
            //var display = gamejs.display.setMode([WIDTH_PX, HEIGHT_PX]);

            //SET UP B2WORLD
            var b2world = new b2World(new b2Vec2(0, 0), false);

            //set up box2d debug draw to draw the bodies for us.
            //in a real game, car will propably be drawn as a sprite rotated by the car's angle
            var debugDraw = new b2DebugDraw();
            debugDraw.SetSprite(this);
            debugDraw.SetDrawScale(SCALE);
            debugDraw.SetFillAlpha(0.5);
            debugDraw.SetLineThickness(1.0);
            debugDraw.SetFlags(b2DebugDraw.e_shapeBit);
            b2world.SetDebugDraw(debugDraw);

            var wheels = new[] { 
                //top left
                new Wheel(b2world: b2world, x :-1,  y :-1.2,  width :0.4,  length :0.8,  revolving :true,  powered :true),

                //top right
                new Wheel(b2world: b2world, x :1,  y :-1.2,  width :0.4,  length :0.8,  revolving :true,  powered :true),

                //back left
                new Wheel(b2world: b2world, x :-1,  y :1.2,  width :0.4,  length :0.8,  revolving :false,  powered :false),

                //back right
                new Wheel(b2world: b2world, x :1,  y :1.2,  width :0.4,  length :0.8,  revolving :false,  powered :false),
            };
            Func<double, double, double[]> ff = (a, b) => { return new double[] { a, b }; };

            ////initialize car
            var car = new Car(
                b2world: b2world,
                width: 2,
                length: 4,
                position: ff(10, 10),
                angle: 180,
                power: 60,
                max_steer_angle: 20,
                max_speed: 60,
                wheels: wheels
            );



            //initialize some props to bounce against
            var props = new List<BoxProp>();


            //outer walls
            props.Add(new BoxProp(b2world, size: ff(WIDTH_M, 1), position: ff(WIDTH_M / 2, 0.5)));
            props.Add(new BoxProp(b2world, size: ff(1, HEIGHT_M - 2), position: ff(0.5, HEIGHT_M / 2)));
            props.Add(new BoxProp(b2world, size: ff(WIDTH_M, 1), position: ff(WIDTH_M / 2, HEIGHT_M - 0.5)));
            props.Add(new BoxProp(b2world, size: ff(1, HEIGHT_M - 2), position: ff(WIDTH_M - 0.5, HEIGHT_M / 2)));

            //pen in the center
            var center = new double[] { WIDTH_M / 2, HEIGHT_M / 2 };
            props.Add(new BoxProp(b2world, size: ff(1, 6), position: ff(center[0] - 3, center[1])));
            props.Add(new BoxProp(b2world, size: ff(1, 6), position: ff(center[0] + 3, center[1])));
            props.Add(new BoxProp(b2world, size: ff(5, 1), position: ff(center[0], center[1] + 2.5)));

            var frameid = 0;

            var KEYS_DOWN = new Dictionary<Keys, bool> { 
            
                { Keys.Left, false },
                { Keys.Up, false },
                { Keys.Right, false },
                { Keys.Down, false },
            }; //keep track of what keys are held down by the player


            this.stage.keyDown +=
                e =>
                {
                    Console.WriteLine("keyDown " + new { e.keyCode });
                    KEYS_DOWN[(Keys)e.keyCode] = true;
                };

            this.stage.keyUp +=
               e =>
               {
                   Console.WriteLine("keyUp " + new { e.keyCode });
                   KEYS_DOWN[(Keys)e.keyCode] = false;
               };


            #region tick
            Action<double> tick = (msDuration) =>
            {
                frameid++;

                //if (frameid > 1)
                //    return;

                //Console.WriteLine(new { frameid });
                //GAME LOOP

                //handle events. Key status (depressed or no) is tracked in via KEYS_DOWN associative array
                //gamejs.event.get().forEach(function(event){
                //    //key press
                //    if (event.type === gamejs.event.KEY_DOWN) KEYS_DOWN[event.key] = true;
                //    //key release
                //    else if (event.type === gamejs.event.KEY_UP) KEYS_DOWN[event.key] = false;           
                //});

                //set car controls according to player input
                if (KEYS_DOWN[Keys.Up])
                {
                    car.accelerate = ACC_ACCELERATE;
                }
                else if (KEYS_DOWN[Keys.Down])
                {
                    car.accelerate = ACC_BRAKE;
                }
                else
                {
                    car.accelerate = ACC_NONE;
                }

                if (KEYS_DOWN[Keys.Right])
                {
                    car.steer = STEER_RIGHT;
                }
                else if (KEYS_DOWN[Keys.Left])
                {
                    car.steer = STEER_LEFT;
                }
                else
                {
                    car.steer = STEER_NONE;
                }

                ////update car
                car.update(msDuration);

                //update physics world
                b2world.Step(msDuration / 1000.0, 10, 8);

                //clear applied forces, so they don't stack from each update
                b2world.ClearForces();

                //fill background
                //gamejs.draw.rect(display, '#FFFFFF', new gamejs.Rect([0, 0], [WIDTH_PX, HEIGHT_PX]),0)

                //let box2d draw it's bodies
                b2world.DrawDebugData();

                //fps and car speed display
                //display.blit(font.render('FPS: '+parseInt((1000)/msDuration)), [25, 25]);
                //display.blit(font.render('SPEED: '+parseInt(Math.ceil(car.getSpeedKMH()))+' km/h'), [25, 55]);
                //Console.WriteLine(new { frameid } + " done!");
            };

            ////gamejs.time.fpsCallback(tick, this, 60);
            var sw = new Stopwatch();
            sw.Start();

            this.enterFrame +=
                delegate
                {
                    tick(sw.ElapsedMilliseconds);
                    sw.Restart();
                };
            #endregion

        }
        public StarlingGameSpriteWithPhysics()
        {
            sessionid = random.Next();


            //b2Body ground_current = null;
            //b2Body air_current = null;




            #region ground_b2world
            // first frame  ... set up our physccs
            // zombies!!
            ground_b2world = new b2World(new b2Vec2(0, 0), false);
            ground_b2world.SetContactListener(
               new XContactListener()
            );

            var ground_b2debugDraw = new b2DebugDraw();

            ground_dd = new ScriptCoreLib.ActionScript.flash.display.Sprite();
            ground_dd.transform.colorTransform = new ColorTransform(0.0, 0, 1.0);





            ground_b2debugDraw.SetSprite(ground_dd);
            // textures are 512 pixels, while our svgs are 400px
            // so how big is a meter in our game world? :)
            ground_b2debugDraw.SetDrawScale(16);
            ground_b2debugDraw.SetFillAlpha(0.1);
            ground_b2debugDraw.SetLineThickness(1.0);
            ground_b2debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

            ground_b2world.SetDebugDraw(ground_b2debugDraw);





            #endregion


            #region groundkarma_b2world
            // first frame  ... set up our physccs
            // zombies!!
            groundkarma_b2world = new b2World(new b2Vec2(0, 0), false);

            var groundkarma_b2debugDraw = new b2DebugDraw();

            groundkarma_dd = new ScriptCoreLib.ActionScript.flash.display.Sprite();
            groundkarma_dd.transform.colorTransform = new ColorTransform(0.0, 1.0, 0.0);





            groundkarma_b2debugDraw.SetSprite(groundkarma_dd);
            // textures are 512 pixels, while our svgs are 400px
            // so how big is a meter in our game world? :)
            groundkarma_b2debugDraw.SetDrawScale(16);
            groundkarma_b2debugDraw.SetFillAlpha(0.1);
            groundkarma_b2debugDraw.SetLineThickness(1.0);
            groundkarma_b2debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

            groundkarma_b2world.SetDebugDraw(groundkarma_b2debugDraw);





            #endregion

            #region air_b2world
            // first frame  ... set up our physccs
            // zombies!!
            air_b2world = new b2World(new b2Vec2(0, 0), false);

            var air_b2debugDraw = new b2DebugDraw();

            air_dd = new ScriptCoreLib.ActionScript.flash.display.Sprite();

            // make it red!
            air_dd.transform.colorTransform = new ColorTransform(1.0, 0, 0);
            // make it slave
            air_dd.alpha = 0.3;





            air_b2debugDraw.SetSprite(air_dd);
            // textures are 512 pixels, while our svgs are 400px
            // so how big is a meter in our game world? :)
            air_b2debugDraw.SetDrawScale(16);
            air_b2debugDraw.SetFillAlpha(0.1);
            air_b2debugDraw.SetLineThickness(1.0);
            air_b2debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

            air_b2world.SetDebugDraw(air_b2debugDraw);








            #endregion

            #region damage_b2world
            // first frame  ... set up our physccs
            // zombies!!
            damage_b2world = new b2World(new b2Vec2(0, 0), false);
            damage_b2world.SetContactListener(
               new XContactListener { DiscardSmallImpulse = false }
            );

            var damage_b2debugDraw = new b2DebugDraw();

            damage_dd = new ScriptCoreLib.ActionScript.flash.display.Sprite();

            // make it red!
            //air_dd.transform.colorTransform = new ColorTransform(1.0, 0, 0);
            // make it slave
            damage_dd.alpha = 0.3;





            damage_b2debugDraw.SetSprite(air_dd);
            // textures are 512 pixels, while our svgs are 400px
            // so how big is a meter in our game world? :)
            damage_b2debugDraw.SetDrawScale(16);
            damage_b2debugDraw.SetFillAlpha(0.1);
            damage_b2debugDraw.SetLineThickness(1.0);
            damage_b2debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

            damage_b2world.SetDebugDraw(damage_b2debugDraw);








            #endregion

            #region smoke_b2world
            // first frame  ... set up our physccs
            // zombies!!
            smoke_b2world = new b2World(new b2Vec2(0, 0), false);

            var smoke_b2debugDraw = new b2DebugDraw();

            smoke_dd = new ScriptCoreLib.ActionScript.flash.display.Sprite();

            // make it red!
            //air_dd.transform.colorTransform = new ColorTransform(1.0, 0, 0);
            // make it slave
            smoke_dd.alpha = 0.3;





            smoke_b2debugDraw.SetSprite(air_dd);
            // textures are 512 pixels, while our svgs are 400px
            // so how big is a meter in our game world? :)
            smoke_b2debugDraw.SetDrawScale(16);
            smoke_b2debugDraw.SetFillAlpha(0.1);
            smoke_b2debugDraw.SetLineThickness(1.0);
            smoke_b2debugDraw.SetFlags(b2DebugDraw.e_shapeBit);

            smoke_b2world.SetDebugDraw(smoke_b2debugDraw);








            #endregion



            //#region obstacles
            //{
            //    var bodyDef = new b2BodyDef();

            //    bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;

            //    // stop moving if legs stop walking!
            //    bodyDef.linearDamping = 10.0;
            //    bodyDef.angularDamping = 0.3;
            //    //bodyDef.angle = 1.57079633;
            //    bodyDef.fixedRotation = true;

            //    var body = air_b2world.CreateBody(bodyDef);
            //    body.SetPosition(new b2Vec2(10, 10));

            //    var fixDef = new Box2D.Dynamics.b2FixtureDef();
            //    fixDef.density = 0.1;
            //    fixDef.friction = 0.01;
            //    fixDef.restitution = 0;


            //    fixDef.shape = new Box2D.Collision.Shapes.b2CircleShape(4);


            //    var fix = body.CreateFixture(fixDef);

            //    body.SetPosition(
            //        new b2Vec2(-8, 0)
            //    );
            //}

            //{
            //    var bodyDef = new b2BodyDef();

            //    bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;

            //    // stop moving if legs stop walking!
            //    bodyDef.linearDamping = 10.0;
            //    bodyDef.angularDamping = 0.3;
            //    //bodyDef.angle = 1.57079633;
            //    bodyDef.fixedRotation = true;

            //    var body = ground_b2world.CreateBody(bodyDef);
            //    body.SetPosition(new b2Vec2(10, 10));

            //    var fixDef = new Box2D.Dynamics.b2FixtureDef();
            //    fixDef.density = 0.1;
            //    fixDef.friction = 0.01;
            //    fixDef.restitution = 0;


            //    fixDef.shape = new Box2D.Collision.Shapes.b2CircleShape(4);


            //    var fix = body.CreateFixture(fixDef);


            //    body.SetPosition(
            //        new b2Vec2(8, -8)
            //    );
            //}

            //{
            //    var bodyDef = new b2BodyDef();

            //    bodyDef.type = Box2D.Dynamics.b2Body.b2_dynamicBody;

            //    // stop moving if legs stop walking!
            //    bodyDef.linearDamping = 10.0;
            //    bodyDef.angularDamping = 0.3;
            //    //bodyDef.angle = 1.57079633;
            //    bodyDef.fixedRotation = true;

            //    var body = groundkarma_b2world.CreateBody(bodyDef);
            //    body.SetPosition(new b2Vec2(10, 10));

            //    var fixDef = new Box2D.Dynamics.b2FixtureDef();
            //    fixDef.density = 0.1;
            //    fixDef.friction = 0.01;
            //    fixDef.restitution = 0;


            //    fixDef.shape = new Box2D.Collision.Shapes.b2CircleShape(4);


            //    var fix = body.CreateFixture(fixDef);


            //    body.SetPosition(
            //        new b2Vec2(8, 8)
            //    );
            //}
            //#endregion


            physicstime.Start();

            this.onbeforefirstframe += (stage, s) =>
            {
                if (!disablephysicsdiagnostics)
                {
                    s.nativeOverlay.addChild(ground_dd);
                    s.nativeOverlay.addChild(groundkarma_dd);
                    s.nativeOverlay.addChild(air_dd);
                    s.nativeOverlay.addChild(damage_dd);
                }
                // 1000 / 15
                var syncframeinterval = 1000 / 15;
                var syncframesince = 0L;
                var syncframeextra = 0L;


                // NaN ?
                syncframeid = 0;
                syncframetime = 0;


                var prevvelocity = 0.0;
                onframe +=
                    delegate
                    {
                        // drop frames
                        //if (frameid % 2 == 0)
                        //    return;

                        var physicstime_elapsed = physicstime.ElapsedMilliseconds + syncframeextra;
                        syncframeextra = 0;

                        // physicstime_elapsed needs to be split!

                        var physicstime_elapsed_PRE = physicstime_elapsed;




                        physicstime.Restart();

                        // add up the time we are spending
                        syncframesince += physicstime_elapsed;

                        #region raise_onsyncframe
                        var raise_onsyncframe = false;
                        // time for sync frame yet?
                        if (syncframesince >= syncframeinterval)
                        {
                            // time for syncframe!
                            raise_onsyncframe = true;

                            // does it actually help us? disabled for now
                            var dx = syncframesince - syncframeinterval;
                            syncframesince = 0;

                            // Error: raise_onsyncframe: { physicstime_elapsed_PRE = 275, dx = 75, physicstime_elapsed_POST = 0 }

                            physicstime_elapsed_PRE -= dx;
                            syncframeextra += dx;


                            // dropping frames?
                            //syncframeextra = Math.Min(syncframeextra, syncframeinterval);
                        }
                        #endregion


                        var iterations = 10;

                        syncframetime += physicstime_elapsed_PRE;

                        #region PRE Step

                        //update physics world
                        ground_b2world.Step(physicstime_elapsed_PRE / 1000.0, iterations, iterations);
                        groundkarma_b2world.Step(physicstime_elapsed_PRE / 1000.0, iterations, iterations);
                        air_b2world.Step(physicstime_elapsed_PRE / 1000.0, iterations, iterations);
                        damage_b2world.Step(physicstime_elapsed_PRE / 1000.0, iterations, iterations);
                        smoke_b2world.Step(physicstime_elapsed_PRE / 1000.0, iterations, iterations);
                        #endregion

                        ground_b2world.ClearForces();
                        groundkarma_b2world.ClearForces();
                        air_b2world.ClearForces();
                        damage_b2world.ClearForces();
                        smoke_b2world.ClearForces();


                        #region DrawDebugData ClearForces
                        if (!disablephysicsdiagnostics)
                        {

                            ground_b2world.DrawDebugData();
                            groundkarma_b2world.DrawDebugData();
                            air_b2world.DrawDebugData();
                            damage_b2world.DrawDebugData();
                            smoke_b2world.DrawDebugData();
                        }
                        #endregion

                        // syncframe
                        if (raise_onsyncframe)
                        {
                            syncframeid++;
                            this.onsyncframe(stage, s);

                            foreach (var item in units)
                            {
                                item.FeedKarma();
                            }
                        }


                        #region air_dd vs ground_dd
                        if (current != null)
                            if (current.body.GetWorld() == air_b2world)
                            {
                                air_dd.alpha = 0.6;
                                ground_dd.alpha = 0.1;
                            }
                            else
                            {
                                air_dd.alpha = 0.1;
                                ground_dd.alpha = 0.6;
                            }
                        #endregion

                        #region DisableDefaultContentDransformation
                        DisableDefaultContentDransformation = true;
                        {
                            var cm = new Matrix();

                            var any_movement = 0.0;

                            if (current != null)
                            {
                                cm.translate(
                                    -(current.body.GetPosition().x * 16),
                                    -(current.body.GetPosition().y * 16)
                                );



                                cm.rotate(-current.body.GetAngle() - Math.PI / 2 + current.CameraRotation);


                                if (current.body.GetType() == Box2D.Dynamics.b2Body.b2_dynamicBody)
                                {
                                    any_movement = (
                                        current.body.GetLinearVelocity().Length() +
                                        Math.Abs(current.body.GetAngularVelocity())
                                        )

                                    / 15.0;
                                }
                                else
                                {
                                    // force movement mode in a building
                                    any_movement = 1.0;
                                }

                            }


                            //cm.rotate(-current.GetAngle());








                            move_zoom +=
                               (any_movement - 0.5) *
                                 physicstime_elapsed * 0.004;
                            move_zoom = move_zoom.Max(0.0).Min(1.0);

                            var xinternalscale = internalscale;

                            if (disable_movezoom_and_altitude_for_scale)
                            {
                                // nop
                            }
                            else
                            {
                                xinternalscale +=
                                    +0.20 * (1.0 - current.Altitude)
                                    + ((1.0 - move_zoom) * 0.04);
                            }

                            var diagonal = new __vec2
                            {
                                x = stage.stageWidth,
                                y = stage.stageHeight
                            };

                            //stagescale = xinternalscale * (stage.stageWidth) / (800.0);
                            stagescale = xinternalscale * (diagonal.GetLength()) / (1000.0);


                            cm.scale(stagescale, stagescale);


                            cm.translate(
                                (stage.stageWidth * 0.5),
                                (stage.stageHeight * internal_center_y)
                            );


                            Content.transformationMatrix = cm;

                            ground_dd.transform.matrix = cm;
                            groundkarma_dd.transform.matrix = cm;
                            air_dd.transform.matrix = cm;
                            damage_dd.transform.matrix = cm;
                        }
                        #endregion



                        foreach (var item in units)
                        {
                            item.ShowPositionAndAngle();

                            #region driverseat
                            if (item.driverseat != null)
                                if (item.driverseat.driver != null)
                                {
                                    var driver = item.driverseat.driver;

                                    driver.SetPositionAndAngle(


                                            item.body.GetPosition().x + Math.Cos(item.body.GetAngle() - Math.PI * 0.5 - item.CameraRotation) * 2.2,
                                            item.body.GetPosition().y + Math.Sin(item.body.GetAngle() - Math.PI * 0.5 - item.CameraRotation) * 2.2
                                        ,
                                        item.body.GetAngle() - item.CameraRotation
                                    );

                                    driver.ShowPositionAndAngle();
                                }
                            #endregion

                            item.ApplyVelocity();
                        }





                    };
            };


        }