/**
   * Calculates the acknowledgement Message for a given message. <br>
   * <br>
   * The process for this is as follows:<br>
   * <br>
   * 1) initialPayload = time || stream number || 32 bytes of random data<br>
   * <br>
   * 2) Do POW for the initialPayload<br>
   * <br>
   * 3) ackData = POWnonce || msgHeader || initialPayload<br>
   * <br>
   *
   * @param message - The original plain text Message object, provided so that its status can be
   *     updated during the process
   * @param ackData - A byte[] containing the 32 bytes of random data which is the acknowledgment
   *     data
   * @param toStreamNumber - An int representing the stream number of the destination address of the
   *     message to be sent
   * @param doPOW - A boolean indicating whether or not POW should be done for ack msgs generated
   *     during this process
   * @param timeToLive - The 'time to live' value (in seconds) to be used in processing this message
   * @return A byte[] containing the acknowledgement data for the message we wish to send
   */
  private byte[] generateFullAckMessage(
      Message message, byte[] ackData, int toStreamNumber, boolean doPOW, long timeToLive) {
    // Get the fuzzed expiration time
    long expirationTime = TimeUtils.getFuzzedExpirationTime(timeToLive);

    // Encode the expiration time, object type, object version, and stream number values into byte
    // form
    byte[] expirationTimeBytes = ByteUtils.longToBytes((expirationTime));
    byte[] objectTypeBytes = ByteUtils.intToBytes(OBJECT_TYPE_MSG);
    byte[] objectVersionBytes = VarintEncoder.encode(OBJECT_VERSION_MSG);
    byte[] streamNumberBytes = VarintEncoder.encode((long) toStreamNumber);

    // Combine the time, object type, object version, stream number, and ack data values into a
    // single byte[]
    byte[] initialPayload =
        ByteUtils.concatenateByteArrays(
            expirationTimeBytes, objectTypeBytes, objectVersionBytes, streamNumberBytes, ackData);

    // Create the payload for the ack msg
    byte[] payload = new byte[0];
    if (doPOW) {
      // Update the status of this message displayed in the UI
      String messageStatus = App.getContext().getString(R.string.message_status_doing_ack_pow);
      MessageStatusHandler.updateMessageStatus(message, messageStatus);

      // Do proof of work for the acknowledgement payload
      Log.i(
          TAG,
          "About to do POW calculations for the acknowledgment payload of a msg that we are sending");
      long powNonce =
          new POWProcessor()
              .doPOW(
                  initialPayload,
                  expirationTime,
                  POWProcessor.NETWORK_NONCE_TRIALS_PER_BYTE,
                  POWProcessor.NETWORK_EXTRA_BYTES);

      byte[] powNonceBytes = ByteUtils.longToBytes(powNonce);

      payload = ByteUtils.concatenateByteArrays(powNonceBytes, initialPayload);
    } else {
      payload = initialPayload;
    }

    byte[] headerData = new MessageProcessor().generateObjectHeader(payload);
    byte[] fullAckMsg = ByteUtils.concatenateByteArrays(headerData, payload);

    return fullAckMsg;
  }