public static T Parse <T>(string message, IUserInterface userInterface = null, bool throwJsonException = true) where T : CMakeMessage
        {
            try
            {
                CMakeMessage cMakeMessage;
                JObject      content = JObject.Parse(message, new JsonLoadSettings {
                    LineInfoHandling = LineInfoHandling.Ignore
                });
                userInterface?.WriteVerbose(content.ToString(Formatting.Indented));
                switch (content["type"].Value <string>())
                {
                case "hello":
                    cMakeMessage = CMakeHelloMessage.Create(content);
                    break;

                case "reply":
                    cMakeMessage = CMakeReplyMessage.Create(content);
                    break;

                case "progress":
                    cMakeMessage = CMakeProgressMessage.Create();
                    break;

                case "message":
                    cMakeMessage = CMakeMessageMessage.Create(content);
                    break;

                case "signal":
                    userInterface?.WriteVerbose("Received signal message from cmake server. Signal messages are ignored.");
                    return(null);

                default:
                    throw new FormattableException($"Unknown message type {content["type"].Value<string>()}.{Environment.NewLine}" +
                                                   $"Complete message: {message}");
                }

                if (cMakeMessage is T converted)
                {
                    return(converted);
                }
                throw new FormattableException($"Expected message of type {typeof(T)}, but message was of type  {cMakeMessage.GetType()}");
            }
            catch (JsonReaderException e)
            {
                if (throwJsonException)
                {
                    throw new FormattableException($"Error while parsing the response json{Environment.NewLine}{message}.", e);
                }

                return(null);
            }
        }
        private async Task <CMakeReplyMessage> WaitForReply(string type)
        {
            CMakeProgressMessage progressMessage = null;
            CMakeMessageMessage  messageMessage  = null;
            CMakeMessage         message         = null;

            do
            {
                foreach (string singleMessage in await serverStream.ReadMessage()
                         .TimeoutAfter(CMakeServerTimeout)
                         .ConfigureAwait(false))
                {
                    //Expect at least a progress message after default timeout
                    message = ParseMessage(singleMessage);
                    if (message is CMakeReplyMessage replyMessage)
                    {
                        CheckCookieAndType(replyMessage);
                        return(replyMessage);
                    }
                }
            } while (progressMessage != null ||
                     messageMessage != null ||
                     message == null);

            throw new InvalidOperationException("This is not possible.");

            void CheckCookieAndType(CMakeBaseResponseMessage responseMessage)
            {
                if (!string.IsNullOrEmpty(cookie) &&
                    responseMessage.Cookie != cookie)
                {
                    throw new FormattableException($"Expected a message with the cookie {cookie}, " +
                                                   $"but got a message with the cookie {responseMessage.Cookie}. " +
                                                   "Someone else is taking to the server unexpectedly.");
                }

                if (responseMessage.ResponseType != type)
                {
                    throw new FormattableException($"Expected a message in response to {type}, " +
                                                   $"but got a message in response to {responseMessage.ResponseType}. " +
                                                   "Someone else is taking to the server unexpectedly " +
                                                   "or the server is confusing.");
                }
            }

            CMakeMessage ParseMessage(string singleMessage)
            {
                CMakeMessage parsedMessage = CMakeMessage.Parse <CMakeMessage>(singleMessage, userInterface);

                progressMessage = parsedMessage as CMakeProgressMessage;
                messageMessage  = parsedMessage as CMakeMessageMessage;
                if (messageMessage != null)
                {
                    CheckCookieAndType(messageMessage);
                    if (messageMessage.Title.Contains("error", StringComparison.OrdinalIgnoreCase))
                    {
                        throw new FormattableException($"CMake discovered an error.{Environment.NewLine}" +
                                                       $"{messageMessage.Title}{Environment.NewLine}" +
                                                       $"==================================={Environment.NewLine}" +
                                                       $"{messageMessage.Message}");
                    }
                }

                return(parsedMessage);
            }
        }