public static void InvokeTest(Action<string> WriteLine)
        {
            var Enumerable_Range_1_2 = new[] { 1, 2 }.AsEnumerable();
            var Enumerable_Range_3_2 = new[] { 3, 4 }.AsEnumerable();


            foreach (var item in

                Enumerable_Range_1_2

                .SelectMany(
                    x =>
                    {
                        WriteLine("SelectMany " + new { x });

                        return Enumerable_Range_3_2.Select(
                            y =>
                            {
                                WriteLine("Select " + new { x, y });

                                return new { x, y };
                            }
                        );
                    }
                )

                // jsc should call it automatically to allow arrays
                .AsEnumerable()

                )
            {
                WriteLine(item.ToString());
            }
        }
        /// <summary>
        /// This is a javascript application.
        /// </summary>
        /// <param name="page">HTML document rendered by the web server which can now be enhanced.</param>
        public Application(IApp page)
        {
            // https://sites.google.com/a/jsc-solutions.net/work/knowledge-base/15-dualvr/20150824

            // http://stackoverflow.com/questions/14103986/canvas-and-spritematerial

            // X:\jsc.svn\examples\javascript\WebGL\WebGLSVGAnonymous\WebGLSVGAnonymous\Application.cs
            // X:\jsc.svn\examples\javascript\WebGL\WebGLVRCreativeLeadership\WebGLVRCreativeLeadership\Application.cs
            // X:\jsc.svn\examples\javascript\WebGL\WebGLSVGSprite\WebGLSVGSprite\Application.cs

            //var l = new NotificationLayout().layout;

            //l.AttachToDocument();

            // : INodeConvertible<IHTMLElement>
            //var c = (IHTMLCanvas)l.layout;
            //var c = (IHTMLCanvas)l;

            // look we have a databound 2D image!

            // make it implicit operator for assetslibrary?

            //l.style



            //c.AttachToDocument();



            // https://play.google.com/store/apps/details?id=com.abstractatech.vr
            // could we display LAN UDP notifications too. like
            // which youtube video is playing?

            Native.body.Clear();
            Native.body.style.margin = "0px";
            Native.body.style.backgroundColor = "black";

            // https://vronecontest.zeiss.com/index.php?controller=ideas&view=show&id=652

            //          hResolution: 1920,
            //vResolution: 1080,

            // "X:\jsc.svn\examples\javascript\synergy\webgl\WebGLEquirectangularPanorama\WebGLEquirectangularPanorama.sln"

            // http://oculusstreetview.eu.pn/?lat=44.301987&lng=9.211561999999958&q=3&s=false&heading=0
            // https://github.com/troffmo5/OculusStreetView

            // http://stackoverflow.com/questions/23817633/threejs-using-a-sprite-with-the-oculusrifteffect
            // http://laht.info/dk2-parameters-for-three-oculusrifteffect-js/

            // http://stemkoski.github.io/Three.js/Sprites.html
            // http://stemkoski.github.io/Three.js/Texture-Animation.html
            // http://blog.thematicmapping.org/2013/10/terrain-visualization-with-threejs-and.html

            // http://mrdoob.github.io/three.js/examples/webgl_panorama_equirectangular.html

            var window = Native.window;

            var fov = 70.0;

            var camera = new THREE.PerspectiveCamera(fov,
                1920.0 / 1080.0,
                //window.aspect,
                1, 1100);
            var target = new THREE.Vector3(0, 0, 0);

            //(camera as dynamic).target = target;

            var scene = new THREE.Scene();
            //scene.add(new THREE.AmbientLight(0xffffff));
            //scene.add(new THREE.AmbientLight(0xafafaf));

            // http://www.html5canvastutorials.com/three/html5-canvas-webgl-ambient-lighting-with-three-js/

            // http://stackoverflow.com/questions/14717135/three-js-ambient-light-unexpected-effect

            scene.add(new THREE.AmbientLight(0x2f2f2f));


            //var light = new THREE.DirectionalLight(0xffffff, 1.0);
            #region light
            var light = new THREE.DirectionalLight(0xffffff, 2.5);
            //var light = new THREE.DirectionalLight(0xffffff, 2.5);
            //var light = new THREE.DirectionalLight(0xffffff, 1.5);
            //var lightOffset = new THREE.Vector3(0, 1000, 2500.0);
            var lightOffset = new THREE.Vector3(
                2000,
                700,

                // lower makes longer shadows 
                700.0
                );
            light.position.copy(lightOffset);
            light.castShadow = true;

            var xlight = light as dynamic;
            xlight.shadowMapWidth = 4096;
            xlight.shadowMapHeight = 2048;

            xlight.shadowDarkness = 0.3;
            //xlight.shadowDarkness = 0.5;

            xlight.shadowCameraNear = 10;
            xlight.shadowCameraFar = 10000;
            xlight.shadowBias = 0.00001;
            xlight.shadowCameraRight = 4000;
            xlight.shadowCameraLeft = -4000;
            xlight.shadowCameraTop = 4000;
            xlight.shadowCameraBottom = -4000;

            xlight.shadowCameraVisible = true;

            scene.add(light);
            #endregion

            var mesh = new THREE.Mesh(new THREE.SphereGeometry(500, 60, 40),
                new THREE.MeshBasicMaterial(new
                {
                    map = THREE.ImageUtils.loadTexture(
                        new _2294472375_24a3b8ef46_o().src
                        //new WebGLEquirectangularPanorama.HTML.Images.FromAssets.PANO_20130616_222058().src
                        //new WebGLEquirectangularPanorama.HTML.Images.FromAssets.PANO_20121225_210448().src

                        )
                }));
            mesh.scale.x = -1;
            scene.add(mesh);

            var labove = new NotificationLayout();

            #region sprite2 above
            {
                labove.Message.innerText = "VR CREATIVE LEADERSHIP";
                labove.layout.style.background = "";

                new { }.With(
                    async delegate
                    {
                        retry:

                        // make it blink. gaze cursor is on it?


                        labove.box.style.background = "rgba(255,255,0,0.7)";
                        labove.box.setAttribute("invoke", "onmutation1");
                        await Task.Delay(1500);

                        // is mutation observer noticing it?
                        labove.box.style.background = "rgba(255,255,255,0.7)";
                        labove.box.setAttribute("invoke", "onmutation2");
                        await Task.Delay(1500);


                        goto retry;
                    }
                );



                var c = labove.AsCanvas();

                var xcrateTexture = new THREE.Texture(c);

                // like video texture
                Native.window.onframe += delegate { xcrateTexture.needsUpdate = true; };

                var xcrateMaterial = new THREE.SpriteMaterial(
                    new
                    {
                        map = xcrateTexture,
                        useScreenCoordinates = false,
                        //color = 0xff0000
                        color = 0xffffff
                    }
            );



                var xsprite2 = new THREE.Sprite(xcrateMaterial);

                //floor
                //sprite2.position.set(0, -200, 0);

                // left
                xsprite2.position.set(200, 0, 0);

                //sprite2.position.set(0, 0, 200);

                //sprite2.position.set(-100, 0, 0);
                xsprite2.scale.set(
                   c.width * 0.5,
                   c.height * 0.5,
                    //64, 64,
                    1.0); // imageWidth, imageHeight
                scene.add(xsprite2);
            }
            #endregion




            //var lineTo = new List<THREE.Vector3>();
            var others = new
            {
                ui = default(NotificationLayout),
                canvas = default(IHTMLCanvas),

                map = default(THREE.Texture),

                sprite = default(THREE.Sprite)

            }.ToEmptyList();

            #region add
            Action<NotificationLayout> add = ui =>
            {
                ui.layout.style.background = "";

                var canvas = ui.AsCanvas();
                var index = others.Count;

                //ui.Message  += new { index };
                //ui.Message.innerText += new { index };

                //lbelow0.Message = new { THREE.REVISION }.ToString();

                // THREE.WebGLRenderer: Texture is not power of two. Texture.minFilter is set to THREE.LinearFilter or THREE.NearestFilter. ( undefined )

                var map = new THREE.Texture(canvas);

                map.minFilter = THREE.LinearFilter;

                // like video texture

                var xcrateMaterial = new THREE.SpriteMaterial(
                    new
                    {
                        map,
                        useScreenCoordinates = false,
                        //color = 0xff0000
                        color = 0xffffff
                    }
                );



                var sprite = new THREE.Sprite(xcrateMaterial);

                //floor
                //sprite2.position.set(0, -200, 0);

                // left middle
                //sprite2.position.set(200, 0, 0);
                //sprite.position.set(250, -50, 50);
                //lineTo.Add(sprite.position);




                //sprite2.position.set(0, 0, 200);

                //sprite2.position.set(-100, 0, 0);
                sprite.scale.set(
                   canvas.width * 0.5,
                   canvas.height * 0.5,
                    //64, 64,
                    1.0); // imageWidth, imageHeight
                scene.add(sprite);

                others.Add(
                    new
                    {
                        ui,
                        canvas,
                        map,
                        sprite
                    }
                );

                var sw = Stopwatch.StartNew();

                Native.window.onframe += delegate
                {
                    // can we get some of the crazy c++ template bitmapbuffer code from the past?
                    map.needsUpdate = true;

                    var offset = Math.PI * 2 * ((double)(index + 1) / others.Count);

                    sprite.position.x = 300;

                    sprite.position.z = Math.Sin(sw.ElapsedMilliseconds * 0.00001 + offset) * 120;
                    sprite.position.y = Math.Cos(sw.ElapsedMilliseconds * 0.00001 + offset) * 120;
                };

            };
            #endregion


            add(
                new NotificationLayout
                {
                    // keep attributes around?
                    // like we do with XElement sync?
                    Icon = new HTML.Images.FromAssets._0_10122014_ECAF4B1F638429B44F4B142C2A4F38EE().With(
                        x =>
                        {
                            x.style.width = "96px";
                            x.style.height = "96px";
                        }
                    ),
                    Message = "Advanced Mechanics by Portugal Design Lab"
                }
              );

            add(
                   new NotificationLayout
                   {
                       // keep attributes around?
                       // like we do with XElement sync?
                       Icon = new HTML.Images.FromAssets._42_08122014_D2639E0AAA3E54E5F4568760AEE605EE().With(
                           x =>
                           {
                               x.style.width = "96px";
                               x.style.height = "96px";
                           }
                       ),
                       Message = "Face Value by mshariful"
                   }
                 );



            add(
                   new NotificationLayout
                   {
                       // keep attributes around?
                       // like we do with XElement sync?
                       Icon = new HTML.Images.FromAssets._371_28122014_2045510F2F7974319A9F7E9F4B39DF07().With(
                           x =>
                           {
                               x.style.width = "96px";
                               x.style.height = "96px";
                           }
                       ),
                       Message = "Mind Wall. by Sumit Goski"
                   }
                 );


            #region lines
            {
                var geometry = new THREE.Geometry
                {
                    // how can we keep streaming verticies data points to gpu?
                    vertices =

                        others.SelectMany(
                            lineTo =>
                                new[]
                                {
                                    new THREE.Vector3(200, 0, 0),
                                    lineTo.sprite.position
                                }
                        ).ToArray()

                    // https://github.com/mrdoob/three.js/wiki/Updates
                    // http://stackoverflow.com/questions/17842521/adding-geometry-to-a-three-js-mesh-after-render
                    //verticesNeedUpdate = true
                };

                Native.window.onframe += delegate { geometry.verticesNeedUpdate = true; };

                var o = new THREE.Line(geometry, new THREE.LineDashedMaterial(
                    new { color = 0x00aaff, dashSize = 1, gapSize = 0.5, linewidth = 1 }
                    ), THREE.LineStrip);

                //objects.Add(o);
                scene.add(o);
            }
            #endregion


            //          // DK2
            //          hResolution: 1920,
            //vResolution: 1080,

            var renderer = new THREE.WebGLRenderer();
            renderer.setSize(1920, 1080);

            #region HMD
            // broken?
            var distortionK = new double[] { 1.0, 0.22, 0.24, 0.0 };
            var chromaAbParameter = new double[] { 0.996, -0.004, 1.014, 0.0 };

            var HMD = new OculusRiftEffectOptions
            {
                hResolution = window.Width,
                vResolution = window.Height,

                hScreenSize = 0.12576,
                vScreenSize = 0.07074,
                interpupillaryDistance = 0.0635,
                lensSeparationDistance = 0.0635,
                eyeToScreenDistance = 0.041,

                //  j.distortionK = [0, 1.875, -71.68, 1.595, -3.218644E+26, 1.615, 0, 0];
                //distortionK = new double[] { 1.0, 0.22, 0.24, 0.0 },
                distortionK = distortionK,

                // j.chromaAbParameter = [1.609382E+22, 1.874, -5.189695E+11, -0.939, 4.463059E-29, 1.87675, 0, 0];
                //chromaAbParameter = new double[] { 0.996, -0.004, 1.014, 0.0 }
                chromaAbParameter = chromaAbParameter

            };
            #endregion


            //var effect = new THREE.OculusRiftEffect(
            //    renderer, new
            //    {
            //        worldScale = 100,

            //        //HMD
            //    }
            //    );

            //effect.setSize(1920, 1080);


            renderer.domElement.AttachToDocument();

            //renderer.domElement.style.position = IStyle.PositionEnum.absolute;
            renderer.domElement.style.SetLocation(0, 0);

            Native.document.body.style.overflow = IStyle.OverflowEnum.hidden;

            // x:\jsc.svn\examples\javascript\synergy\comanchebysiorki\comanchebysiorki\application.cs

            new { }.With(
                     async delegate
                     {
                         retry:

                         var s = (double)Native.window.Width / 1920.0;


                         Native.document.body.style.transform = "scale(" + s + ")";
                         Native.document.body.style.transformOrigin = "0% 0%";

                         await Native.window.async.onresize;
                         goto retry;
                     }
                   );

            //#region onresize
            //Native.window.onresize +=
            //    delegate
            //    {
            //        camera.aspect = Native.window.aspect;
            //        camera.updateProjectionMatrix();

            //        renderer.setSize(window.Width, window.Height);
            //        effect.setSize(window.Width, window.Height);
            //    };
            //#endregion


            //Native.document.body.onmousewheel +=
            //    e =>
            //    {
            //        fov -= e.WheelDirection * 5.0;
            //        camera.projectionMatrix.makePerspective(fov,
            //            (double)window.Width / window.Height, 1, 1100);
            //    };

            var lon0 = -45.0;
            var lon1 = 0.0;

            var lon = new sum(
                 () => lon0,
                 () => lon1
             );

            var lat0 = 0.0;
            var lat1 = 0.0;

            // or could we do it with byref or pointers?
            var lat = new sum(
                () => lat0,
                () => lat1
            );

            var phi = 0.0;
            var theta = 0.0;

            //var controls = new THREE.OrbitControls(camera);

            var camera_rotation_z = 0.0;

            //Native.document.onmousemove +=
            //  e =>
            //  {

            //      l.Message = new { e.CursorX, e.CursorY }.ToString();
            //  };


            Native.window.onframe +=
                        ee =>
                        {
                            //labove.Message = new
                            //{
                            //    lon,
                            //    lat,
                            //    ee.counter
                            //}.ToString();

                            //if (Native.document.pointerLockElement == Native.document.body)
                            //    lon += 0.00;
                            //else
                            //    lon += 0.01;

                            //lat = Math.Max(-85, Math.Min(85, lat));

                            //Native.document.title = new { lon, lat }.ToString();


                            phi = THREE.Math.degToRad(90 - lat);
                            theta = THREE.Math.degToRad(lon);

                            target.x = 500 * Math.Sin(phi) * Math.Cos(theta);
                            target.y = 500 * Math.Cos(phi);
                            target.z = 500 * Math.Sin(phi) * Math.Sin(theta);


                            //controls.update();
                            //camera.position = controls.center.clone();


                            camera.lookAt(target);
                            camera.rotation.z += camera_rotation_z;

                            renderer.render(scene, camera);

                            //effect.render(scene, camera);
                        };



            // http://blog.thematicmapping.org/2013/10/terrain-visualization-with-threejs-and.html

            // http://stackoverflow.com/questions/13278087/determine-vertical-direction-of-a-touchmove



            var compassHeadingOffset = 0.0;
            var compassHeadingInitialized = 0;

            #region compassHeading
            // X:\jsc.svn\examples\javascript\android\Test\TestCompassHeading\TestCompassHeading\Application.cs
            Native.window.ondeviceorientation +=
                          dataValues =>
                          {
                              // Convert degrees to radians
                              var alphaRad = dataValues.alpha * (Math.PI / 180);
                              var betaRad = dataValues.beta * (Math.PI / 180);
                              var gammaRad = dataValues.gamma * (Math.PI / 180);

                              // Calculate equation components
                              var cA = Math.Cos(alphaRad);
                              var sA = Math.Sin(alphaRad);
                              var cB = Math.Cos(betaRad);
                              var sB = Math.Sin(betaRad);
                              var cG = Math.Cos(gammaRad);
                              var sG = Math.Sin(gammaRad);

                              // Calculate A, B, C rotation components
                              var rA = -cA * sG - sA * sB * cG;
                              var rB = -sA * sG + cA * sB * cG;
                              var rC = -cB * cG;

                              // Calculate compass heading
                              var compassHeading = Math.Atan(rA / rB);

                              // Convert from half unit circle to whole unit circle
                              if (rB < 0)
                              {
                                  compassHeading += Math.PI;
                              }
                              else if (rA < 0)
                              {
                                  compassHeading += 2 * Math.PI;
                              }

                              /*
                              Alternative calculation (replacing lines 99-107 above):

                                var compassHeading = Math.atan2(rA, rB);

                                if(rA < 0) {
                                  compassHeading += 2 * Math.PI;
                                }
                              */

                              // Convert radians to degrees
                              compassHeading *= 180 / Math.PI;

                              // Compass heading can only be derived if returned values are 'absolute'

                              // X:\jsc.svn\examples\javascript\android\Test\TestCompassHeadingWithReset\TestCompassHeadingWithReset\Application.cs

                              //Native.document.body.innerText = new { compassHeading }.ToString();
                              if (compassHeadingInitialized > 0)
                              {
                                  lon1 = compassHeading - compassHeadingOffset;
                              }
                              else
                              {
                                  compassHeadingOffset = compassHeading;
                                  compassHeadingInitialized++;
                              }

                          };
            #endregion

            #region gamma
            Native.window.ondeviceorientation +=
                //e => Native.body.innerText = new { e.alpha, e.beta, e.gamma }.ToString();
                //e => lon = e.gamma;
                e =>
                {
                    lat1 = e.gamma;

                    // after servicing a running instance would be nice
                    // either by patching or just re running the whole iteration in the backgrou
                    camera_rotation_z = e.beta * 0.02;
                };
            #endregion



            #region camera rotation
            var old = new { clientX = 0, clientY = 0 };

            Native.document.body.ontouchstart +=
                            e =>
                            {
                                var n = new { e.touches[0].clientX, e.touches[0].clientY };
                                old = n;
                            };

            Native.document.body.ontouchmove +=
                    e =>
                    {
                        var n = new { e.touches[0].clientX, e.touches[0].clientY };

                        e.preventDefault();

                        lon0 += (n.clientX - old.clientX) * 0.2;
                        lat0 -= (n.clientY - old.clientY) * 0.2;

                        old = n;
                    };


            Native.document.body.onmousemove +=
                e =>
                {
                    e.preventDefault();

                    if (Native.document.pointerLockElement == Native.document.body)
                    {
                        lon0 += e.movementX * 0.1;
                        lat0 -= e.movementY * 0.1;

                        //Console.WriteLine(new { lon, lat, e.movementX, e.movementY });
                    }
                };


            Native.document.body.onmouseup +=
              e =>
              {
                  //drag = false;
                  e.preventDefault();
              };

            Native.document.body.onmousedown +=
                e =>
                {
                    //e.CaptureMouse();

                    //drag = true;
                    e.preventDefault();
                    Native.document.body.requestPointerLock();

                };


            Native.document.body.ondblclick +=
                e =>
                {
                    e.preventDefault();

                    Console.WriteLine("requestPointerLock");
                };

            #endregion

            Native.body.onmousewheel +=
                e =>
                {

                    camera_rotation_z += 0.1 * e.WheelDirection; ;

                };
            // https://developer.android.com/training/system-ui/immersive.html




            //var ze = new ZeProperties();

            //ze.Show();
            //ze.treeView1.Nodes.Clear();

            //ze.Add(() => renderer);
            ////ze.Add(() => controls);
            //ze.Add(() => scene);
            //ze.Left = 0;
        }
        // var points = hilbert3D( new THREE.Vector3( 0,0,0 ), 25.0, recursion, 0, 1, 2, 3, 4, 5, 6, 7 );
        static THREE.Vector3[] hilbert3D(
            THREE.Vector3 center,
            double size = 25.0,
            int iterations = 1,
            int v0 = 0,
            int v1 = 1,
            int v2 = 2,
            int v3 = 3,
            int v4 = 4,
            int v5 = 5,
            int v6 = 6,
            int v7 = 7)
        {
            // 0:71ms {{ i = 0, x = -18.75, y = 18.75, z = -18.75 }}

            // Default Vars
            var half = size / 2;


            var vec_s = new[] {
                new THREE.Vector3(center.x - half, center.y + half, center.z - half),
                new THREE.Vector3(center.x - half, center.y + half, center.z + half),
                new THREE.Vector3(center.x - half, center.y - half, center.z + half),
                new THREE.Vector3(center.x - half, center.y - half, center.z - half),
                new THREE.Vector3(center.x + half, center.y - half, center.z - half),
                new THREE.Vector3(center.x + half, center.y - half, center.z + half),
                new THREE.Vector3(center.x + half, center.y + half, center.z + half),
                new THREE.Vector3(center.x + half, center.y + half, center.z - half)
            };

            var vec = new[] {
                vec_s[v0],
                vec_s[v1],
                vec_s[v2],
                vec_s[v3],
                vec_s[v4],
                vec_s[v5],
                vec_s[v6],
                vec_s[v7]
            };

            // Recurse iterations

            //if (--iterations >= 0)
            if (iterations > 0)
            {
                iterations--;

                var tmp = new[] {
                    hilbert3D(vec[0], half, iterations, v0, v3, v4, v7, v6, v5, v2, v1),
                    hilbert3D(vec[1], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3),
                    hilbert3D(vec[2], half, iterations, v0, v7, v6, v1, v2, v5, v4, v3),
                    hilbert3D(vec[3], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5),
                    hilbert3D(vec[4], half, iterations, v2, v3, v0, v1, v6, v7, v4, v5),
                    hilbert3D(vec[5], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7),
                    hilbert3D(vec[6], half, iterations, v4, v3, v2, v5, v6, v1, v0, v7),
                    hilbert3D(vec[7], half, iterations, v6, v5, v2, v1, v0, v3, v4, v7)
                };

                // Return recursive call
                return tmp.SelectMany(x => x).ToArray();
            }

            // Return complete Hilbert Curve.
            return vec;
        }