static void AckStatusCallback(AWS_IoT_Client *pClient, char *topicName, uint16_t topicNameLen,
							  IoT_Publish_Message_Params *params, void *pData) {
	int32_t tokenCount;
	uint8_t i;
	void *pJsonHandler = NULL;
	char temporaryClientToken[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE];

	IOT_UNUSED(pClient);
	IOT_UNUSED(topicNameLen);
	IOT_UNUSED(pData);

	if(params->payloadLen > SHADOW_MAX_SIZE_OF_RX_BUFFER) {
		IOT_WARN("Payload larger than RX Buffer");
		return;
	}

	memcpy(shadowRxBuf, params->payload, params->payloadLen);
	shadowRxBuf[params->payloadLen] = '\0';    // jsmn_parse relies on a string

	if(!isJsonValidAndParse(shadowRxBuf, pJsonHandler, &tokenCount)) {
		IOT_WARN("Received JSON is not valid");
		return;
	}

	if(isAckForMyThingName(topicName)) {
		uint32_t tempVersionNumber = 0;
		if(extractVersionNumber(shadowRxBuf, pJsonHandler, tokenCount, &tempVersionNumber)) {
			if(tempVersionNumber > shadowJsonVersionNum) {
				shadowJsonVersionNum = tempVersionNumber;
			}
		}
	}

	if(extractClientToken(shadowRxBuf, temporaryClientToken)) {
		for(i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) {
			if(!AckWaitList[i].isFree) {
				if(strcmp(AckWaitList[i].clientTokenID, temporaryClientToken) == 0) {
					Shadow_Ack_Status_t status;
					if(strstr(topicName, "accepted") != NULL) {
						status = SHADOW_ACK_ACCEPTED;
					} else if(strstr(topicName, "rejected") != NULL) {
						status = SHADOW_ACK_REJECTED;
					} else {
						continue;
					}
					/* status == SHADOW_ACK_ACCEPTED || status == SHADOW_ACK_REJECTED */
					if(AckWaitList[i].callback != NULL) {
						AckWaitList[i].callback(AckWaitList[i].thingName, AckWaitList[i].action, status,
												shadowRxBuf, AckWaitList[i].pCallbackContext);
					}
					unsubscribeFromAcceptedAndRejected(i);
					AckWaitList[i].isFree = true;
					return;
				}
			}
		}
	}
}
static int AckStatusCallback(MQTTCallbackParams params) {
	int32_t tokenCount;
	int32_t i;
	void *pJsonHandler;
	char temporaryClientToken[MAX_SIZE_CLIENT_TOKEN_CLIENT_SEQUENCE];

	if (params.MessageParams.PayloadLen > SHADOW_MAX_SIZE_OF_RX_BUFFER) {
		return GENERIC_ERROR;
	}

	memcpy(shadowRxBuf, params.MessageParams.pPayload, params.MessageParams.PayloadLen);
	shadowRxBuf[params.MessageParams.PayloadLen] = '\0';	// jsmn_parse relies on a string

	if (!isJsonValidAndParse(shadowRxBuf, pJsonHandler, &tokenCount)) {
		WARN("Received JSON is not valid");
		return GENERIC_ERROR;
	}

	if (isAckForMyThingName(params.pTopicName)) {
		uint32_t tempVersionNumber = 0;
		if (extractVersionNumber(shadowRxBuf, pJsonHandler, tokenCount, &tempVersionNumber)) {
			if (tempVersionNumber > shadowJsonVersionNum) {
				shadowJsonVersionNum = tempVersionNumber;
			}
		}
	}

	if (extractClientToken(shadowRxBuf, temporaryClientToken)) {
		for (i = 0; i < MAX_ACKS_TO_COMEIN_AT_ANY_GIVEN_TIME; i++) {
			if (!AckWaitList[i].isFree) {
				if (strcmp(AckWaitList[i].clientTokenID, temporaryClientToken) == 0) {
					Shadow_Ack_Status_t status;
					if (strstr(params.pTopicName, "accepted") != NULL) {
						status = SHADOW_ACK_ACCEPTED;
					} else if (strstr(params.pTopicName, "rejected") != NULL) {
						status = SHADOW_ACK_REJECTED;
					}
					if (status == SHADOW_ACK_ACCEPTED || status == SHADOW_ACK_REJECTED) {
						if (AckWaitList[i].callback != NULL) {
							AckWaitList[i].callback(AckWaitList[i].thingName, AckWaitList[i].action, status,
									shadowRxBuf, AckWaitList[i].pCallbackContext);
						}
						unsubscribeFromAcceptedAndRejected(i);
						AckWaitList[i].isFree = true;
						return NONE_ERROR;
					}
				}
			}
		}
	}

	return GENERIC_ERROR;
}
IoT_Error_t iot_shadow_action(const char *pThingName, ShadowActions_t action,
		const char *pJsonDocumentToBeSent, fpActionCallback_t callback, void *pCallbackContext,
		uint32_t timeout_seconds, bool isSticky) {

	IoT_Error_t ret_val = SUCCESS;
	bool isCallbackPresent = false;
	bool isClientTokenPresent = false;
	bool isAckWaitListFree = false;
	uint8_t indexAckWaitList;

	if(pThingName == NULL || pJsonDocumentToBeSent == NULL){
		return NULL_VALUE_ERROR;
	}

	if (callback != NULL) {
		isCallbackPresent = true;
	}

	char extractedClientToken[MAX_SIZE_CLIENT_ID_WITH_SEQUENCE];
	isClientTokenPresent = extractClientToken(pJsonDocumentToBeSent, extractedClientToken);

	if (isClientTokenPresent && isCallbackPresent) {
		if (getNextFreeIndexOfAckWaitList(&indexAckWaitList)) {
			isAckWaitListFree = true;
		}

		if(isAckWaitListFree) {
			if (!isSubscriptionPresent(pThingName, action)) {
				ret_val = subscribeToShadowActionAcks(pThingName, action, isSticky);
			} else {
				incrementSubscriptionCnt(pThingName, action, isSticky);
			}
		}
		else {
			ret_val = FAILURE;
		}
	}


	if (ret_val == SUCCESS) {
		ret_val = publishToShadowAction(pThingName, action, pJsonDocumentToBeSent);
	}

	if (isClientTokenPresent && isCallbackPresent && ret_val == SUCCESS && isAckWaitListFree) {
		addToAckWaitList(indexAckWaitList, pThingName, action, extractedClientToken, callback, pCallbackContext,
				timeout_seconds);
	}
	return ret_val;
}
IoT_Error_t aws_iot_shadow_internal_action(const char *pThingName, ShadowActions_t action,
										   const char *pJsonDocumentToBeSent, fpActionCallback_t callback,
										   void *pCallbackContext, uint32_t timeout_seconds, bool isSticky) {
	IoT_Error_t ret_val = AWS_SUCCESS;
	bool isClientTokenPresent = false;
	bool isAckWaitListFree = false;
	uint8_t indexAckWaitList;
	char extractedClientToken[MAX_SIZE_CLIENT_ID_WITH_SEQUENCE];

	FUNC_ENTRY;

	if(NULL == pThingName || NULL == pJsonDocumentToBeSent) {
		FUNC_EXIT_RC(NULL_VALUE_ERROR);
	}

	isClientTokenPresent = extractClientToken(pJsonDocumentToBeSent, extractedClientToken);

	if(isClientTokenPresent && (NULL != callback)) {
		if(getNextFreeIndexOfAckWaitList(&indexAckWaitList)) {
			isAckWaitListFree = true;
		}

		if(isAckWaitListFree) {
			if(!isSubscriptionPresent(pThingName, action)) {
				ret_val = subscribeToShadowActionAcks(pThingName, action, isSticky);
			} else {
				incrementSubscriptionCnt(pThingName, action, isSticky);
			}
		}
		else {
			ret_val = AWS_FAILURE;
		}
	}

	if(AWS_SUCCESS == ret_val) {
		ret_val = publishToShadowAction(pThingName, action, pJsonDocumentToBeSent);
	}

	if(isClientTokenPresent && (NULL != callback) && (AWS_SUCCESS == ret_val) && isAckWaitListFree) {
		addToAckWaitList(indexAckWaitList, pThingName, action, extractedClientToken, callback, pCallbackContext,
						 timeout_seconds);
	}

	FUNC_EXIT_RC(ret_val);
}