/// <summary>
        /// Parses the passed command line arguments and returns the result
        /// in a CommandLineOptions object.
        /// </summary>
        /// <param name="args">Array of command line arguments</param>
        /// <param name="list">Array of command line arguments</param>
        /// <returns>Object containing the parsed command line</returns>
        public static CommandLineOptions Parse(string[] args, params string[] singleOptionList)
        {
            if (args == null)
                throw new ArgumentNullException("args");

            CommandLineOptions cmdOptions = new CommandLineOptions();
            int index = 0;

            if (index < args.Length)
            {
                string token = NextToken(args, ref index);
                while (!string.IsNullOrEmpty(token))
                {
                    if (IsArgument(token))
                    {
                        string arg = token.TrimStart(OptionStartWithChars).TrimEnd(OptionEqualChar);
                        string value = string.Empty;

                        if (arg.Contains(OptionEqualChar))
                        {
                            // arg was specified with an '=' sign, so we need
                            // to split the string into the arg and value, but only
                            // if there is no space between the '=' and the arg and value.
                            string[] r = arg.Split(new char[] { OptionEqualChar }, 2);
                            if (r.Length == 2)
                            {
                                arg = r[0];
                                value = r[1];
                            }
                        }

                        // single option do not need a following parameter
                        bool isSingleOption = false;
                        if (singleOptionList != null)
                        {
                            for (int i = 0; i < singleOptionList.Length; i++)
                            {
                                if (arg == singleOptionList[i])
                                {
                                    isSingleOption = true;
                                    break;
                                }
                            }
                        }

                        // find following parameter
                        while (!isSingleOption && string.IsNullOrEmpty(value))
                        {
                            index++;
                            if (index < args.Length)
                            {
                                string next = NextToken(args, ref index);
                                if (!string.IsNullOrEmpty(next))
                                {
                                    if (IsArgument(next))
                                    {
                                        // push the token back onto the stack so
                                        // it gets picked up on next pass as an arg
                                        index--;
                                        value = MagicOptionValue;
                                        break;
                                    }
                                    else if (next != OptionEqualChar.ToString())
                                    {
                                        // save the value (trimming any '=' from the start)
                                        value = next.TrimStart(OptionEqualChar);
                                    }
                                }
                            }
                            else
                            {
                                index--;
                                value = MagicOptionValue;
                                break;
                            }
                        }

                        // save the pair
                        if (cmdOptions.Arguments.ContainsKey(arg))
                            throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                              "Option used in invalid context -- {0}", "option with the same argument."));

                        if (value == MagicOptionValue)
                        {
                            cmdOptions.Arguments.Add(arg, string.Empty);
                        }
                        else
                        {
                            cmdOptions.Arguments.Add(arg, value.TrimStart('\'').TrimEnd('\''));
                        }
                    }
                    else
                    {
                        // save stand-alone parameter
                        if (cmdOptions.Parameters.Contains(token))
                            throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                              "Option used in invalid context -- {0}", "option with the same argument."));

                        cmdOptions.Parameters.Add(token.TrimStart('\'').TrimEnd('\''));
                    }

                    index++;
                    if (index < args.Length)
                    {
                        token = NextToken(args, ref index);
                    }
                    else
                    {
                        break;
                    }
                }
            }

            return cmdOptions;
        }
        private static TcpLikaCommandLineOptions ParseOptions(CommandLineOptions commandLineOptions)
        {
            if (commandLineOptions == null)
                throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                  "Option used in invalid context -- {0}", "must specify a <host:port>."));

            var options = new TcpLikaCommandLineOptions();

            if (commandLineOptions.Arguments.Any())
            {
                foreach (var arg in commandLineOptions.Arguments.Keys)
                {
                    var optionType = TcpLikaOptions.GetOptionType(arg);
                    if (optionType == TcpLikaOptionType.None)
                        throw new CommandLineException(
                          string.Format(CultureInfo.CurrentCulture, "Option used in invalid context -- {0}",
                          string.Format(CultureInfo.CurrentCulture, "cannot parse the command line argument : [{0}].", arg)));

                    switch (optionType)
                    {
                        case TcpLikaOptionType.Threads:
                            {
                                options.IsSetThreads = true;
                                int threads;
                                if (!int.TryParse(commandLineOptions.Arguments[arg], out threads))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of threads option -- {0}.", commandLineOptions.Arguments[arg]));
                                if (threads < 1)
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of threads option -- {0}.", commandLineOptions.Arguments[arg]));
                                options.Threads = threads;
                            }
                            break;
                        case TcpLikaOptionType.Nagle:
                            {
                                options.IsSetNagle = true;
                                var nagle = commandLineOptions.Arguments[arg].ToString().ToUpperInvariant();
                                if (nagle != "ON" && nagle != "OFF")
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of nagle option (ON|OFF) -- {0}.", commandLineOptions.Arguments[arg]));
                                options.Nagle = nagle == "ON";
                            }
                            break;
                        case TcpLikaOptionType.ReceiveBufferSize:
                            {
                                options.IsSetReceiveBufferSize = true;
                                int bufferSize;
                                if (!int.TryParse(commandLineOptions.Arguments[arg], out bufferSize))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of receive buffer size option -- {0}.", commandLineOptions.Arguments[arg]));
                                if (bufferSize < 1)
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of receive buffer size option -- {0}.", commandLineOptions.Arguments[arg]));
                                options.ReceiveBufferSize = bufferSize;
                            }
                            break;
                        case TcpLikaOptionType.SendBufferSize:
                            {
                                options.IsSetSendBufferSize = true;
                                int bufferSize;
                                if (!int.TryParse(commandLineOptions.Arguments[arg], out bufferSize))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of send buffer size option -- {0}.", commandLineOptions.Arguments[arg]));
                                if (bufferSize < 1)
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of send buffer size option -- {0}.", commandLineOptions.Arguments[arg]));
                                options.SendBufferSize = bufferSize;
                            }
                            break;
                        case TcpLikaOptionType.Connections:
                            {
                                options.IsSetConnections = true;
                                int connections;
                                if (!int.TryParse(commandLineOptions.Arguments[arg], out connections))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of connections option -- {0}.", commandLineOptions.Arguments[arg]));
                                if (connections < 1)
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of connections option -- {0}.", commandLineOptions.Arguments[arg]));
                                options.Connections = connections;
                            }
                            break;
                        case TcpLikaOptionType.ConnectionLifetime:
                            {
                                options.IsSetChannelLifetime = true;
                                int milliseconds;
                                if (!int.TryParse(commandLineOptions.Arguments[arg], out milliseconds))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of channel lifetime [milliseconds] option -- {0}.", commandLineOptions.Arguments[arg]));
                                if (milliseconds < 1)
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid formats of channel lifetime [milliseconds] option -- {0}.", commandLineOptions.Arguments[arg]));
                                options.ChannelLifetime = TimeSpan.FromMilliseconds(milliseconds);
                            }
                            break;
                        case TcpLikaOptionType.WebSocket:
                            options.IsSetWebSocket = true;
                            break;
                        case TcpLikaOptionType.WebSocketPath:
                            {
                                options.IsSetWebSocketPath = true;
                                options.WebSocketPath = commandLineOptions.Arguments[arg].Trim();
                                if (string.IsNullOrEmpty(options.WebSocketPath))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid value of WebSocketPath option -- {0}.", commandLineOptions.Arguments[arg]));
                                options.WebSocketPath = "/" + options.WebSocketPath.TrimStart('/');
                            }
                            break;
                        case TcpLikaOptionType.WebSocketProtocol:
                            {
                                options.IsSetWebSocketProtocol = true;
                                options.WebSocketProtocol = commandLineOptions.Arguments[arg];
                                if (string.IsNullOrEmpty(options.WebSocketProtocol))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid value of WebSocketProtocol option -- {0}.", commandLineOptions.Arguments[arg]));
                            }
                            break;
                        case TcpLikaOptionType.Ssl:
                            options.IsSetSsl = true;
                            break;
                        case TcpLikaOptionType.SslTargetHost:
                            {
                                options.IsSetSslTargetHost = true;
                                options.SslTargetHost = commandLineOptions.Arguments[arg];
                                if (string.IsNullOrEmpty(options.SslTargetHost))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid value of SslTargetHost option -- {0}.", commandLineOptions.Arguments[arg]));
                            }
                            break;
                        case TcpLikaOptionType.SslClientCertificateFilePath:
                            {
                                options.IsSetSslClientCertificateFilePath = true;
                                options.SslClientCertificateFilePath = commandLineOptions.Arguments[arg];
                                if (string.IsNullOrEmpty(options.SslClientCertificateFilePath))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid value of SslClientCertificateFilePath option -- {0}.", commandLineOptions.Arguments[arg]));
                                if (!File.Exists(options.SslClientCertificateFilePath))
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid value of SslClientCertificateFilePath option -- {0} does not exist.", commandLineOptions.Arguments[arg]));

                                try
                                {
                                    options.SslClientCertificates.Add(new X509Certificate2(options.SslClientCertificateFilePath));
                                }
                                catch (CryptographicException ex)
                                {
                                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                        "Invalid value of SslClientCertificateFilePath option -- {0}.", ex.Message), ex);
                                }
                            }
                            break;
                        case TcpLikaOptionType.SslBypassedErrors:
                            options.IsSetSslPolicyErrorsBypassed = true;
                            break;
                        case TcpLikaOptionType.Help:
                            options.IsSetHelp = true;
                            break;
                        case TcpLikaOptionType.Version:
                            options.IsSetVersion = true;
                            break;
                    }
                }
            }

            if (commandLineOptions.Parameters.Any())
            {
                try
                {
                    foreach (var item in commandLineOptions.Parameters)
                    {
                        var splits = item.Split(':');
                        if (splits.Length < 2)
                            throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                                "{0} is not well formatted as <host:port>.", item));

                        var host = IPAddress.Parse(splits[0]);
                        var port = int.Parse(splits[1]);
                        var endpoint = new IPEndPoint(host, port);
                        options.RemoteEndPoints.Add(endpoint);
                    }
                }
                catch (Exception ex)
                {
                    throw new CommandLineException(string.Format(CultureInfo.CurrentCulture,
                        "Invalid formats of endpoints -- {0}", ex.Message), ex);
                }
            }

            return options;
        }