/**
 * @brief MQTT Connection Function
 *
 * Called to establish an MQTT connection with the AWS IoT Service
 * This is the outer function which does the validations and calls the internal connect above
 * to perform the actual operation. It is also responsible for client state changes
 *
 * @param pClient Reference to the IoT Client
 * @param pConnectParams Pointer to MQTT connection parameters
 *
 * @return An IoT Error Type defining successful/failed connection
 */
IoT_Error_t aws_iot_mqtt_connect(AWS_IoT_Client *pClient, IoT_Client_Connect_Params *pConnectParams) {
	IoT_Error_t rc, disconRc;
	ClientState clientState;

	FUNC_ENTRY;

	if(NULL == pClient) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	clientState = aws_iot_mqtt_get_client_state(pClient);

	if(false == _aws_iot_mqtt_is_client_state_valid_for_connect(clientState)) {
		/* Don't send connect packet again if we are already connected
		 * or in the process of connecting/disconnecting */
		FUNC_EXIT_RC(NETWORK_ALREADY_CONNECTED_ERROR);
	}

	aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTING);

	rc = _aws_iot_mqtt_internal_connect(pClient, pConnectParams);

	if(SUCCESS != rc) {
		pClient->networkStack.disconnect(&(pClient->networkStack));
		disconRc = pClient->networkStack.destroy(&(pClient->networkStack));
		aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTING, CLIENT_STATE_DISCONNECTED_ERROR);
	} else {
		aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTING, CLIENT_STATE_CONNECTED_IDLE);
	}

	FUNC_EXIT_RC(rc);
}
/**
 * @brief Subscribe to an MQTT topic.
 *
 * Called to send a subscribe message to the broker requesting a subscription
 * to an MQTT topic.
 * This is the outer function which does the validations and calls the internal resubscribe above
 * to perform the actual operation. It is also responsible for client state changes
 * @note Call is blocking.  The call returns after the receipt of the SUBACK control packet.
 *
 * @param pClient Reference to the IoT Client
 *
 * @return An IoT Error Type defining successful/failed subscription
 */
IoT_Error_t aws_iot_mqtt_resubscribe(AWS_IoT_Client *pClient) {
	IoT_Error_t rc, resubRc;

	FUNC_ENTRY;

	if(NULL == pClient) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	if(false == aws_iot_mqtt_is_client_connected(pClient)) {
		FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR);
	}

	if(CLIENT_STATE_CONNECTED_IDLE != aws_iot_mqtt_get_client_state(pClient)) {
		FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR);
	}

	rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_IDLE,
									   CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS);
	if(AWS_SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	resubRc = _aws_iot_mqtt_internal_resubscribe(pClient);

	rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_RESUBSCRIBE_IN_PROGRESS,
									   CLIENT_STATE_CONNECTED_IDLE);
	if(AWS_SUCCESS == resubRc && AWS_SUCCESS != rc) {
		resubRc = rc;
	}

	FUNC_EXIT_RC(resubRc);
}
/**
 * @brief Subscribe to an MQTT topic.
 *
 * Called to send a subscribe message to the broker requesting a subscription
 * to an MQTT topic. This is the outer function which does the validations and
 * calls the internal subscribe above to perform the actual operation.
 * It is also responsible for client state changes
 * @note Call is blocking.  The call returns after the receipt of the SUBACK control packet.
 *
 * @param pClient Reference to the IoT Client
 * @param pTopicName Topic Name to publish to
 * @param topicNameLen Length of the topic name
 * @param pApplicationHandler_t Reference to the handler function for this subscription
 *
 * @return An IoT Error Type defining successful/failed subscription
 */
IoT_Error_t aws_iot_mqtt_subscribe(AWS_IoT_Client *pClient, const char *pTopicName, uint16_t topicNameLen,
								   QoS qos, pApplicationHandler_t pApplicationHandler, void *pApplicationHandlerData) {
	ClientState clientState;
	IoT_Error_t rc, subRc;

	FUNC_ENTRY;

	if(NULL == pClient || NULL == pTopicName || NULL == pApplicationHandler) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	if(!aws_iot_mqtt_is_client_connected(pClient)) {
		FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR);
	}

	clientState = aws_iot_mqtt_get_client_state(pClient);
	if(CLIENT_STATE_CONNECTED_IDLE != clientState && CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN != clientState) {
		FUNC_EXIT_RC(MQTT_CLIENT_NOT_IDLE_ERROR);
	}

	rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS);
	if(AWS_SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	subRc = _aws_iot_mqtt_internal_subscribe(pClient, pTopicName, topicNameLen, qos,
											 pApplicationHandler, pApplicationHandlerData);

	rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_SUBSCRIBE_IN_PROGRESS, clientState);
	if(AWS_SUCCESS == subRc && AWS_SUCCESS != rc) {
		subRc = rc;
	}

	FUNC_EXIT_RC(subRc);
}
/**
 * @brief MQTT Manual Re-Connection Function
 *
 * Called to establish an MQTT connection with the AWS IoT Service
 * using parameters from the last time a connection was attempted
 * Use after disconnect to start the reconnect process manually
 * Makes only one reconnect attempt. Sets the client state to
 * pending reconnect in case of failure
 *
 * @param pClient Reference to the IoT Client
 *
 * @return An IoT Error Type defining successful/failed connection
 */
IoT_Error_t aws_iot_mqtt_attempt_reconnect(AWS_IoT_Client *pClient) {
	IoT_Error_t rc;

	FUNC_ENTRY;

	if(NULL == pClient) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	if(aws_iot_mqtt_is_client_connected(pClient)) {
		FUNC_EXIT_RC(NETWORK_ALREADY_CONNECTED_ERROR);
	}

	/* Ignoring return code. failures expected if network is disconnected */
	rc = aws_iot_mqtt_connect(pClient, NULL);

	/* If still disconnected handle disconnect */
	if(CLIENT_STATE_CONNECTED_IDLE != aws_iot_mqtt_get_client_state(pClient)) {
		aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_DISCONNECTED_ERROR, CLIENT_STATE_PENDING_RECONNECT);
		FUNC_EXIT_RC(NETWORK_ATTEMPTING_RECONNECT);
	}

	rc = aws_iot_mqtt_resubscribe(pClient);
	if(SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	FUNC_EXIT_RC(NETWORK_RECONNECTED);
}
/**
 * @brief Disconnect an MQTT Connection
 *
 * Called to send a disconnect message to the broker.
 * This is the outer function which does the validations and calls the internal disconnect above
 * to perform the actual operation. It is also responsible for client state changes
 *
 * @param pClient Reference to the IoT Client
 *
 * @return An IoT Error Type defining successful/failed send of the disconnect control packet.
 */
IoT_Error_t aws_iot_mqtt_disconnect(AWS_IoT_Client *pClient) {
	ClientState clientState;
	IoT_Error_t rc;

	FUNC_ENTRY;

	if(NULL == pClient) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	clientState = aws_iot_mqtt_get_client_state(pClient);
	if(!aws_iot_mqtt_is_client_connected(pClient)) {
		/* Network is already disconnected. Do nothing */
		FUNC_EXIT_RC(NETWORK_DISCONNECTED_ERROR);
	}

	rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_DISCONNECTING);
	if(SUCCESS != rc) {
		FUNC_EXIT_RC(rc);
	}

	rc = _aws_iot_mqtt_internal_disconnect(pClient);

	if(SUCCESS != rc) {
		pClient->clientStatus.clientState = clientState;
	} else {
		/* If called from Keepalive, this gets set to CLIENT_STATE_DISCONNECTED_ERROR */
		pClient->clientStatus.clientState = CLIENT_STATE_DISCONNECTED_MANUALLY;
	}

	FUNC_EXIT_RC(rc);
}
static IoT_Error_t _aws_iot_mqtt_internal_deliver_message(AWS_IoT_Client *pClient, char *pTopicName,
														  uint16_t topicNameLen,
														  IoT_Publish_Message_Params *pMessageParams) {
	uint32_t itr;
	IoT_Error_t rc;
	ClientState clientState;

	FUNC_ENTRY;

	if(NULL == pTopicName) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	/* This function can be called from all MQTT APIs
	 * But while callback return is in progress, Yield should not be called.
	 * The state for CB_RETURN accomplishes that, as yield cannot be called while in that state */
	clientState = aws_iot_mqtt_get_client_state(pClient);
	rc = aws_iot_mqtt_set_client_state(pClient, clientState, CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN);

	/* Find the right message handler - indexed by topic */
	for(itr = 0; itr < AWS_IOT_MQTT_NUM_SUBSCRIBE_HANDLERS; ++itr) {
		if(NULL != pClient->clientData.messageHandlers[itr].topicName) {
			if(((topicNameLen == pClient->clientData.messageHandlers[itr].topicNameLen)
				&&
				(strncmp(pTopicName, (char *) pClient->clientData.messageHandlers[itr].topicName, topicNameLen) == 0))
			   || _aws_iot_mqtt_internal_is_topic_matched((char *) pClient->clientData.messageHandlers[itr].topicName,
														  pTopicName, topicNameLen)) {
				if(NULL != pClient->clientData.messageHandlers[itr].pApplicationHandler) {
					pClient->clientData.messageHandlers[itr].pApplicationHandler(pClient, pTopicName, topicNameLen,
																				 pMessageParams,
																				 pClient->clientData.messageHandlers[itr].pApplicationHandlerData);
				}
			}
		}
	}
	rc = aws_iot_mqtt_set_client_state(pClient, CLIENT_STATE_CONNECTED_WAIT_FOR_CB_RETURN, clientState);

	FUNC_EXIT_RC(rc);
}