/**
   * This blocking function attempts to send the message passed in as a parameter. This {@link
   * AmqpsIotHubConnection} handles all calls to this method.
   *
   * <p>Only the first call to this method will result in an attempt to send. Until the message has
   * been sent, all other calls to this method will block. Once a message has been sent and this
   * method notified that it has been sent, this method will be invoked again if was previously
   * another call to send a message.
   *
   * <p>If a message has been passed down to the handler for sending but the message isn't sent
   * after a default constant number of seconds, the {@link AmqpsTransport} will set an ERROR status
   * code on the message and it will placed back onto the queue.
   *
   * @throws IOException If {@link AmqpsIotHubConnectionBaseHandler} has not been initialized.
   */
  protected synchronized void send(Tuple<CompletableFuture<Boolean>, byte[], Object> message)
      throws IOException {
    if (this.state == ReactorState.CLOSED) {
      throw new IllegalStateException(
          "The AMQPS IotHub Connection is currently closed. Call open() before attempting to send a message.");
    }
    if (message != null) {
      if (this.inProgressMessageMap.size() >= this.maxQueueSize * 0.9) {
        message.V1.completeExceptionally(
            new Throwable("Insufficient link credit to send message."));
      } else {
        try {
          // Use the content and ID fields of the input message to have the handler create and send
          // the message
          CompletableFuture<Integer> deliveryFuture =
              amqpsHandler.createBinaryMessage((byte[]) message.V2, message.V3);

          // Wait for a period of time before rejecting the message
          new Thread(
                  () -> {
                    try {
                      Thread.sleep(DEFAULT_DELIVERY_WAIT_TIME_SECONDS * 1000);
                      deliveryFuture.completeExceptionally(
                          new Throwable("Default timeout exceeded before this message was sent."));
                    } catch (InterruptedException e) {
                      e.printStackTrace();
                    }
                  })
              .start();

          // Wait for the deliveryFuture to be completed, providing the delivery hash code.
          // When this future completes, the message has been SENT
          Integer deliveryHash = deliveryFuture.get();
          inProgressMessageMap.put(deliveryHash, message);
        } catch (InterruptedException e) {
          e.printStackTrace();
        }
        // The message was unable to be sent, exceptionally complete that future causing the message
        // to be put back on the queue.
        catch (ExecutionException e) {
          message.V1.completeExceptionally(e.getCause());
          this.fail(e.getCause());
        }
        // There was some other problem sending, exceptionally complete that future causing the
        // message to be put back on the queue.
        catch (Exception e) {
          if (message != null) {
            message.V1.completeExceptionally(e);
          }
          this.fail(e);
        }
      }
    } else {
      throw new IOException("Cannot send an unitialized message.");
    }
  }