public async Task <DaytimeResult> WriteAsync(HostName address, string service = "10013", ProtocolType protocolType = ProtocolType.Udp, string data = "")
        {
            switch (protocolType)
            {
            case ProtocolType.Tcp:
                return(await WriteTcpAsync(address, service, data));    //TODO: rename to WriteTcpAsync everywhere and WriteUdpAsync, too

            case ProtocolType.Udp:
                return(await WriteUdpAsync(address, service, data));
            }
            return(DaytimeResult.MakeFailed(SocketErrorStatus.SocketTypeNotSupported, 0.0));
        }
        private DaytimeResult ReadUdp(DataReader dr) //TODO: directly read buffer instead of weirding out the DataReader?
        {
            uint count = dr.UnconsumedBufferLength;

            if (count > 0)
            {
                byte[] buffer = new byte[dr.UnconsumedBufferLength];
                dr.ReadBytes(buffer);
                var stringresult = BufferToString.ToString(buffer);
                Log($"{stringresult}"); // Will be printed on the screen.
                var delta = DateTime.UtcNow.Subtract(UdpStartTime).TotalSeconds;
                return(DaytimeResult.MakeSucceeded(stringresult, delta));
            }
            else
            {
                var stringresult = "ERROR:No results!";
                Log($"DAYTIME: CLIENT:{stringresult}");
                var delta = DateTime.UtcNow.Subtract(UdpStartTime).TotalSeconds;
                return(DaytimeResult.MakeFailed(SocketErrorStatus.HttpInvalidServerResponse, delta));
            }
        }
        private void UdpSocket_MessageReceived(DatagramSocket sender, DatagramSocketMessageReceivedEventArgs args)
        {
            try
            {
                var dr        = args.GetDataReader();
                var udpResult = ReadUdp(dr);
                UdpResults.TryAdd(sender.Information.LocalPort, udpResult);
            }
            catch (Exception ex)
            {
                // This should not every actually happen
                Stats.NExceptions++;
                Log($"DAYTIME: CLIENT: ERROR {ex.Message}");
                var delta     = DateTime.UtcNow.Subtract(UdpStartTime).TotalSeconds;
                var udpResult = DaytimeResult.MakeFailed(ex, delta);
                UdpResults.TryAdd(sender.Information.LocalPort, udpResult);
            }

            // Don't need the socket after we get the first message
            sender.MessageReceived -= UdpSocket_MessageReceived;
            sender.Dispose();
        }
        /// <summary>
        /// Sends out a query and then waits for the reply. Waiting on a UDP involves waiting for a message to come back in.
        /// </summary>
        private async Task <DaytimeResult> WriteUdpAsync(HostName address, string service, string data)
        {
            UdpStartTime = DateTime.UtcNow;
            try
            {
                var udpSocket = new DatagramSocket(); //TODO: really use the same socket each time?
                await udpSocket.ConnectAsync(address, service);

                udpSocket.MessageReceived += UdpSocket_MessageReceived;

                if (string.IsNullOrEmpty(data))
                {
                    // A blank string, when written to a data writer, won't actually result in a
                    // UDP packet being sent. For the special case of not sending any data,
                    // use the WriteAsync on the socket's OutputStream directly.
                    var b = new Windows.Storage.Streams.Buffer(0);
                    await udpSocket.OutputStream.WriteAsync(b);

                    Stats.NWrites++;
                }
                else
                {
                    var dw = new DataWriter(udpSocket.OutputStream);
                    dw.WriteString(data);
                    await dw.StoreAsync();

                    Stats.NWrites++;
                }
                Log(ClientOptions.Verbosity.Verbose, $"Client: UDP: Sent request on local port {udpSocket.Information.LocalPort} request {data}");

                // Wait for an answer

                const int START_DELAY_MS = 10;
                int       currTotalDelay = 0;
                int       currDelay      = START_DELAY_MS;

                DaytimeResult udpResult = null;
                while (!UdpResults.TryRemove(udpSocket.Information.LocalPort, out udpResult))
                {
                    await Task.Delay(currDelay);

                    currTotalDelay += currDelay;
                    currDelay       = Math.Min(currDelay * 2, Options.MaxPollLoopInMilliseconds); // Do an exponential backup up to max (10 seconds)
                    if (currTotalDelay >= Options.MaxWaitInMilliseconds)
                    {
                        Log($"ERROR: Client: reply from {address} took too long (outgoing data={data})");
                        var delta = DateTime.UtcNow.Subtract(UdpStartTime).TotalSeconds;
                        udpResult = DaytimeResult.MakeFailed(SocketErrorStatus.ConnectionTimedOut, delta);
                        break;
                    }
                }
                return(udpResult);
            }
            catch (Exception ex)
            {
                Stats.NExceptions++;
                Log($"ERROR: Client: Writing {data} to {address} exception {ex.Message}");
                var delta = DateTime.UtcNow.Subtract(UdpStartTime).TotalSeconds;
                return(DaytimeResult.MakeFailed(ex, delta));
            }
        }
        private async Task <DaytimeResult> WriteTcpAsync(HostName address, string service, string data)
        {
            var startTime = DateTime.UtcNow;

            try
            {
                var tcpSocket = new StreamSocket();
                await tcpSocket.ConnectAsync(address, service);

                // Everything that's sent will be ignored.
                if (!string.IsNullOrEmpty(data))
                {
                    var dw = new DataWriter(tcpSocket.OutputStream);
                    dw.WriteString(data);
                    await dw.StoreAsync();
                }
                Stats.NWrites++;

                // Now read everything
                var s      = tcpSocket.InputStream;
                var buffer = new Windows.Storage.Streams.Buffer(2048);

                string stringresult = "";
                var    keepGoing    = true;
                while (keepGoing)
                {
                    try
                    {
                        var read = s.ReadAsync(buffer, buffer.Capacity, InputStreamOptions.Partial);

                        /* This is the syntax that the editor will suggest. There's a
                         * much simpler syntax (below) that's syntactic sugar over this.
                         * read.Progress = new AsyncOperationProgressHandler<IBuffer, uint>(
                         *  (operation, progress) =>
                         *  {
                         *      var err = operation.ErrorCode == null ? "null" : operation.ErrorCode.ToString();
                         *      Log(ClientOptions.Verbosity.Verbose, $"Daytime Progress count={progress} status={operation.Status} errorcode={err}");
                         *  });
                         */
                        read.Progress = (operation, progress) =>
                        {
                            var err = operation.ErrorCode == null ? "null" : operation.ErrorCode.ToString();
                            Log(ClientOptions.Verbosity.Verbose, $"Daytime Progress count={progress} status={operation.Status} errorcode={err}");
                        };
                        var result = await read;
                        if (result.Length != 0)
                        {
                            var partialresult = BufferToString.ToString(result);
                            stringresult += partialresult;
                            Log($"{stringresult}"); // This will be printed on the user's screen.
                        }
                        else
                        {
                            keepGoing = false;
                            Log(ClientOptions.Verbosity.Verbose, $"Read completed with zero bytes; closing");
                        }
                    }
                    catch (Exception ex2)
                    {
                        keepGoing = false;
                        Stats.NExceptions++;
                        Log($"EXCEPTION while reading: {ex2.Message} {ex2.HResult:X}");

                        var faildelta = DateTime.UtcNow.Subtract(startTime).TotalSeconds;
                        return(DaytimeResult.MakeFailed(ex2, faildelta));
                    }
                }

                var delta = DateTime.UtcNow.Subtract(startTime).TotalSeconds;
                return(DaytimeResult.MakeSucceeded(stringresult, delta));
            }
            catch (Exception ex)
            {
                Stats.NExceptions++;
                Log($"ERROR: Client: Writing {data} to {address} exception {ex.Message}");
                var delta = DateTime.UtcNow.Subtract(startTime).TotalSeconds;
                return(DaytimeResult.MakeFailed(ex, delta));
            }
        }