// can jsc accept parameters to constructor yet?
        // or fields?

        //public ApplicationWebService(int session)
        //{

        //}

        public void CheckDatabase(string e, Action<string> y)
        {
            try
            {
                //__ConsoleToDatabaseWriter.InternalWriteLine("CheckDatabase...");

                var data = new SystemConsoleOut();

                y("CheckDatabase ok");
            }
            catch (Exception ex)
            {
                y("CheckDatabase error " + new { ex });
            }

        }
        public void SelectTransactionKey(string session, Action<string> y)
        {
            try
            {
                var data = new SystemConsoleOut();

                Action<long> yield =
                    id =>
                    {
                        y("" + id);
                    };

                data.SelectTransactionKey(
                    e: new SystemConsoleOutQueries.SelectTransaction { session = int.Parse(session) },
                    yield: yield
                );

                //y("CheckDatabase ok");
            }
            catch (Exception ex)
            {
                //__ConsoleToDatabaseWriter.InternalWriteLine("SelectTransactionKey error " + new { ex });
                //y("CheckDatabase error " + new { ex });
            }

            //var c = new __ConsoleToDatabaseWriter(int.Parse(session));

            //c.data.SelectTransactionKey(
            //    new SystemConsoleOutQueries.SelectTransaction { session = int.Parse(session) },

            //    id =>
            //    {
            //        y("" + id);
            //    }
            //);
        }
        public /* will not be part of web service itself */ void Handler(WebServiceHandler h)
        {
            //            I/System.Console(12961): Caused by: java.lang.NullPointerException
            //I/System.Console(12961):        at ConsoleByCookie.ApplicationWebService.Handler(ApplicationWebService.java:162)
            //I/System.Console(12961):        ... 17 more

            var Accepts = h.Context.Request.Headers["Accept"];

            __ConsoleToDatabaseWriter.InternalWriteLine(
                new { h.Context.Request.Path, Accepts } + Environment.NewLine
            );


            //EventSource's response has a MIME type ("text/plain") that is not "text/event-stream". Aborting the connection.

            if (h.Context.Request.Path == "/wait")
            {
                Thread.Sleep(5000);
                h.CompleteRequest();
                return;
            }

            if (Accepts != null)
                if (Accepts.Contains("text/event-stream"))
                {
                    //try
                    //{
                    // http://www.w3schools.com/html/html5_serversentevents.asp
                    // http://caniuse.com/eventsource
                    // http://www.w3.org/TR/eventsource/
                    // https://developer.mozilla.org/en-US/docs/Server-sent_events/Using_server-sent_events

                    // http://opensource.apple.com/source/WebCore/WebCore-1640.1/page/EventSource.cpp?txt
                    // http://stackoverflow.com/questions/13166284/eventsources-response-has-a-mime-type-text-html-that-is-not-text-event-str

                    h.Context.Response.ContentType = "text/event-stream";
                    h.Context.Response.Cache.SetCacheability(HttpCacheability.NoCache);

                    //EventSource's response has a MIME type ("text/plain") that is not "text/event-stream". Aborting the connection.


                    // A potentially dangerous Request.QueryString value was detected from the client (e="<client value="15.12...").
                    //var _e_xml = h.Context.Request.RawUrl
                    //    .SkipUntilLastOrEmpty("?")
                    //    .SkipUntilOrEmpty("e=")
                    //    .TakeUntilIfAny("&");

                    //var _e_xml_decoded = HttpUtility.UrlDecode(_e_xml);


                    // No Cookies on andrid yet?
                    // http://en.wikipedia.org/wiki/List_of_HTTP_header_fields
                    var header_cookie = h.Context.Request.Headers["Cookie"];

                    //Implementation not found for type import :
                    //System.Web.HttpRequest :: System.Web.HttpCookieCollection get_Cookies()
                    //Did you forget to add the [Script] attribute?
                    //Please double check the signature!
                    //type: ConsoleByCookie.ApplicationWebService offset: 0x014c  method:Void Handler(ScriptCoreLib.Ultra.WebService.WebServiceHandler)

                    //foreach (var item in collection)
                    //{
                        
                    //}
                    var cookie_session = h.Context.Request.Cookies["session"];

                    //no session cookie? { header_cookie = session=2006041647; xfoo=foo; random=2376781287 }

                    #region workaround
                    if (cookie_session == null)
                        cookie_session = new HttpCookie(
                            "session",
                            header_cookie.SkipUntilLastOrEmpty("session=").TakeUntilOrEmpty(";")
                        );
                    #endregion

                    if (cookie_session == null)
                    {
                        //__ConsoleToDatabaseWriter.InternalWriteLine("no session cookie? " + new { header_cookie });
                        h.Context.Response.StatusCode = 500;
                        h.CompleteRequest();
                        return;
                    }

                    //var _e = XElement.Parse(cookie_session.Value);

                    var session = int.Parse(cookie_session.Value);

                    //var c = new __ConsoleToDatabaseWriter(session);
                    var data = new SystemConsoleOut();

                    var header_id = h.Context.Request.Headers["Last-Event-ID"];



                    var id = 0;

                    if (header_id != null)
                    {
                        //__ConsoleToDatabaseWriter.InternalWrite("Continue " + new { session, header_id }.ToString() + Environment.NewLine);

                        //id = int.Parse(XElement.Parse(header_id).Attribute("id").Value);
                        id = int.Parse(header_id);
                    }
                    else
                    {
                        data.SelectTransactionKey(session,
                            nextid =>
                            {
                                id = (int)nextid;

                                //var xml = new XElement("e",
                                //    new XAttribute("id", id)
                                //);
                                //__ConsoleToDatabaseWriter.InternalWrite("Reset To " + new { session, id }.ToString() + Environment.NewLine);

                                h.Context.Response.Write("id: " + id + "\n\n");
                                //h.Context.Response.Write("event: SystemConsoleOut\n");
                                h.Context.Response.Write("data: reset to " + new { id } + " \n\n");
                                h.Context.Response.Flush();

                            }
                        );
                    }

                    int retry = 1000 / 30;

                    Action CheckForUpdates = delegate
                    {
                        Action later = delegate { };
                        data.SelectTransactionKey(session,
                              nextid =>
                              {
                                  if (id == (int)nextid)
                                  {
                                      later = delegate
                                      {
                                          // no updates yet
                                          // it is imortant to sleep
                                          // only if we released the database.
                                          // otherwise other threads will wait for us!
                                          Thread.Sleep(retry);
                                      };

                                      return;
                                  }


                                  data.SelectContentUpdates(
                                      new SystemConsoleOutQueries.SelectContentUpdates
                                      {
                                          id = id,
                                          nextid = (int)nextid,
                                          session = session
                                      },
                                      r =>
                                      {
                                          string value = r.value;

                                          h.Context.Response.Write("event: SystemConsoleOut\n");
                                          h.Context.Response.Write("data: " +
                                              value.Replace("\r", "\\r").Replace("\n", "\\n") + "\n\n");

                                          // wont work on appengine?
                                          // https://groups.google.com/forum/?fromgroups=#!topic/google-appengine-java/LWWRMP5KU1w
                                          // https://developers.google.com/appengine/docs/java/runtime#Responses
                                          //  limited to 32MB

                                          // need to use channel API instead?
                                          // https://developers.google.com/appengine/docs/python/channel/overview

                                          h.Context.Response.Flush();
                                      }
                                  );


                                  id = (int)nextid;
                                  //var xml = new XElement("e",
                                  //    new XAttribute("id", id)
                                  //);

                                  //h.Context.Response.Write("id: " + xml.ToString() + "\n\n");
                                  h.Context.Response.Write("id: " + id + "\n\n");
                                  h.Context.Response.Flush();

                              }
                          );

                        later();
                    };

                    // loop forever?
                    var t = new Stopwatch();

                    t.Start();

                    //for (int i = 0; i < 1024; i++)
                    while (t.ElapsedMilliseconds < 7000)
                    {
                        // http://stackoverflow.com/questions/9743253/how-to-detect-a-disconnection-when-using-server-sent-events-on-asp-net

                        //if (!h.Context.Response.IsClientConnected)
                        //{
                        //    __ConsoleToDatabaseWriter.InternalWrite("!IsClientConnected");
                        //    break;
                        //}
                        //__ConsoleToDatabaseWriter.InternalWrite("CheckForUpdates " + new { Thread.CurrentThread.ManagedThreadId, session, i, id }.ToString() + Environment.NewLine);
                        CheckForUpdates();
                    }



                    //Thread.Sleep(5000);

                    #region prevent error 2, required if we did not send any other events
                    h.Context.Response.Write("retry: " + retry + "\n\n");
                    h.Context.Response.Write("data: retry later\n\n");
                    h.Context.Response.Flush();
                    #endregion

                    h.CompleteRequest();
                    //}
                    //catch //(Exception ex)
                    //{
                    //    throw;
                    //}


                }

            __ConsoleToDatabaseWriter.InternalWrite(
             new { h.Context.Request.Path, Accepts, h.Context.Response.ContentType } + " done!" + Environment.NewLine
         );

        }