/// <summary>
        ///   Initializes a new instance of the <see cref="EventHubConnection"/> class.
        /// </summary>
        ///
        /// <param name="connectionString">The connection string to use for connecting to the Event Hubs namespace; it is expected that the shared key properties are contained in this connection string, but not the Event Hub name.</param>
        /// <param name="eventHubName">The name of the specific Event Hub to associate the connection with.</param>
        /// <param name="connectionOptions">A set of options to apply when configuring the connection.</param>
        ///
        /// <remarks>
        ///   If the connection string is copied from the Event Hub itself, it will contain the name of the desired Event Hub,
        ///   and can be used directly without passing the <paramref name="eventHubName" />.  The name of the Event Hub should be
        ///   passed only once, either as part of the connection string or separately.
        /// </remarks>
        ///
        public EventHubConnection(string connectionString,
                                  string eventHubName,
                                  EventHubConnectionOptions connectionOptions)
        {
            Argument.AssertNotNullOrEmpty(connectionString, nameof(connectionString));

            connectionOptions = connectionOptions?.Clone() ?? new EventHubConnectionOptions();
            ValidateConnectionOptions(connectionOptions);

            var connectionStringProperties = EventHubsConnectionStringProperties.Parse(connectionString);

            connectionStringProperties.Validate(eventHubName, nameof(connectionString));

            var fullyQualifiedNamespace = connectionStringProperties.FullyQualifiedNamespace;

            if (string.IsNullOrEmpty(eventHubName))
            {
                eventHubName = connectionStringProperties.EventHubName;
            }

            SharedAccessSignature sharedAccessSignature;

            if (string.IsNullOrEmpty(connectionStringProperties.SharedAccessSignature))
            {
                sharedAccessSignature = new SharedAccessSignature(
                    BuildConnectionAudience(connectionOptions.TransportType, fullyQualifiedNamespace, eventHubName),
                    connectionStringProperties.SharedAccessKeyName,
                    connectionStringProperties.SharedAccessKey);
            }
            else
            {
                sharedAccessSignature = new SharedAccessSignature(connectionStringProperties.SharedAccessSignature);
            }

            var sharedCredentials = new SharedAccessSignatureCredential(sharedAccessSignature);
            var tokenCredentials  = new EventHubTokenCredential(sharedCredentials, BuildConnectionAudience(connectionOptions.TransportType, fullyQualifiedNamespace, eventHubName));

            FullyQualifiedNamespace = fullyQualifiedNamespace;
            EventHubName            = eventHubName;
            Options = connectionOptions;

#pragma warning disable CA2214 // Do not call overridable methods in constructors. This internal method is virtual for testing purposes.
            InnerClient = CreateTransportClient(fullyQualifiedNamespace, eventHubName, tokenCredentials, connectionOptions);
#pragma warning restore CA2214 // Do not call overridable methods in constructors.
        }
        /// <summary>
        ///   Parses the specified Event Hubs connection string into its component properties.
        /// </summary>
        ///
        /// <param name="connectionString">The connection string to parse.</param>
        ///
        /// <returns>The component properties parsed from the connection string.</returns>
        ///
        /// <exception cref="FormatException">The specified connection string was malformed and could not be parsed.</exception>
        ///
        public static EventHubsConnectionStringProperties Parse(string connectionString)
        {
            Argument.AssertNotNullOrEmpty(connectionString, nameof(connectionString));

            var parsedValues          = new EventHubsConnectionStringProperties();
            var tokenPositionModifier = (connectionString[0] == TokenValuePairDelimiter) ? 0 : 1;
            var lastPosition          = 0;
            var currentPosition       = 0;

            int    valueStart;
            string slice;
            string token;
            string value;

            while (currentPosition != -1)
            {
                // Slice the string into the next token/value pair.

                currentPosition = connectionString.IndexOf(TokenValuePairDelimiter, lastPosition + 1);

                if (currentPosition >= 0)
                {
                    slice = connectionString.Substring(lastPosition, (currentPosition - lastPosition));
                }
                else
                {
                    slice = connectionString.Substring(lastPosition);
                }

                // Break the token and value apart, if this is a legal pair.

                valueStart = slice.IndexOf(TokenValueSeparator);

                if (valueStart >= 0)
                {
                    token = slice.Substring((1 - tokenPositionModifier), (valueStart - 1 + tokenPositionModifier));
                    value = slice.Substring(valueStart + 1);

                    // Guard against leading and trailing spaces, only trimming if there is a need.

                    if ((!string.IsNullOrEmpty(token)) && (char.IsWhiteSpace(token[0])) || char.IsWhiteSpace(token[token.Length - 1]))
                    {
                        token = token.Trim();
                    }

                    if ((!string.IsNullOrEmpty(value)) && (char.IsWhiteSpace(value[0]) || char.IsWhiteSpace(value[value.Length - 1])))
                    {
                        value = value.Trim();
                    }

                    // If there was no value for a key, then consider the connection string to
                    // be malformed.

                    if (string.IsNullOrEmpty(value))
                    {
                        throw new FormatException(Resources.InvalidConnectionString);
                    }

                    // Compare the token against the known connection string properties and capture the
                    // pair if they are a known attribute.

                    if (string.Compare(EndpointToken, token, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        var endpointBuilder = new UriBuilder(value)
                        {
                            Scheme = EventHubsEndpointScheme,
                            Port   = -1
                        };

                        if ((string.Compare(endpointBuilder.Scheme, EventHubsEndpointSchemeName, StringComparison.OrdinalIgnoreCase) != 0) ||
                            (Uri.CheckHostName(endpointBuilder.Host) == UriHostNameType.Unknown))
                        {
                            throw new FormatException(Resources.InvalidConnectionString);
                        }

                        parsedValues.Endpoint = endpointBuilder.Uri;
                    }
                    else if (string.Compare(EventHubNameToken, token, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        parsedValues.EventHubName = value;
                    }
                    else if (string.Compare(SharedAccessKeyNameToken, token, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        parsedValues.SharedAccessKeyName = value;
                    }
                    else if (string.Compare(SharedAccessKeyValueToken, token, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        parsedValues.SharedAccessKey = value;
                    }
                    else if (string.Compare(SharedAccessSignatureToken, token, StringComparison.OrdinalIgnoreCase) == 0)
                    {
                        parsedValues.SharedAccessSignature = value;
                    }
                }
                else if ((slice.Length != 1) || (slice[0] != TokenValuePairDelimiter))
                {
                    // This wasn't a legal pair and it is not simply a trailing delimiter; consider
                    // the connection string to be malformed.

                    throw new FormatException(Resources.InvalidConnectionString);
                }

                tokenPositionModifier = 0;
                lastPosition          = currentPosition;
            }

            return(parsedValues);
        }