/// <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)
        {
            // can we inject ourselves into a chrome tab
            // before the page loads?

            // http://stackoverflow.com/questions/19191679/chrome-extension-inject-js-before-page-load
            // If you want to dynamically run a script as soon as possible, then call chrome.tabs.executeScript when the chrome.webNavigation.onCommitted event is triggered.

            // when does that happen?

            // if we are able to preload, would we be able to act as adblock?


            // as per X:\jsc.svn\examples\javascript\chrome\extensions\ChromeExtensionShadowExperiment\ChromeExtensionShadowExperiment\Application.cs



            // first lets get this test running in chrome


            dynamic self = Native.self;
            dynamic self_chrome = self.chrome;
            object self_chrome_tabs = self_chrome.tabs;

            if (self_chrome_tabs != null)
            {
                #region Installed

                chrome.runtime.Installed += delegate
                {
                    // our API does not have a Show
                    new chrome.Notification
                    {
                        Message = "ChromeExtensionPreShadow Installed!"
                    };
                };
                #endregion

                // what about
                // Error code: ERR_INTERNET_DISCONNECTED

                var once = new { tabId = default(TabIdInteger), url = default(string) }.ToEmptyList();


                new { }.With(
                    async delegate
                {
                    // X:\jsc.svn\examples\javascript\chrome\extensions\ChromeExtensionWithWorker\ChromeExtensionWithWorker\Application.cs

                    var code = await new WebClient().DownloadStringTaskAsync(
                       new Uri(Worker.ScriptApplicationSource, UriKind.Relative)
                  );

                    new chrome.Notification
                    {
                        Message = "code " + new { code.Length }
                    };

                    chrome.webNavigation.Committed +=
                          async z =>
                        {
                            // 0:5212ms at Delay {{ _title = , _message = webNavigation! Committed {{ url = http://example.com/, tabId = 99, transitionType = typed, transitionQualifiers = from_address_bar }} }} 

                            if (z.url.StartsWith("https://www.google.ee/_/chrome/newtab?"))
                            {
                                //Unchecked runtime.lastError while running tabs.executeScript: Cannot access a chrome:// URL
                                return;
                            }

                            //                            0:487298ms at Delay {
                            //                                {
                            //                                    _title = , _message = webNavigation!Committed {
                            //                                        {
                            //                                            url = chrome://chrome/extensions/, tabId = 158, transitionType = auto_bookmark, transitionQualifiers =  }} }} view-source:41478
                            //Unchecked runtime.lastError while running tabs.executeScript: Cannot access a chrome:// URL

                            if (z.transitionType == "auto_subframe")
                            {
                                // this seems to be an ad?
                                // https://developer.chrome.com/extensions/history

                                return;
                            }

                            if (z.url.StartsWith("chrome-devtools://"))
                                return;

                            if (z.url.StartsWith("chrome://"))
                                return;


                            // now would be nice to check if this tab was already injected.
                            once.Add(new { z.tabId, z.url });


                            var n = new chrome.Notification
                            {
                                Message = "webNavigation! Committed " + new { z.url, z.tabId, z.transitionType, z.transitionQualifiers }
                            };

                            //                        0:3388ms at Delay {
                            //                            {
                            //                                _title = , _message = webNavigation!Committed {
                            //                                    {
                            //                                        url = https://www.google.ee/_/chrome/newtab?espv=2&ie=UTF-8, tabId = 125, transitionType = typed, transitionQualifiers =  }} }} view-source:41478
                            //Unchecked runtime.lastError while running tabs.executeScript: Cannot access a chrome:// URL


                            // https://developer.chrome.com/extensions/tabs#method-executeScript
                            //z.tabId.executeScript(
                            //    new
                            //    {
                            //        code = "document.body.style.borderLeft='1em solid yellow';",
                            //        runAt = "document_start"
                            //    }
                            //);

                            // we should now be able to take command of that web page.
                            // yet. we might not want to change its DOM yet?
                            // maybe wait for context menu, keyboard or action icon click?


                            // Content scripts execute in a special environment called an isolated world. 
                            // They have access to the DOM of the page they are injected into, but not to any JavaScript variables or 
                            // functions created by the page. It looks to each content script as if there is no other JavaScript executing
                            // on the page it is running on. The same is true in reverse: JavaScript running on the page cannot call any 
                            // functions or access any variables defined by content scripts.

                            var result = await z.tabId.executeScript(
                            //new { file = url }
                            new
                            {
                                code,

                                runAt = "document_start"
                            }
                            );
                        };
                }
                );



                //chrome.tabs.Created +=
                //     (z) =>
                //    {
                //        var n = new Notification
                //        {
                //            Message = "Created! " + new { z.id }
                //        };
                //    };

                //chrome.tabs.Activated +=
                //     (z) =>
                //    {
                //        var n = new Notification
                //        {
                //            Message = "Activated! " + new { z }
                //        };

                //    };


                return;
            }


            // inside executeScript

            Native.document.documentElement.style.borderTop = "1em solid yellow";
            //Native.body.style.borderTop = "1em solid yellow";
            Console.WriteLine("injected!");

            // save view-source to B:
            // reload extension

            // 0:6311ms injected!
            // lets test agiainst file://

            // it works.
            // either do the workers now or lets test register element?

            // Uncaught NotSupportedError: Failed to execute 'registerElement' on 'Document': Registration failed for type 'x-foo'. Elements cannot be registered from extensions.
            //Native.document.registerElement("x-foo",
            //    (IHTMLElement e) =>
            //    {
            //        e.shadow.appendChild("x-foo element provided by ChromeExtensionPreShadow");
            //    }
            //);



            // !! Elements cannot be registered from extensions.
            // X:\jsc.svn\examples\javascript\chrome\extensions\ChromeExtensionPreShadow\ChromeExtensionPreShadow\Application.cs
            // https://code.google.com/p/chromium/issues/detail?id=390807

            Native.document.querySelectorAll("x-foo").WithEach(
                e =>
                {
                    // what about elements added later?

                    e.shadow.appendChild("x-foo element provided by ChromeExtensionPreShadow without registerElement");

                    //MutationCallback

                }
            );

            // ILMutationObserver
            new MutationObserver(
                new MutationCallback(
                    (MutationRecord[] mutations, MutationObserver observer) =>
                            {
                                // MutationCallback: {{ Length = 3 }}
                                // MutationCallback: {{ type = childList }}
                                // MutationCallback: {{ type = characterData }}

                                mutations.WithEach(
                                    m =>
                                    {
                                        // not a good idea. recursive
                                        //new IHTMLPre { "MutationCallback: " + new { m.type } }.AttachToDocument();

                                        if (m.type == "childList")
                                        {
                                            m.addedNodes.WithEach(
                                                addedNode =>
                                                {
                                                    //new IHTMLPre { "MutationCallback: " + new { addedNode } }.AttachToDocument();

                                                    //MutationCallback: { { addedNode = [object HTMLElement] } }
                                                    if (addedNode.nodeType == INode.NodeTypeEnum.ElementNode)
                                                    {
                                                        var addedElement = (IHTMLElement)addedNode;

                                                        //MutationCallback: addedElement { { localName = x - foo } }
                                                        //new IHTMLPre { "MutationCallback: addedElement " + new { addedElement.localName } }.AttachToDocument();

                                                        if (addedElement.localName == "x-foo")
                                                        {
                                                            addedElement.shadow.appendChild("x-foo element provided by ChromeExtensionPreShadow without registerElement");
                                                        }
                                                    }
                                                }
                                            );

                                        }
                                    }
                                );


                            }
                )
            ).observe(Native.document.documentElement,
                new
            {
                // Set to true if mutations to target's children are to be observed.
                childList = true,
                // Set to true if mutations to target's attributes are to be observed. Can be omitted if attributeOldValue and/or attributeFilter is specified.
                //attributes = true,
                // Set to true if mutations to target's data are to be observed. Can be omitted if characterDataOldValue is specified.
                //characterData = true,
                // Set to true if mutations to not just target, but also target's descendants are to be observed.
                subtree = true,
            }
            );



        }
        /// <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;
        }
        /// <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)
        {
            page.clear.WhenClicked(
                delegate
                {
                    //page.search.Clear();
                    page.search.value = "";
                }
            );

            new IHTMLButton { innerText = "Install" }.AttachToDocument().With(
                   btn =>
                   {
                       // http://help.adobe.com/en_US/air/build/WSfffb011ac560372f-5d0f4f25128cc9cd0cb-7ffd.html

                       btn.onclick +=
                           delegate
                           {
                               service.Install("assets/AndroidListApplications/foo.apk");
                           };
                   }
               );

            var items = new
            {
                div = default(IHTMLDiv),
                packageName = "",
                name = "",
                Remove = default(IHTMLButton),
                Launch = default(IHTMLButton)
            }.ToEmptyList();

            Action queryIntentActivities =
                async delegate
                {
                    var a = new List<string>();

                    // Send data from JavaScript to the server tier
                    await service.queryIntentActivities(
                        yield: (packageName, name, icon_base64, label) =>
                        {
                            #region yield
                            a.Add(packageName);

                            // already have it
                            if (items.Any(k => k.packageName == packageName))
                                return;

                            var div = new IHTMLDiv();
                            div.style.margin = "1em";

                            if (Native.Document.body.firstChild == null)
                                div.AttachToDocument();
                            else
                                Native.Document.body.insertBefore(div, Native.Document.body.firstChild);

                            new IHTMLImage { src = "data:image/png;base64," + icon_base64 }.AttachTo(div);
                            new IHTMLSpan { innerText = label }.AttachTo(div);

                            var Remove = new IHTMLButton { innerText = "Remove" }.AttachTo(div).WhenClicked(
                                    btn =>
                                    {
                                        // http://help.adobe.com/en_US/air/build/WSfffb011ac560372f-5d0f4f25128cc9cd0cb-7ffd.html


                                        if (!Native.window.confirm("Remove " + name + "?"))
                                            return;

                                        service.Remove(packageName, name);
                                    }
                               );


                            //div.appendChild(new { icon_base64.Length }.ToString());

                            var Launch = new IHTMLButton { innerText = "Launch" }.AttachTo(div).WhenClicked(
                                    btn =>
                                    {
                                        // http://help.adobe.com/en_US/air/build/WSfffb011ac560372f-5d0f4f25128cc9cd0cb-7ffd.html

                                        service.Launch(packageName, name);
                                    }
                                );


                            var LaunchFloat = new IHTMLButton { innerText = "Launch Float" }.AttachTo(div).WhenClicked(
                                 btn =>
                                 {

                                     service.Launch(packageName, name,
                                         ExtraKey: "Float",
                                         ExtraValue: "Float"
                                     );
                                 }
                             );

                            // var LaunchWebService = new IHTMLButton { innerText = "Launch WebService" }.AttachTo(div).WhenClicked(
                            //    btn =>
                            //    {

                            //        //service.Launch(packageName, name);
                            //    }
                            //);


                            var item = new
                            {
                                div,
                                packageName,
                                name,
                                Remove,
                                Launch
                            };


                            items.Add(item);

                            //https://play.google.com/store/apps/details?id=com.abstractatech.battery

                            new IHTMLAnchor { href = "https://play.google.com/store/apps/details?id=" + packageName, innerText = name }.AttachTo(div);
                            #endregion

                        }
                    );

                    items.WithEach(
                                   item =>
                                   {
                                       if (a.Contains(item.packageName))
                                           return;

                                       item.div.style.color = "red";

                                       item.Launch.disabled = true;
                                       item.Remove.disabled = true;
                                   }
                               );

                    // remove others!
                };

            queryIntentActivities();

            new Timer(
                delegate
                {
                    items.WithEach(
                        item =>
                        {
                            if (string.IsNullOrEmpty(page.search.value))
                            {
                                item.div.Show();
                            }
                            else
                            {
                                if (item.packageName.Contains(page.search.value))
                                {
                                    item.div.Show();
                                }
                                else
                                {
                                    item.div.Hide();
                                }
                            }

                        }
                    );

                    queryIntentActivities();
                }
            ).StartInterval(2000);


        }
        /// <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)
        {
            var y = page.ThePath_y;
            var x = page.ThePath_x;
            var z = page.ThePath_z;

            page.TheText.textContent = "pointer lock movement";

            // script: error JSC1000: No implementation found for this native method, please implement [static Microsoft.CSharp.RuntimeBinder.Binder.IsEvent(Microsoft.CSharp.RuntimeBinder.CSharpBinderFlags, System.String, System.Type)]

            //pp.setAttribute("d", pp.getAttribute("d") + " l100,-10         ");



            var history =
                new { x = 0.0, y = 0.0, z = 0.0 }.ToEmptyList();


            50.Times(
                delegate
                {
                    history.Add(
                        // vec2?
                       new { x = 0.0, y = 0.0, z = 0.0 }
                   );

                }
            );

            var movementX = 0.0;
            var movementY = 0.0;
            var movementZ = 0.0;

            Native.Document.body.onmousedown +=
                  e =>
                  {
                      Native.Document.body.requestPointerLock();
                  };


            Native.Document.body.onmousemove +=
                e =>
                {
                    if (Native.Document.body == Native.Document.pointerLockElement)
                    {
                        movementX += e.movementX;
                        movementY += e.movementY;
                    }
                };

            new ScriptCoreLib.JavaScript.Runtime.Timer(
                delegate
                {
                    history.Add(
                        // vec2?
                        new { x = movementX, y = movementY, z = movementZ }
                            );

                    movementX = 0;
                    movementY = 0;
                    movementZ = 0;

                    if (history.Count > 500)
                    {
                        history.RemoveAt(0);
                    }

                    // http://www.w3.org/TR/SVG/paths.html#PathDataMovetoCommands
                    var xw = new StringBuilder().Append("M10,200 ");
                    var yw = new StringBuilder().Append("M10,300 ");
                    var zw = new StringBuilder().Append("M10,400 ");


                    history.WithEachIndex(
                        (p, i) =>
                        {
                            xw.Append(" L" + (10 + 2 * i) + "," + (p.x + 200));
                            yw.Append(" L" + (10 + 2 * i) + "," + (p.y + 300));
                            zw.Append(" L" + (10 + 2 * i) + "," + (p.z + 400));

                        }
                    );

                    xw.Append(" L" + (10 + 2 * history.Count) + "," + (201));
                    yw.Append(" L" + (10 + 2 * history.Count) + "," + (301));
                    zw.Append(" L" + (10 + 2 * history.Count) + "," + (401));

                    //Console.WriteLine(new { xw, yw });

                    y.d = yw.ToString();
                    x.d = xw.ToString();
                    z.d = zw.ToString();
                }
            ).StartInterval(1000 / 10);


            Native.Document.onkeydown +=
                delegate
                {
                    movementZ = 100;
                };

            Native.Document.onkeyup +=
            delegate
            {
                movementZ = 0;
            };


            Native.window.ondeviceorientation +=
                eventData =>
                {

                    movementX = eventData.alpha;
                    movementY = eventData.beta;
                    movementZ = eventData.gamma;


                };
    
        }
        /// <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)
        {
            var random = new Random();
            device_id = random.Next();

            #region con
            var con = new ConsoleForm();

            con.InitializeConsoleFormWriter();
            con.StartPosition = FormStartPosition.Manual;
            con.Show();

            // make it slim
            con.Height = 100;

            // TopMost
            con.GetHTMLTarget().style.zIndex = 20002;
            con.Opacity = 0.9;




            Action Toggle =
                delegate
                {
                    if (con.WindowState == FormWindowState.Minimized)
                    {
                        con.WindowState = FormWindowState.Normal;

                    }
                    else
                    {
                        con.WindowState = FormWindowState.Minimized;


                    }

                    // put the console far right bottom
                    con.MoveTo(
                        Native.window.Width - con.Width,
                        Native.window.Height - con.Height
                    );

                };

            Action<int> AtKeyCode =
               KeyCode =>
               {
                   Console.WriteLine(
                       new { KeyCode }

                   );



                   // US
                   if (KeyCode == 222)
                   {
                       Toggle();
                   }
                   // EE
                   if (KeyCode == 192)
                   {
                       Toggle();
                   }
               };


#if onorientationchange
            Native.window.onorientationchange +=
                e =>
                {
                    Toggle();
                };
#endif

            Native.document.onkeyup +=
                e =>
                {
                    AtKeyCode(e.KeyCode);

                };

            Toggle();
            #endregion

            Console.WriteLine("console ready for " + new { id = device_id });

            var x = 0;
            var y = 0;

            #region Virtual Screen
            FormStyler.AtFormCreated = LikeDesktop;
            var fs = new Form { };

            //fs.FormBorderStyle = FormBorderStyle.None;

            fs.BackColor = Color.FromArgb(0, 148, 155);
            fs.Width = Native.screen.width / 4;
            fs.Height = Native.screen.height / 4;
            fs.Show();
            fs.Opacity = 0.5;

            FormStyler.AtFormCreated = LikeVirtualScreen;
            var fvs = new Form { Text = "Virtual Screen" };
            fvs.BackColor = Color.Transparent;
            FormStyler.AtFormCreated = FormStyler.LikeWindowsClassic;
            fvs.Width = Native.screen.width / 4;
            fvs.Height = Native.screen.height / 4;
            fvs.Show();
            fvs.Owner = fs;

            var fw = new Form { };

            fw.Width = Native.window.Width / 4;
            fw.Height = Native.window.Height / 4;
            fw.Show();
            fw.Owner = fvs;
            fw.Opacity = 0.8;

            KeepOwnedFormsLinkedToOwnerLocation(fs);
            KeepOwnedFormsLinkedToOwnerLocation(fvs);
            KeepOwnedFormsLinkedToOwnerLocation(fw);
            #endregion


            // doesnt work yet?
            //fw.SizeGripStyle = SizeGripStyle.Hide;

            var svg = new ISVGSVGElement().AttachTo(page.content);
            svg.style.SetLocation(0, 0);

            var vsvg = new ISVGSVGElement().AttachTo(fvs.GetHTMLTarget());
            vsvg.style.SetLocation(0, 0);

            #region VirtualScreenUpdate
            Action VirtualScreenUpdate = delegate
            {
                if (fs.Capture)
                    return;

                // dragging it?
                if (fvs.Capture)
                    return;

                var max_right = fvs.OwnedForms.Max(k => k.Right);
                var min_left = fvs.OwnedForms.Min(k => k.Left);

                var max_bottom = fvs.OwnedForms.Max(k => k.Bottom);
                var min_top = fvs.OwnedForms.Min(k => k.Top);

                DisableKeepOwnedFormsLinkedToOwnerLocation = true;

                fvs.Left = min_left;
                fvs.Top = min_top;
                fvs.Width = max_right - min_left;
                fvs.Height = max_bottom - min_top;

                page.content.style.SetLocation(
                    (min_left - fw.Left) * 4,
                    (min_top - fw.Top) * 4,
                    (max_right - min_left) * 4,
                    (max_bottom - min_top) * 4
                );



                DisableKeepOwnedFormsLinkedToOwnerLocation = false;

            };
            #endregion


            fw.LocationChanged +=
                delegate
                {
                    VirtualScreenUpdate();
                };

            #region AtResize
            Action AtResize = delegate
            {
                // screen can change, but only once, when window is moved to the other monitor?
                fs.Text = "Screen " + new { Native.screen.width, Native.screen.height };
                fs.Width = Native.screen.width / 4;
                fs.Height = Native.screen.height / 4;

                fw.Text = " " + new { Native.window.Width, Native.window.Height
#if onorientationchange                    
                    , Native.window.orientation 
#endif
                };
                fw.Width = Native.window.Width / 4;
                fw.Height = Native.window.Height / 4;

                VirtualScreenUpdate();
            };



            Native.window.onresize +=
                delegate
                {
                    AtResize();
                };

            Native.window.onfocus +=
                delegate
                {
                    AtResize();
                };

            Native.window.onblur +=
                delegate
                {
                    AtResize();
                };

            new ScriptCoreLib.JavaScript.Runtime.Timer(
                delegate
                {
                    AtResize();
                }
            ).StartInterval(1000 / 2);
            #endregion


            // what is attaching what?
            // what about await
            var svgcursor1ghost = new cursor1().AttachTo(page.content).ToSVG();

            var svgcursor1 = new cursor1().AttachTo(page.content).ToSVG();

            svgcursor1.fill += svgcursor1ghost.fill;

            var vcursor = new cursor1().AttachTo(fvs.GetHTMLTarget()).ToSVG();

            var Shadows = new List<Form>();

            Func<Form, Form> CreateShadow =
                _fw =>
                {
                    FormStyler.AtFormCreated = LikeVirtualWindow;
                    var fwshadow = new Form();
                    Shadows.Add(fwshadow);

                    fwshadow.BackColor = Color.Transparent;
                    FormStyler.AtFormCreated = FormStyler.LikeWindowsClassic;
                    fwshadow.Width = Native.screen.width / 4;
                    fwshadow.Height = Native.screen.height / 4;
                    fwshadow.Show();


                    Action fwshadow_update = delegate
                    {

                        fwshadow.MoveTo(_fw.Left, _fw.Top);
                        fwshadow.SizeTo(_fw.Width, _fw.Height);
                    };

                    _fw.LocationChanged +=
                        delegate
                        {
                            fwshadow_update();
                        };

                    _fw.SizeChanged +=
                        delegate
                        {
                            fwshadow_update();

                        };

                    fwshadow_update();

                    _fw.BringToFront();

                    return fwshadow;
                };

            Shadows.Add(CreateShadow(fw));

            bool canexit = false;
            bool canenter = true;

            Action at_exit_MultiMouseMode = delegate
            {
                //Console.WriteLine("at_exit_MultiMouseMode");
            };

            Action at_enter_MultiMouseMode = delegate
            {

                //Console.WriteLine("at_enter_MultiMouseMode");
            };

            Action<IEvent.MouseButtonEnum> at_mousedown = button =>
            {

                //Console.WriteLine("at_enter_mousedown: " + button);
            };


            Action at_mouseup = delegate
            {

                //Console.WriteLine("at_enter_mouseup");
            };




            var path = new ISVGPathElement().AttachTo(svg);
            path.setAttribute("style", "stroke: black; stroke-width: 4; fill: none;");

            var path_d = "";



            var vpath = new ISVGPathElement().AttachTo(vsvg);
            vpath.setAttribute("style", "stroke: black; stroke-width: 1; fill: none;");

            var vpath_d = "";

            bool internal_ismousedown = false;

            Action<IEvent.MouseButtonEnum> internal_mousedown = button =>
            {
                internal_ismousedown = true;

                path = new ISVGPathElement().AttachTo(svg);

                if (button == IEvent.MouseButtonEnum.Left)
                    path.setAttribute("style", "stroke: black; stroke-width: 4; fill: none;");
                else
                    path.setAttribute("style", "stroke: rgb(0, 108, 115); stroke-width: 32; fill: none;");

                path_d = "";


                vpath = new ISVGPathElement().AttachTo(vsvg);

                if (button == IEvent.MouseButtonEnum.Left)
                    vpath.setAttribute("style", "stroke: black; stroke-width: 1; fill: none;");
                else
                    vpath.setAttribute("style", "stroke: rgb(0, 108, 115); stroke-width: 8; fill: none;");

                vpath_d = "";

                svgcursor1.fill(0, 0, 255);
                vcursor.fill(0, 0, 255);

                //path.d = "    M100,50  L10,10   L200,200   ";
                path_d += " M" + x + "," + y;
                path.d = path_d;

                vpath_d += " M" + (x / 4) + "," + (y / 4);
                vpath.d = vpath_d;
            };

            Action<IEvent.MouseButtonEnum> mousedown = button =>
            {
                at_mousedown(button);
                internal_mousedown(button);
            };

            Action internal_mouseup = delegate
            {
                internal_ismousedown = false;





                svgcursor1.fill(0, 255, 0);
                vcursor.fill(0, 255, 0);
            };

            Action mouseup = delegate
            {
                at_mouseup();
                internal_mouseup();
            };



            #region exit_MultiMouseMode
            Action internal_exit_MultiMouseMode = delegate
            {
                svgcursor1.fill(0, 0, 0);
                vcursor.fill(0, 0, 0);

                con.Opacity = 0.9;
                page.content.style.Opacity = 0.3;
                page.content.style.zIndex = 0;

                page.content.style.backgroundColor = "rgba(0, 148, 155, 1)";
                Native.Document.body.style.backgroundColor = "black";

                page.content.style.With(
                    (dynamic s) =>
                    {
                        s.webkitFilter = "blur(3px)";
                    }
                );

                page.info.style.With(
                    (dynamic s) =>
                    {
                        s.webkitFilter = "";
                    }
                );

                Shadows.WithEach(
                   f =>
                       f.GetHTMLTarget().style.With(
                           (dynamic s) =>
                           {
                               s.webkitFilter = "";
                           }
                       )
                 );


                fs.FormsByOwnership().WithEach(
                 f =>
                     f.GetHTMLTarget().style.With(
                         (dynamic s) =>
                         {
                             s.webkitFilter = "";

                         }
                     )
            );


                fvs.OwnedForms.WithEach(
                    k =>
                    {
                        k.GetHTMLTarget().style.display = IStyle.DisplayEnum.block;
                    }
                );




            };

            Action exit_MultiMouseMode = delegate
            {
                if (!canexit)
                    return;

                canexit = false;
                canenter = true;

                at_exit_MultiMouseMode();
                internal_exit_MultiMouseMode();
            };
            #endregion

            #region enter_MultiMouseMode
            Action internal_enter_MultiMouseMode = delegate
            {
                svgcursor1.fill(255, 0, 0);
                vcursor.fill(255, 0, 0);
                con.Opacity = 0.5;

                page.content.style.Opacity = 1.0;
                page.content.style.backgroundColor = "";
                Native.Document.body.style.backgroundColor = "rgba(0, 148, 155, 1)";

                page.content.style.zIndex = 20000;
                con.GetHTMLTarget().style.zIndex = 20002;
                page.content.style.With(
                    (dynamic s) =>
                    {
                        s.webkitFilter = "";
                    }
                );

                page.info.style.With(
                    (dynamic s) =>
                    {
                        s.webkitFilter = "blur(3px)";
                    }
                );


                fvs.OwnedForms.WithEach(
                     k =>
                     {
                         k.GetHTMLTarget().style.display = IStyle.DisplayEnum.none;
                     }
                 );

                Shadows.WithEach(
                  f =>
                      f.GetHTMLTarget().style.With(
                          (dynamic s) =>
                          {
                              s.webkitFilter = "blur(3px)";
                          }
                      )
                );


                fs.FormsByOwnership().WithEach(
                    f =>
                        f.GetHTMLTarget().style.With(
                            (dynamic s) =>
                            {
                                s.webkitFilter = "blur(3px)";
                            }
                        )
                );
            };

            Action enter_MultiMouseMode = delegate
            {
                if (!canenter)
                    return;

                canexit = true;
                canenter = false;

                at_enter_MultiMouseMode();
                internal_enter_MultiMouseMode();
            };
            #endregion





            #region onmousemove



            Native.Document.body.onmouseup +=
              e =>
              {
                  if (Native.Document.pointerLockElement == Native.Document.body)
                  {
                      mouseup();
                      return;
                  }
              };

            Native.Document.body.onmousedown +=
                e =>
                {
                    if (Native.Document.pointerLockElement == Native.Document.body)
                    {
                        mousedown(e.MouseButton);
                        return;
                    }

                    Console.WriteLine("requesting MultiMouse mode!");

                    x = e.CursorX;
                    y = e.CursorY;
                    e.preventDefault();
                    Native.Document.body.requestPointerLock();

                };

            Action<int, int> at_set_cursor_position = delegate { };


            var pxx = 0;
            var pyy = 0;

            var ghost_busy = false;

            Action<int, int> internal_set_cursor_position =
                 (xx, yy) =>
                 {
                     // already set to that exact location!
                     if (pxx == xx)
                         if (pyy == yy)
                             return;


                     pxx = xx;
                     pyy = yy;

                     vcursor.Element.style.SetSize(
                           cursor1.ImageDefaultWidth / 4,
                           cursor1.ImageDefaultHeight / 4
                       );

                     vcursor.Element.style.SetLocation(
                           (xx - 14) / 4,
                           (yy - (64 - 56)) / 4
                       );


                     svgcursor1.Element.style.SetLocation(

                         xx - 14,

                         // inscaope/svg Y is upside down!
                         yy - (64 - 56)

                     );

                     if (!ghost_busy)
                     {
                         ghost_busy = true;

                         svgcursor1ghost.Element.style.Opacity = 0.2;
                         svgcursor1ghost.Element.style.With(
                                (dynamic s) => s.webkitTransition = "all 0.5s linear"
                         );

                         svgcursor1ghost.Element.style.SetLocation(

                            pxx - 14,

                            // inscaope/svg Y is upside down!
                            pyy - (64 - 56)

                        );

                         new ScriptCoreLib.JavaScript.Runtime.Timer(
                             delegate
                             {
                                 svgcursor1ghost.Element.style.SetLocation(

                                    pxx - 14,

                                    // inscaope/svg Y is upside down!
                                    pyy - (64 - 56)

                                );

                                 ghost_busy = false;
                             }
                         ).StartTimeout(500);
                     }


                     // if this window will be activated next time we can continue where we were
                     // told to..
                     x = xx;
                     y = yy;

                     if (internal_ismousedown)
                     {
                         path_d += " L" + x + "," + y;
                         path.d = path_d;

                         vpath_d += " L" + (x / 4) + "," + (y / 4);
                         vpath.d = vpath_d;
                     }

                 };

            Action<int, int> set_cursor_position =
                (xx, yy) =>
                {
                    at_set_cursor_position(xx, yy);
                    internal_set_cursor_position(xx, yy);
                };

            Native.Document.body.onmousemove +=
                e =>
                {
                    if (Native.Document.pointerLockElement == Native.Document.body)
                    {
                        enter_MultiMouseMode();

                        x += e.movementX;
                        y += e.movementY;

                        // clip it
                        // fullscreen behaves differently?
                        x = x.Min(fvs.Width * 4).Max(0);
                        y = y.Min(fvs.Height * 4).Max(0);

                        set_cursor_position(x, y);
                    }
                    else
                    {
                        exit_MultiMouseMode();
                    }

                };
            #endregion

            internal_exit_MultiMouseMode();
            internal_set_cursor_position(0, 0);

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

                     e.touches[0].With(
                         touch =>
                         {
                             // how do we enter?
                             enter_MultiMouseMode();
                             // exit by broswer history move?

                             set_cursor_position(touch.clientX, touch.clientY);

                             // ipad has 11 touchpoints. multiply that with the number of devices/
                             // for now we support 1 pointer per session :)

                             if (e.touches.length == 1)
                                 mousedown(IEvent.MouseButtonEnum.Left);
                             else
                                 mousedown(IEvent.MouseButtonEnum.Right);

                         }
                     );
                 };

            Native.document.body.ontouchend +=
               e =>
               {

                   e.preventDefault();
                   e.stopPropagation();



                   // ipad has 11 touchpoints. multiply that with the number of devices/
                   // for now we support 1 pointer per session :)

                   if (e.touches.length == 0)
                       mouseup();
                   else
                       mousedown(IEvent.MouseButtonEnum.Left);

               };

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


                     e.touches[0].With(
                         touch =>
                         {
                             set_cursor_position(touch.clientX, touch.clientY);
                         }
                     );
                 };



            #region onmessage

            bool disable_bind_reconfigure = false;


            Action<int, int> internal_reconfigure =
                delegate { };

            Action<MessageEvent, XElement> internal_onmessage =
                (e, xml) =>
                {
                    device_onmessage(0, xml);
                };

            Native.window.onmessage +=
                e =>
                {
                    // who sent this? :P
                    var source = (string)e.data;
                    //var now = DateTime.Now;
                    //Console.WriteLine(now + " " + source);


                    var xml = XElement.Parse(source);




                    internal_onmessage(e, xml);
                };

            var friendly_devices = new
            {
                source_device_id = 0,
                f = default(Form),
                children = new
                {
                    child_id = 0,
                    fc = default(Form)
                }.ToEmptyList()
            }.ToEmptyList();

            #region device_onmessage
            this.device_onmessage =
                (source_device_id, xml) =>
                {
                    // mothership to local network?
                    // source_device_id = 0 means it came from one of our virtual screens?

                    if (xml.Name.LocalName == "at_mousedown")
                    {
                        int button = int.Parse(xml.Attribute("button").Value);
                        internal_mousedown((IEvent.MouseButtonEnum)button);
                    }

                    if (xml.Name.LocalName == "at_mouseup")
                    {
                        internal_mouseup();
                    }

                    if (xml.Name.LocalName == "at_enter_MultiMouseMode")
                    {
                        internal_enter_MultiMouseMode();
                    }

                    if (xml.Name.LocalName == "at_exit_MultiMouseMode")
                    {
                        internal_exit_MultiMouseMode();
                    }

                    if (xml.Name.LocalName == "at_set_cursor_position")
                    {
                        int xx = int.Parse(xml.Attribute("x").Value);
                        int yy = int.Parse(xml.Attribute("y").Value);

                        internal_set_cursor_position(xx, yy);
                    }


                };
            #endregion

            var ListOfChildren = new { child_id = 0, fc = default(Form) }.ToEmptyList();

            // when is this called?
            this.device_bind =
                (mothership_postXElement) =>
                {
                    // we might now be able to invoke the server, and via that any other device
                    Console.WriteLine("device_bind");

                    #region at_enter_MultiMouseMode
                    at_enter_MultiMouseMode +=
                        delegate
                        {
                            var xml = new XElement("at_enter_MultiMouseMode");

                            mothership_postXElement(xml);
                        };
                    #endregion

                    #region at_exit_MultiMouseMode
                    at_exit_MultiMouseMode +=
                        delegate
                        {
                            mothership_postXElement(new XElement("at_exit_MultiMouseMode"));
                        };
                    #endregion

                    #region at_mousedown
                    at_mousedown +=
                      button =>
                      {
                          mothership_postXElement(new XElement("at_mousedown", new XAttribute("button", "" + (int)button)));
                      };
                    #endregion

                    #region at_mouseup
                    at_mouseup +=
                     delegate
                     {
                         mothership_postXElement(new XElement("at_mouseup"));
                     };
                    #endregion

                    #region at_set_cursor_position
                    at_set_cursor_position +=
                       (xx, yy) =>
                       {

                           var xml = new XElement("at_set_cursor_position",
                               // int not yet supported?
                                   new XAttribute("x", "" + xx),
                                   new XAttribute("y", "" + yy)
                               );

                           mothership_postXElement(
                               xml
                           );



                       };
                    #endregion

                    // now we can reply..
                    this.device_onmessage +=
                       (source_device_id, xml) =>
                       {
                           #region at_virtualwindowsync_reconfigure
                           if (source_device_id != 0)
                               if (xml.Name.LocalName == "at_virtualwindowsync_reconfigure")
                               {
                                   int __device_id = int.Parse(xml.Attribute("device_id").Value);

                                   if (__device_id == device_id)
                                   {
                                       // are we being reconfigured?

                                       friendly_devices.Where(k => k.source_device_id == source_device_id).WithEach(
                                           q =>
                                           {
                                               int dx = int.Parse(xml.Attribute("dx").Value);
                                               int dy = int.Parse(xml.Attribute("dy").Value);
                                               disable_bind_reconfigure = true;

                                               q.f.MoveTo(
                                                   fw.Left - dx,
                                                   fw.Top - dy
                                               );
                                               disable_bind_reconfigure = false;

                                           }
                                       );
                                   }
                               }
                           #endregion

                           #region at_virtualwindowsync
                           if (source_device_id != 0)
                               if (xml.Name.LocalName == "at_virtualwindowsync")
                               {
                                   Console.WriteLine("got at_virtualwindowsync");

                                   // do we know this device?
                                   var q = friendly_devices.FirstOrDefault(k => k.source_device_id == source_device_id);

                                   int w = int.Parse(xml.Attribute("w").Value);
                                   int h = int.Parse(xml.Attribute("h").Value);

                                   Action reposition = delegate { };

                                   if (q == null)
                                   {
                                       var fc = new Form { Text = new { source_device_id }.ToString() };

                                       q = new { source_device_id, f = fc, children = new { child_id = 0, fc = default(Form) }.ToEmptyList() };

                                       friendly_devices.Add(q);

                                       q.f.StartPosition = FormStartPosition.Manual;
                                       q.f.Show();
                                       // show should respect opacity?
                                       q.f.Opacity = 0.3;


                                       // where to put it?
                                       // left or right?

                                       var max_right = fvs.OwnedForms.Max(k => k.Right);
                                       var min_left = fvs.OwnedForms.Min(k => k.Left);

                                       if (source_device_id < device_id)
                                           q.f.Left = min_left - w;
                                       else
                                           q.f.Left = max_right;

                                       q.f.Top = fw.Top;
                                       q.f.Owner = fvs;

                                       var fcShadow = CreateShadow(q.f);
                                       Shadows.Add(fcShadow);

                                       #region from now on if we move any of our screens
                                       // in relation to this source_device_id we have to notify it

                                       Action SendDelta = delegate
                                       {
                                           var pdx = fc.Left - fw.Left;
                                           var pdy = fc.Top - fw.Top;

                                           mothership_postXElement(
                                             new XElement("at_virtualwindowsync_reconfigure",
                                                 new XAttribute("device_id", "" + source_device_id),
                                                 new XAttribute("dx", "" + pdx),
                                                 new XAttribute("dy", "" + pdy)
                                             )
                                           );
                                       };

                                       fw.LocationChanged +=
                                           delegate
                                           {
                                               if (disable_bind_reconfigure)
                                                   return;

                                               SendDelta();
                                           };

                                       fc.LocationChanged +=
                                           delegate
                                           {
                                               if (disable_bind_reconfigure)
                                                   return;


                                               SendDelta();
                                           };


                                       #endregion


                                   }

                                   // thanks for letting us know that you changed your size...
                                   q.f.Width = w;
                                   q.f.Height = h;


                                   xml.Elements("child").WithEach(
                                       cxml =>
                                       {
                                           // any new children?
                                           int child_id = int.Parse(cxml.Attribute("child_id").Value);

                                           int pdx = int.Parse(cxml.Attribute("pdx").Value);
                                           int pdy = int.Parse(cxml.Attribute("pdy").Value);

                                           int cw = int.Parse(cxml.Attribute("w").Value);
                                           int ch = int.Parse(cxml.Attribute("h").Value);

                                           var cq = q.children.FirstOrDefault(k => k.child_id == child_id);

                                           if (cq == null)
                                           {
                                               var fc = new Form { Text = new { source_device_id, child_id }.ToString() };

                                               cq = new { child_id, fc };

                                               q.children.Add(cq);

                                               cq.fc.StartPosition = FormStartPosition.Manual;
                                               cq.fc.Show();
                                               // show should respect opacity?
                                               cq.fc.Opacity = 0.2;

                                               // if this child needs to be between then add it
                                               // before reposition

                                               cq.fc.Owner = fvs;

                                               var fcShadow = CreateShadow(cq.fc);
                                               Shadows.Add(fcShadow);

                                           }


                                           cq.fc.Left = q.f.Left + pdx;
                                           cq.fc.Top = q.f.Top + pdy;

                                           // thanks for letting us know that you changed your size...
                                           cq.fc.Width = cw;
                                           cq.fc.Height = ch;
                                       }
                                   );

                               }
                           #endregion

                       };

                    // lets tell the world about virtual screens owned by us.
                    // lets start by advertising our size.

                    #region at_virtualwindowsync
                    var t = new ScriptCoreLib.JavaScript.Runtime.Timer(
                        delegate
                        {
                            // do we know whats the dx to other windows?
                            var xml = new XElement("at_virtualwindowsync",
                                // int not yet supported?
                                new XAttribute("w", "" + fw.Width),
                                new XAttribute("h", "" + fw.Height)


                            );

                            #region what about children?
                            ListOfChildren.WithEach(
                                c =>
                                {
                                    var pdx = c.fc.Left - fw.Left;
                                    var pdy = c.fc.Top - fw.Top;

                                    xml.Add(

                                        new XElement("child",
                                            new XAttribute("child_id", "" + c.child_id),
                                        // int not yet supported?
                                            new XAttribute("pdx", "" + pdx),
                                            new XAttribute("pdy", "" + pdy),
                                            new XAttribute("w", "" + c.fc.Width),
                                            new XAttribute("h", "" + c.fc.Height)
                                        )

                                    );
                                }
                            );
                            #endregion


                            mothership_postXElement(
                              xml
                            );

                            Console.WriteLine("sent at_virtualwindowsync");

                        }
                    );

                    t.StartInterval(5000);
                    #endregion

                };

            Action<IWindow, Form> bind =
                (w, fc) =>
                {
                    this.device_bind(w.postXElement);

                    internal_onmessage +=
                        (e, xml) =>
                        {
                            if (xml.Name.LocalName == "reconfigure")
                            {
                                // how do we know this reconfigrue event is for us?

                                if (e.source == w)
                                {
                                    disable_bind_reconfigure = true;

                                    int dx = int.Parse(xml.Attribute("dx").Value);
                                    int dy = int.Parse(xml.Attribute("dy").Value);

                                    //Console.WriteLine("reconfigure " + new { dx, dy, fw.Left });

                                    //fw.Left += dx;
                                    //fw.Top += dy;


                                    fc.MoveTo(
                                        fw.Left - dx,
                                        fw.Top - dy
                                    );

                                    disable_bind_reconfigure = false;
                                }
                            }
                        };

                    Action SendDelta = delegate
                    {
                        var pdx = fc.Left - fw.Left;
                        var pdy = fc.Top - fw.Top;

                        w.postXElement(
                          new XElement("reconfigure",
                              new XAttribute("dx", "" + pdx),
                              new XAttribute("dy", "" + pdy)
                          )
                        );
                    };

                    fw.LocationChanged +=
                        delegate
                        {
                            if (disable_bind_reconfigure)
                                return;

                            SendDelta();
                        };

                    fc.LocationChanged +=
                        delegate
                        {
                            if (disable_bind_reconfigure)
                                return;


                            SendDelta();
                        };
                };
            #endregion


            #region opener
            Native.window.opener.With(
                w =>
                {
                    // disable features
                    page.info.Hide();

                    Console.WriteLine("we have opener: " + w.document.location.href);

                    var fc = new Form { Text = "opener" };

                    fc.Owner = fvs;

                    Action cAtResize = delegate
                    {
                        fc.Text = "Opener " + new { w.Width, w.Height };
                        fc.Width = w.Width / 4;
                        fc.Height = w.Height / 4;
                    };

                    w.onresize += delegate
                    {
                        cAtResize();
                    };

                    var ct = new ScriptCoreLib.JavaScript.Runtime.Timer(
                       delegate
                       {
                           cAtResize();
                       }
                    );

                    ct.StartInterval(1000 / 15);

                    cAtResize();

                    fc.StartPosition = FormStartPosition.Manual;
                    fc.Show();
                    fc.Opacity = 0.7;
                    fc.BackColor = Color.Transparent;

                    var fcShadow = CreateShadow(fc);
                    Shadows.Add(fcShadow);



                    Native.window.requestAnimationFrame +=
                        delegate
                        {
                            // ScriptCoreLib Windows Forms has a few bugs:P
                            fc.MoveTo(fw.Left - fc.Width, fw.Top);

                            bind(w, fc);
                        };

                }
            );
            #endregion

            #region make info clickable

            page.info.onmousedown +=
             e =>
             {
                 if (internal_ismousedown)
                     return;

                 e.stopPropagation();
             };

            page.info.ontouchstart +=
              e =>
              {
                  if (internal_ismousedown)
                      return;


                  e.stopPropagation();
              };

            page.info.ontouchmove +=
          e =>
          {
              if (internal_ismousedown)
                  return;


              e.stopPropagation();
          };

            page.info.ontouchend +=
     e =>
     {
         if (internal_ismousedown)
             return;

         e.stopPropagation();
     };
            #endregion

            #region OpenChildSession



            page.OpenChildSession.onclick +=
                e =>
                {
                    e.preventDefault();

                    Console.WriteLine("open child session...");

                    Native.window.open(
                        Native.Document.location.href,
                        "_blank", 400, 400, true).With(
                        w =>
                        {
                            w.onload +=
                                delegate
                                {
                                    if (w.document.location.href == "about:blank")
                                        return;

                                    Console.WriteLine("child onload " + w.document.location.href);

                                    var fc = new Form { Text = "child" };



                                    Action cAtResize = delegate
                                    {
                                        fc.Text = "Child " + new { w.Width, w.Height };
                                        fc.Width = w.Width / 4;
                                        fc.Height = w.Height / 4;

                                        VirtualScreenUpdate();
                                    };

                                    w.onresize += delegate
                                    {
                                        cAtResize();

                                    };

                                    var ct = new ScriptCoreLib.JavaScript.Runtime.Timer(
                                       delegate
                                       {
                                           cAtResize();
                                       }
                                    );

                                    ct.StartInterval(1000 / 2);

                                    //cAtResize();

                                    fc.StartPosition = FormStartPosition.Manual;
                                    fc.Show();
                                    fc.Opacity = 0.5;
                                    // first child could be a monitor to our right
                                    fc.MoveTo(fw.Right, fw.Top);
                                    fc.Owner = fvs;
                                    fc.BackColor = Color.Transparent;

                                    fc.Width = 400 / 4;
                                    fc.Height = 400 / 4;

                                    VirtualScreenUpdate();



                                    fc.LocationChanged +=
                                        delegate
                                        {
                                            VirtualScreenUpdate();

                                        };

                                    var fcShadow = CreateShadow(fc);
                                    Shadows.Add(fcShadow);

                                    var token = new { child_id = random.Next(), fc };

                                    ListOfChildren.Add(token);

                                    #region FormClosing
                                    w.onbeforeunload +=
                                        delegate
                                        {
                                            if (fc == null)
                                                return;

                                            w = null;

                                            ct.Stop();
                                            fc.Close();
                                            fc = null;
                                        };

                                    Native.window.onbeforeunload +=
                                        delegate
                                        {
                                            if (w == null)
                                                return;

                                            w.close();
                                            w = null;
                                        };

                                    fc.FormClosing +=
                                        delegate
                                        {

                                            if (w == null)
                                                return;

                                            ListOfChildren.Remove(token);
                                            Shadows.Remove(fcShadow);

                                            fc = null;

                                            w.close();
                                            w = null;
                                        };
                                    #endregion



                                    bind(w, fc);


                                };
                        }
                    );
                };
            #endregion





            Native.document.documentElement.style.overflow = IStyle.OverflowEnum.hidden;
        }