/// <remarks>Runs <see cref="Keybase.API.Environment.EnsureInitialization"/>.</remarks>
            public static void Listen([NotNull] IListener listener)
            {
                Process process;

                if (null == (process = CreateProcess(kListenArguments)))
                {
                    listener.OnError();

                    return;
                }

                process.OutputDataReceived += (sender, arguments) =>
                {
                    if (null == process || null == arguments.Data)
                    {
                        return;
                    }

                    Log.Debug(typeof(Chat), Log.Type.Message, "Incoming: {0}", arguments.Data);

                    Incoming incoming = JsonSerializer.Deserialize <Incoming> (
                        arguments.Data,
                        StandardResolver.AllowPrivateCamelCase
                        );

                    Incoming.Content.Type type = incoming.GetContentType();
                    switch (type)
                    {
                    case Incoming.Content.Type.Text:
                        if (!incoming.MarkReceipt())
                        {
                            Log.Error("API.Chat.Listen MarkReceipt failed. Incoming contained no message structure?");
                            return;
                        }

                        Message message = incoming.ToMessage();

                        if (message.Valid)
                        {
#if DEBUG_API_LISTEN
                            Log.Message("API.Chat.Listen invoking OnIncoming: {0}", message);
#endif

                            listener.OnIncoming(message);
                        }
                        else
                        {
                            Log.Error("API.Chat.Listen failed to log an incoming message. Keeping busy?");
                        }
                        break;

                    case Incoming.Content.Type.Reaction:
                        if (!incoming.MarkReceipt())
                        {
                            Log.Error("API.Chat.Listen MarkReceipt failed. Incoming contained no message structure?");
                            return;
                        }

                        Reaction reaction = incoming.ToReaction();

                        if (reaction.Valid)
                        {
#if DEBUG_API_LISTEN
                            Log.Message("API.Chat.Listen invoking OnReaction: {0}", reaction);
#endif

                            listener.OnReaction(reaction);
                        }
                        else
                        {
                            Log.Error("API.Chat.Listen failed to log an incoming reaction. Keeping busy?");
                        }
                        break;

                    case Incoming.Content.Type.Delete:
                        DeletePolicy policy = listener.DeletePolicy;

                        using (PooledList <Message.ID> targets = incoming.ToDeleteList())
                        {
                            if (null == targets)
                            {
                                Log.Error("API.Chat.Listen received delete with an invalid targets list");

                                return;
                            }

#if DEBUG_API_LISTEN
                            Log.Message("API.Chat.Listen handling delete of {0} messages", targets.Value.Count);
#endif

                            if (policy.HasFlag(DeletePolicy.Callback))
                            {
                                foreach (Message.ID target in targets.Value)
                                {
                                    listener.OnDelete(target);
                                }
                            }

                            if (policy.HasFlag(DeletePolicy.Remove))
                            {
                                foreach (Message.ID target in targets.Value)
                                {
                                    TryRemoveFromLog(target);
                                }
                            }
                        }
                        break;

                    case Incoming.Content.Type.Unknown:
                        Log.Error("API.Chat.Listen received unknown content type. Incomplete API spec? Data:\n{0}", arguments.Data);
                        return;

                    default:
                        Log.Warning("API.Chat.Listen received unhandled content type: {0}", type);
                        break;
                    }
                };

                bool receivedStartMessage = false;

                process.ErrorDataReceived += (sender, arguments) =>
                {
                    if (null == process || null == arguments.Data)
                    {
                        return;
                    }

                    if (!receivedStartMessage && arguments.Data.StartsWith(kListenOutputStart))
                    {
                        receivedStartMessage = true;
                        return;
                    }

                    Log.Error("API.Chat.Listen process received error output: {0}", arguments.Data);
                };

                process.Exited += (sender, arguments) =>
                {
                    if (null == process)
                    {
                        return;
                    }

                    Log.Debug(typeof(Chat), Log.Type.Message, "API.Chat.Listen process exit");

                    listener.OnError();

                    process.EnableRaisingEvents = false;
                    process.Dispose();
                    process = null;
                };

                if (!process.Start())
                {
                    Log.Error("API.Chat.Listen process failed to start");

                    listener.OnError();

                    return;
                }

                process.BeginOutputReadLine();
                process.BeginErrorReadLine();
            }