/**
  * Serializes the supplied subscribe data into the supplied buffer, ready for sending
  * @param pTxBuf the buffer into which the packet will be serialized
  * @param txBufLen the length in bytes of the supplied buffer
  * @param dup unsigned char - the MQTT dup flag
  * @param packetId uint16_t - the MQTT packet identifier
  * @param topicCount - number of members in the topicFilters and reqQos arrays
  * @param pTopicNameList - array of topic filter names
  * @param pTopicNameLenList - array of length of topic filter names
  * @param pRequestedQoSs - array of requested QoS
  * @param pSerializedLen - the length of the serialized data
  *
  * @return An IoT Error Type defining successful/failed operation
  */
static IoT_Error_t _aws_iot_mqtt_serialize_subscribe(unsigned char *pTxBuf, size_t txBufLen,
													 unsigned char dup, uint16_t packetId, uint32_t topicCount,
													 const char **pTopicNameList, uint16_t *pTopicNameLenList,
													 QoS *pRequestedQoSs, uint32_t *pSerializedLen) {
	unsigned char *ptr;
	uint32_t itr, rem_len;
	IoT_Error_t rc;
	MQTTHeader header = {0};

	FUNC_ENTRY;
	if(NULL == pTxBuf || NULL == pSerializedLen) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	ptr = pTxBuf;
	rem_len = 2; /* packetId */

	for(itr = 0; itr < topicCount; ++itr) {
		rem_len += (uint32_t) (pTopicNameLenList[itr] + 2 + 1); /* topic + length + req_qos */
	}

	if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(rem_len) > txBufLen) {
		FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR);
	}

	rc = aws_iot_mqtt_internal_init_header(&header, SUBSCRIBE, QOS1, dup, 0);
	if(AWS_SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}
	/* write header */
	aws_iot_mqtt_internal_write_char(&ptr, header.byte);

	/* write remaining length */
	ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, rem_len);

	aws_iot_mqtt_internal_write_uint_16(&ptr, packetId);

	for(itr = 0; itr < topicCount; ++itr) {
		aws_iot_mqtt_internal_write_utf8_string(&ptr, pTopicNameList[itr], pTopicNameLenList[itr]);
		aws_iot_mqtt_internal_write_char(&ptr, (unsigned char) pRequestedQoSs[itr]);
	}

	*pSerializedLen = (uint32_t) (ptr - pTxBuf);

	FUNC_EXIT_RC(AWS_SUCCESS);
}
/**
  * Serializes the connect options into the buffer.
  * @param buf the buffer into which the packet will be serialized
  * @param len the length in bytes of the supplied buffer
  * @param options the options to be used to build the connect packet
  * @param serialized length
  * @return IoT_Error_t indicating function execution status
  */
static IoT_Error_t _aws_iot_mqtt_serialize_connect(unsigned char *pTxBuf, size_t txBufLen,
												   IoT_Client_Connect_Params *pConnectParams,
												   size_t *pSerializedLen) {
	unsigned char *ptr;
	uint32_t len;
	IoT_Error_t rc;
	MQTTHeader header = {0};
	MQTT_Connect_Header_Flags flags = {0};

	FUNC_ENTRY;

	if(NULL == pTxBuf || NULL == pConnectParams || NULL == pSerializedLen ||
	   (NULL == pConnectParams->pClientID && 0 != pConnectParams->clientIDLen) ||
	   (NULL != pConnectParams->pClientID && 0 == pConnectParams->clientIDLen)) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	/* Check needed here before we start writing to the Tx buffer */
	switch(pConnectParams->MQTTVersion) {
		case MQTT_3_1_1:
			break;
		default:
			return MQTT_CONNACK_UNACCEPTABLE_PROTOCOL_VERSION_ERROR;
	}

	ptr = pTxBuf;
	len = _aws_iot_get_connect_packet_length(pConnectParams);
	if(aws_iot_mqtt_internal_get_final_packet_length_from_remaining_length(len) > txBufLen) {
		FUNC_EXIT_RC(MQTT_TX_BUFFER_TOO_SHORT_ERROR);
	}

	rc = aws_iot_mqtt_internal_init_header(&header, CONNECT, QOS0, 0, 0);
	if(SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	aws_iot_mqtt_internal_write_char(&ptr, header.byte); /* write header */

	ptr += aws_iot_mqtt_internal_write_len_to_buffer(ptr, len); /* write remaining length */

	// Enable if adding support for more versions
	//if(MQTT_3_1_1 == pConnectParams->MQTTVersion) {
	aws_iot_mqtt_internal_write_utf8_string(&ptr, "MQTT", 4);
	aws_iot_mqtt_internal_write_char(&ptr, (unsigned char) pConnectParams->MQTTVersion);
	//}

	flags.all = 0;
	flags.bits.cleansession = (pConnectParams->isCleanSession) ? 1 : 0;
	flags.bits.will = (pConnectParams->isWillMsgPresent) ? 1 : 0;
	if(flags.bits.will) {
		flags.bits.willQoS = pConnectParams->will.qos;
		flags.bits.willRetain = (pConnectParams->will.isRetained) ? 1 : 0;
	}

	if(pConnectParams->pUsername) {
		flags.bits.username = 1;
	}

	if(pConnectParams->pPassword) {
		flags.bits.password = 1;
	}

	aws_iot_mqtt_internal_write_char(&ptr, flags.all);
	aws_iot_mqtt_internal_write_uint_16(&ptr, pConnectParams->keepAliveIntervalInSec);

	/* If the code have passed the check for incorrect values above, no client id was passed as argument */
	if(NULL == pConnectParams->pClientID) {
		aws_iot_mqtt_internal_write_uint_16(&ptr, 0);
	} else {
		aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pClientID, pConnectParams->clientIDLen);
	}

	if(pConnectParams->isWillMsgPresent) {
		aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->will.pTopicName,
												pConnectParams->will.topicNameLen);
		aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->will.pMessage, pConnectParams->will.msgLen);
	}

	if(flags.bits.username) {
		aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pUsername, pConnectParams->usernameLen);
	}

	if(flags.bits.password) {
		aws_iot_mqtt_internal_write_utf8_string(&ptr, pConnectParams->pPassword, pConnectParams->passwordLen);
	}

	*pSerializedLen = (size_t) (ptr - pTxBuf);

	FUNC_EXIT_RC(SUCCESS);
}