/// <summary>
        ///   Attempts to add an event to the batch, ensuring that the size
        ///   of the batch does not exceed its maximum.
        /// </summary>
        ///
        /// <param name="eventData">The event to attempt to add to the batch.</param>
        ///
        /// <returns><c>true</c> if the event was added; otherwise, <c>false</c>.</returns>
        ///
        public override bool TryAdd(EventData eventData)
        {
            Argument.AssertNotNull(eventData, nameof(eventData));
            Argument.AssertNotDisposed(_disposed, nameof(EventDataBatch));

            AmqpMessage eventMessage = MessageConverter.CreateMessageFromEvent(eventData, Options.PartitionKey);

            try
            {
                // Calculate the size for the event, based on the AMQP message size and accounting for a
                // bit of reserved overhead size.

                var size = _sizeBytes
                           + eventMessage.SerializedMessageSize
                           + (eventMessage.SerializedMessageSize <= MaximumBytesSmallMessage
                        ? OverheadBytesSmallMessage
                        : OverheadBytesLargeMessage);

                if (size > MaximumSizeInBytes)
                {
                    return(false);
                }

                _sizeBytes = size;
                BatchEvents.Add(eventData);

                return(true);
            }
            finally
            {
                eventMessage?.Dispose();
            }
        }
        /// <summary>
        ///   Attempts to add an event to the batch, ensuring that the size
        ///   of the batch does not exceed its maximum.
        /// </summary>
        ///
        /// <param name="eventData">The event to attempt to add to the batch.</param>
        ///
        /// <returns><c>true</c> if the event was added; otherwise, <c>false</c>.</returns>
        ///
        public override bool TryAdd(EventData eventData)
        {
            Guard.ArgumentNotNull(nameof(eventData), eventData);
            GuardDisposed();

            var eventMessage = MessageConverter.CreateMessageFromEvent(eventData, Options.PartitionKey);

            try
            {
                // Calculate the size for the event, based on the AMQP message size and accounting for a
                // bit of reserved overhead size.

                var size = _sizeBytes
                           + eventMessage.SerializedMessageSize
                           + (eventMessage.SerializedMessageSize <= MaximumBytesSmallMessage
                        ? OverheadBytesSmallMessage
                        : OverheadBytesLargeMessage);

                if (size > MaximumSizeInBytes)
                {
                    eventMessage.Dispose();
                    return(false);
                }

                _sizeBytes = size;
                BatchMessages.Add(eventMessage);
                return(true);
            }
            catch
            {
                eventMessage?.Dispose();
                throw;
            }
        }
        /// <summary>
        ///   Sends a set of events to the associated Event Hub using a batched approach.  If the size of events exceed the
        ///   maximum size of a single batch, an exception will be triggered and the send will fail.
        /// </summary>
        ///
        /// <param name="events">The set of event data to send.</param>
        /// <param name="sendOptions">The set of options to consider when sending this batch.</param>
        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request to cancel the operation.</param>
        ///
        public override async Task SendAsync(IReadOnlyCollection <EventData> events,
                                             SendEventOptions sendOptions,
                                             CancellationToken cancellationToken)
        {
            Argument.AssertNotNull(events, nameof(events));
            Argument.AssertNotClosed(_closed, nameof(AmqpProducer));
            Argument.AssertNotClosed(ConnectionScope.IsDisposed, nameof(EventHubConnection));

            var partitionKey = sendOptions?.PartitionKey;
            var messages     = new AmqpMessage[events.Count];
            var index        = 0;

            foreach (var eventData in events)
            {
                messages[index] = MessageConverter.CreateMessageFromEvent(eventData, partitionKey);
                ++index;
            }

            try
            {
                await SendAsync(messages, partitionKey, cancellationToken).ConfigureAwait(false);
            }
            finally
            {
                foreach (var message in messages)
                {
                    message.Dispose();
                }
            }
        }
        /// <summary>
        ///   Attempts to add an event to the batch, ensuring that the size
        ///   of the batch does not exceed its maximum.
        /// </summary>
        ///
        /// <param name="eventData">The event to attempt to add to the batch.</param>
        ///
        /// <returns><c>true</c> if the event was added; otherwise, <c>false</c>.</returns>
        ///
        public override bool TryAdd(EventData eventData)
        {
            Argument.AssertNotNull(eventData, nameof(eventData));
            Argument.AssertNotDisposed(_disposed, nameof(EventDataBatch));

            // Reserve space for producer-owned fields that correspond to special
            // features, if enabled.

            if ((ActiveFeatures & TransportProducerFeatures.IdempotentPublishing) != 0)
            {
                eventData.PendingPublishSequenceNumber = int.MaxValue;
                eventData.PendingProducerGroupId       = long.MaxValue;
                eventData.PendingProducerOwnerLevel    = short.MaxValue;
            }

            try
            {
                using var eventMessage = MessageConverter.CreateMessageFromEvent(eventData, Options.PartitionKey);

                // Calculate the size for the event, based on the AMQP message size and accounting for a
                // bit of reserved overhead size.

                var size = _sizeBytes
                           + eventMessage.SerializedMessageSize
                           + (eventMessage.SerializedMessageSize <= MaximumBytesSmallMessage
                        ? OverheadBytesSmallMessage
                        : OverheadBytesLargeMessage);

                if (size > MaximumSizeInBytes)
                {
                    return(false);
                }

                _sizeBytes = size;
                BatchEvents.Add(eventData);

                return(true);
            }
            finally
            {
                eventData.ClearPublishingState();
            }
        }
            /// <summary>
            ///   Represents the batch as a set of the AMQP-specific representations of an event.
            /// </summary>
            ///
            /// <typeparam name="T">The transport-specific event representation being requested.</typeparam>
            ///
            /// <returns>The set of events as an enumerable of the requested type.</returns>
            ///
            public override IReadOnlyCollection <T> AsReadOnlyCollection <T>()
            {
                if (typeof(T) != typeof(AmqpMessage))
                {
                    throw new FormatException(string.Format(CultureInfo.CurrentCulture, Resources.UnsupportedTransportEventType, typeof(T).Name));
                }

                // The AMQP messages must be recreated for each transform request to
                // ensure that the idempotent publishing properties are current and correct.
                //
                // This is a safe pattern, because the method is internal and only invoked
                // during a send operation, during which the batch is locked to prevent
                // changes or parallel attempts to send.
                //
                // Multiple requests to produce the collection would happen if this batch instance
                // is being published more than once, which is only valid if a call to SendAsync fails
                // across all retries, making it a fairly rare occurrence.

                if (_batchMessages != null)
                {
                    foreach (var message in _batchMessages)
                    {
                        message.Dispose();
                    }
                }

                _batchMessages = new(_backingStore.Count);

                // Serialize the events in the batch into their AMQP transport format.  Because
                // the batch holds responsibility for disposing these, hold onto the references.

                foreach (var eventData in _backingStore)
                {
                    _batchMessages.Add(MessageConverter.CreateMessageFromEvent(eventData));
                }

                return(_batchMessages as IReadOnlyCollection <T>);
            }