text: /*
   * AWS IoT Shadow V2.1.0
   * Copyright (C) 2018 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of
   * this software and associated documentation files (the "Software"), to deal in
   * the Software without restriction, including without limitation the rights to
   * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
   * the Software, and to permit persons to whom the Software is furnished to do so,
   * subject to the following conditions:
   *
   * The above copyright notice and this permission notice shall be included in all
   * copies or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
   * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
   * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
   * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  /**
   * @file aws_iot_shadow.h
   * @brief User-facing functions of the Shadow library.
   */
  
  #ifndef AWS_IOT_SHADOW_H_
  #define AWS_IOT_SHADOW_H_
  
  /* The config header is always included first. */
  #include "iot_config.h"
  
  /* Shadow types include. */
  #include "types/aws_iot_shadow_types.h"
  
  /*------------------------ Shadow library functions -------------------------*/
  
  /**
   * @functionspage{shadow,Shadow library}
   * - @functionname{shadow_function_init}
   * - @functionname{shadow_function_cleanup}
   * - @functionname{shadow_function_deleteasync}
   * - @functionname{shadow_function_deletesync}
   * - @functionname{shadow_function_getasync}
   * - @functionname{shadow_function_getsync}
   * - @functionname{shadow_function_updateasync}
   * - @functionname{shadow_function_updatesync}
   * - @functionname{shadow_function_wait}
   * - @functionname{shadow_function_setdeltacallback}
   * - @functionname{shadow_function_setupdatedcallback}
   * - @functionname{shadow_function_removepersistentsubscriptions}
   * - @functionname{shadow_function_strerror}
   */
  
  /**
   * @functionpage{AwsIotShadow_Init,shadow,init}
   * @functionpage{AwsIotShadow_Cleanup,shadow,cleanup}
   * @functionpage{AwsIotShadow_DeleteAsync,shadow,deleteasync}
   * @functionpage{AwsIotShadow_DeleteSync,shadow,deletesync}
   * @functionpage{AwsIotShadow_GetAsync,shadow,getasync}
   * @functionpage{AwsIotShadow_GetSync,shadow,getsync}
   * @functionpage{AwsIotShadow_UpdateAsync,shadow,updateasync}
   * @functionpage{AwsIotShadow_UpdateSync,shadow,updatesync}
   * @functionpage{AwsIotShadow_Wait,shadow,wait}
   * @functionpage{AwsIotShadow_SetDeltaCallback,shadow,setdeltacallback}
   * @functionpage{AwsIotShadow_SetUpdatedCallback,shadow,setupdatedcallback}
   * @functionpage{AwsIotShadow_RemovePersistentSubscriptions,shadow,removepersistentsubscriptions}
   * @functionpage{AwsIotShadow_strerror,shadow,strerror}
   */
  
  /**
   * @brief One-time initialization function for the Shadow library.
   *
   * This function performs internal setup of the Shadow library. <b>It must be
   * called once (and only once) before calling any other Shadow function.</b>
   * Calling this function more than once without first calling @ref
   * shadow_function_cleanup may result in a crash.
   *
   * @param[in] mqttTimeoutMs The amount of time (in milliseconds) that the Shadow
   * library will wait for MQTT operations. Optional; set this to `0` to use
   * @ref AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS.
   *
   * @return One of the following:
   * - #AWS_IOT_SHADOW_SUCCESS
   * - #AWS_IOT_SHADOW_INIT_FAILED
   *
   * @warning No thread-safety guarantees are provided for this function.
   *
   * @see @ref shadow_function_cleanup
   */
  /* @[declare_shadow_init] */
  AwsIotShadowError_t AwsIotShadow_Init( uint32_t mqttTimeoutMs );
  /* @[declare_shadow_init] */
  
  /**
   * @brief One-time deinitialization function for the Shadow library.
   *
   * This function frees resources taken in @ref shadow_function_init and deletes
   * any [persistent subscriptions.](@ref AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS)
   * It should be called to clean up the Shadow library. After this function returns,
   * @ref shadow_function_init must be called again before calling any other Shadow
   * function.
   *
   * @warning No thread-safety guarantees are provided for this function.
   *
   * @see @ref shadow_function_init
   */
  /* @[declare_shadow_cleanup] */
  void AwsIotShadow_Cleanup( void );
  /* @[declare_shadow_cleanup] */
  
  /**
   * @brief Delete a Thing Shadow and receive an asynchronous notification when
   * the Delete completes.
   *
   * This function deletes any existing Shadow document for the given Thing Name.
   * If the given Thing has no Shadow and this function is called, the result will
   * be #AWS_IOT_SHADOW_NOT_FOUND.
   *
   * Deleting a Shadow involves sending an MQTT message to AWS IoT and waiting on
   * a response. This message will always be sent at [MQTT QoS 0](@ref #IOT_MQTT_QOS_0).
   *
   * @param[in] mqttConnection The MQTT connection to use for Shadow delete.
   * @param[in] pThingName The Thing Name associated with the Shadow to delete.
   * @param[in] thingNameLength The length of `pThingName`.
   * @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
   * @param[in] pCallbackInfo Asynchronous notification of this function's completion.
   * @param[out] pDeleteOperation Set to a handle by which this operation may be referenced
   * after this function returns. This reference is invalidated once the Shadow delete
   * completes.
   *
   * @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
   * queuing a Shadow delete.
   * @return If this function fails before queuing a Shadow delete, it will return one of:
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * @return Upon successful completion of the Shadow delete (either through an #AwsIotShadowCallbackInfo_t
   * or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
   * @return Should the Shadow delete fail, the status will be one of:
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_BAD_RESPONSE
   * - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
   * and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
   *
   * @see @ref shadow_function_deletesync for a blocking variant of this function.
   *
   * <b>Example</b>
   * @code{c}
   * #define THING_NAME "Test_device"
   * #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
   *
   * // Shadow operation handle.
   * AwsIotShadowOperation_t deleteOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
   *
   * // Queue a Shadow delete.
   * AwsIotShadowError_t deleteResult = AwsIotShadow_DeleteAsync( mqttConnection,
   *                                                              THING_NAME,
   *                                                              THING_NAME_LENGTH,
   *                                                              AWS_IOT_SHADOW_FLAG_WAITABLE,
   *                                                              NULL,
   *                                                              &deleteOperation );
   *
   * // Shadow delete should return AWS_IOT_SHADOW_STATUS_PENDING upon success.
   * if( deleteResult == AWS_IOT_SHADOW_STATUS_PENDING )
   * {
   *     // Wait for the Shadow delete to complete.
   *     deleteResult = AwsIotShadow_Wait( deleteOperation, 5000 );
   *
   *     // Delete result should be AWS_IOT_SHADOW_SUCCESS upon successfully
   *     // deleting an existing Shadow.
   * }
   * @endcode
   */
  /* @[declare_shadow_deleteasync] */
  AwsIotShadowError_t AwsIotShadow_DeleteAsync( IotMqttConnection_t mqttConnection,
                                                const char * pThingName,
                                                size_t thingNameLength,
                                                uint32_t flags,
                                                const AwsIotShadowCallbackInfo_t * pCallbackInfo,
                                                AwsIotShadowOperation_t * const pDeleteOperation );
  /* @[declare_shadow_deleteasync] */
  
  /**
   * @brief Delete a Thing Shadow with a timeout.
   *
   * This function queues a Shadow delete, then waits for the result. Internally, this
   * function is a call to @ref shadow_function_deleteasync followed by @ref shadow_function_wait.
   * See @ref shadow_function_deleteasync for more information on the Shadow delete operation.
   *
   * @param[in] mqttConnection The MQTT connection to use for Shadow delete.
   * @param[in] pThingName The Thing Name associated with the Shadow to delete.
   * @param[in] thingNameLength The length of `pThingName`.
   * @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
   * @param[in] timeoutMs If the Shadow service does not respond to the Shadow delete
   * within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
   *
   * @return One of the following:
   * - #AWS_IOT_SHADOW_SUCCESS
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_BAD_RESPONSE
   * - #AWS_IOT_SHADOW_TIMEOUT
   * - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
   * and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
   */
  /* @[declare_shadow_deletesync] */
  AwsIotShadowError_t AwsIotShadow_DeleteSync( IotMqttConnection_t mqttConnection,
                                               const char * pThingName,
                                               size_t thingNameLength,
                                               uint32_t flags,
                                               uint32_t timeoutMs );
  /* @[declare_shadow_deletesync] */
  
  /**
   * @brief Retrieve a Thing Shadow and receive an asynchronous notification when
   * the Shadow document is received.
   *
   * This function retrieves the Thing Shadow document currently stored by the
   * Shadow service. If a given Thing has no Shadow and this function is called,
   * the result will be #AWS_IOT_SHADOW_NOT_FOUND.
   *
   * Shadow documents may be large, and their size is not known beforehand.
   * Therefore, this function works best when memory is dynamically allocated.
   * Because the Shadow document is retrieved in an MQTT PUBLISH packet, the MQTT
   * library will allocate a buffer for the Shadow document using #IotMqtt_MallocMessage.
   *
   * The MQTT library may free the buffer for a retrieved Shadow document as soon
   * as the [Shadow completion callback](@ref AwsIotShadowCallbackInfo_t) returns.
   * Therefore, any data needed later must be copied from the Shadow document.
   * Similarly, if the flag #AWS_IOT_SHADOW_FLAG_WAITABLE is given to this function
   * (which indicates that the Shadow document will be needed after the Shadow
   * operation completes), #AwsIotShadowDocumentInfo_t.mallocDocument must be
   * provided to allocate a longer-lasting buffer.
   *
   * @note Because of the potentially large size of complete Shadow documents, it is more
   * memory-efficient for most applications to use [delta callbacks]
   * (@ref shadow_function_setdeltacallback) to retrieve Shadows from
   * the Shadow service.
   *
   * @param[in] mqttConnection The MQTT connection to use for Shadow get.
   * @param[in] pGetInfo Shadow document parameters.
   * @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
   * @param[in] pCallbackInfo Asynchronous notification of this function's completion.
   * @param[out] pGetOperation Set to a handle by which this operation may be referenced
   * after this function returns. This reference is invalidated once the Shadow get
   * completes.
   *
   * @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
   * queuing a Shadow get.
   * @return If this function fails before queuing a Shadow get, it will return one of:
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * @return Upon successful completion of the Shadow get (either through an #AwsIotShadowCallbackInfo_t
   * or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
   * @return Should the Shadow get fail, the status will be one of:
   * - #AWS_IOT_SHADOW_NO_MEMORY (Memory could not be allocated for incoming document)
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_BAD_RESPONSE
   * - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
   * and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
   *
   * @see @ref shadow_function_getsync for a blocking variant of this function.
   *
   * <b>Example</b>
   * @code{c}
   * // Shadow get completion callback. The retrieved document will be in
   * // pCallbackParam. Any data in the retrieved document needed after this
   * // function returns must be copied.
   * void _processRetrievedDocument( void * pCallbackContext,
   *                                 AwsIotShadowCallbackParam_t * pCallbackParam );
   *
   * // Parameters and return value of Shadow get.
   * AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
   * AwsIotShadowDocumentInfo_t getInfo = { ... };
   * uint32_t timeout = 5000; // 5 seconds
   *
   * // Callback for get completion.
   * AwsIotShadowCallbackInfo_t getCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
   * getCallback.function = _processRetrievedDocument;
   *
   * // Shadow get operation.
   * result = AwsIotShadow_GetAsync( mqttConnection,
   *                                 &getInfo,
   *                                 0,
   *                                 &getCallback,
   *                                 NULL );
   *
   * // Get should have returned AWS_IOT_SHADOW_STATUS_PENDING. The function
   * // _processRetrievedDocument will be invoked once the Shadow get completes.
   * @endcode
   *
   * See @ref shadow_function_wait <b>Example 2</b> for an example of using this
   * function with #AWS_IOT_SHADOW_FLAG_WAITABLE and @ref shadow_function_wait.
   */
  /* @[declare_shadow_getasync] */
  AwsIotShadowError_t AwsIotShadow_GetAsync( IotMqttConnection_t mqttConnection,
                                             const AwsIotShadowDocumentInfo_t * pGetInfo,
                                             uint32_t flags,
                                             const AwsIotShadowCallbackInfo_t * pCallbackInfo,
                                             AwsIotShadowOperation_t * const pGetOperation );
  /* @[declare_shadow_getasync] */
  
  /**
   * @brief Retrieve a Thing Shadow with a timeout.
   *
   * This function queues a Shadow get, then waits for the result. Internally, this
   * function is a call to @ref shadow_function_getasync followed by @ref shadow_function_wait.
   * See @ref shadow_function_getasync for more information on the Shadow get operation.
   *
   * @param[in] mqttConnection The MQTT connection to use for Shadow get.
   * @param[in] pGetInfo Shadow document parameters.
   * @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
   * @param[in] timeoutMs If the Shadow service does not respond to the Shadow get
   * within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
   * @param[out] pShadowDocument A pointer to a buffer containing the Shadow document
   * retrieved by a Shadow get is placed here. The buffer was allocated with the function
   * `pGetInfo->get.mallocDocument`. This output parameter is only valid if this function
   * returns #AWS_IOT_SHADOW_SUCCESS.
   * @param[out] pShadowDocumentLength The length of the Shadow document in
   * `pShadowDocument` is placed here. This output parameter is only valid if this function
   * returns #AWS_IOT_SHADOW_SUCCESS.
   *
   * @return One of the following:
   * - #AWS_IOT_SHADOW_SUCCESS
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_BAD_RESPONSE
   * - #AWS_IOT_SHADOW_TIMEOUT
   * - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
   * and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
   */
  /* @[declare_shadow_getsync] */
  AwsIotShadowError_t AwsIotShadow_GetSync( IotMqttConnection_t mqttConnection,
                                            const AwsIotShadowDocumentInfo_t * pGetInfo,
                                            uint32_t flags,
                                            uint32_t timeoutMs,
                                            const char ** const pShadowDocument,
                                            size_t * const pShadowDocumentLength );
  /* @[declare_shadow_getsync] */
  
  /**
   * @brief Send a Thing Shadow update and receive an asynchronous notification when
   * the Shadow Update completes.
   *
   * This function modifies the Thing Shadow document stored by the Shadow service.
   * If a given Thing has no Shadow and this function is called, then a new Shadow
   * is created.
   *
   * New JSON keys in the Shadow document will be appended. For example, if the Shadow service
   * currently has a document containing key `example1` and this function sends a document
   * only containing key `example2`, then the resulting document in the Shadow service
   * will contain both `example1` and `example2`.
   *
   * Existing JSON keys in the Shadow document will be replaced. For example, if the Shadow
   * service currently has a document containing `"example1": [0,1,2]` and this function sends
   * a document containing key `"example1": [1,2,3]`, then the resulting document in the Shadow
   * service will contain `"example1": [1,2,3]`.
   *
   * Successful Shadow updates will trigger the [Shadow updated callback]
   * (@ref shadow_function_setupdatedcallback). If the resulting Shadow document contains
   * different `desired` and `reported` keys, then the [Shadow delta callback]
   * (@ref shadow_function_setdeltacallback) will be triggered as well.
   *
   * @attention All documents passed to this function must contain a `clientToken`.
   * The [client token]
   * (https://docs.aws.amazon.com/iot/latest/developerguide/device-shadow-document.html#client-token)
   * is a string used to distinguish between Shadow updates. They are limited to 64
   * characters; attempting to use a client token longer than 64 characters will
   * cause the Shadow update to fail. They must be unique at any given time, i.e.
   * they may be reused <i>as long as no two Shadow updates are using the same
   * client token at the same time</i>.
   *
   * @param[in] mqttConnection The MQTT connection to use for Shadow update.
   * @param[in] pUpdateInfo Shadow document parameters.
   * @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
   * @param[in] pCallbackInfo Asynchronous notification of this function's completion.
   * @param[out] pUpdateOperation Set to a handle by which this operation may be referenced
   * after this function returns. This reference is invalidated once the Shadow update
   * completes.
   *
   * @return This function will return #AWS_IOT_SHADOW_STATUS_PENDING upon successfully
   * queuing a Shadow update.
   * @return If this function fails before queuing a Shadow update, it will return one of:
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * @return Upon successful completion of the Shadow update (either through an #AwsIotShadowCallbackInfo_t
   * or #AwsIotShadow_Wait), the status will be #AWS_IOT_SHADOW_SUCCESS.
   * @return Should the Shadow update fail, the status will be one of:
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_BAD_RESPONSE
   * - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
   * and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
   *
   * @see @ref shadow_function_updatesync for a blocking variant of this function.
   *
   * <b>Example</b>
   * @code{c}
   * // Shadow update completion callback.
   * void _updateComplete( void * pCallbackContext,
   *                       AwsIotShadowCallbackParam_t * pCallbackParam );
   *
   * // Parameters and return value of Shadow update.
   * AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
   * AwsIotShadowDocumentInfo_t updateInfo = { ... };
   * uint32_t timeout = 5000; // 5 seconds
   *
   * // Set Shadow document to send.
   * updateInfo.update.pUpdateDocument = "{...}"; // Must contain clientToken
   * updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
   *
   * // Callback for update completion.
   * AwsIotShadowCallbackInfo_t updateCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
   * updateCallback.function = _updateComplete;
   *
   * // Shadow update operation.
   * result = AwsIotShadow_UpdateAsync( mqttConnection,
   *                                    &updateInfo,
   *                                    0,
   *                                    &updateCallback,
   *                                    NULL );
   *
   * // Update should have returned AWS_IOT_SHADOW_STATUS_PENDING. The function
   * // _updateComplete will be invoked once the Shadow update completes.
   * @endcode
   *
   * See @ref shadow_function_wait <b>Example 1</b> for an example of using this
   * function with #AWS_IOT_SHADOW_FLAG_WAITABLE and @ref shadow_function_wait.
   */
  /* @[declare_shadow_updateasync] */
  AwsIotShadowError_t AwsIotShadow_UpdateAsync( IotMqttConnection_t mqttConnection,
                                                const AwsIotShadowDocumentInfo_t * pUpdateInfo,
                                                uint32_t flags,
                                                const AwsIotShadowCallbackInfo_t * pCallbackInfo,
                                                AwsIotShadowOperation_t * const pUpdateOperation );
  /* @[declare_shadow_updateasync] */
  
  /**
   * @brief Send a Thing Shadow update with a timeout.
   *
   * This function queues a Shadow update, then waits for the result. Internally, this
   * function is a call to @ref shadow_function_updateasync followed by @ref shadow_function_wait.
   * See @ref shadow_function_updateasync for more information on the Shadow update operation.
   *
   * @param[in] mqttConnection The MQTT connection to use for Shadow update.
   * @param[in] pUpdateInfo Shadow document parameters.
   * @param[in] flags Flags which modify the behavior of this function. See @ref shadow_constants_flags.
   * @param[in] timeoutMs If the Shadow service does not respond to the Shadow update
   * within this timeout, this function returns #AWS_IOT_SHADOW_TIMEOUT.
   *
   * @return One of the following:
   * - #AWS_IOT_SHADOW_SUCCESS
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_BAD_RESPONSE
   * - #AWS_IOT_SHADOW_TIMEOUT
   * - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
   * and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
   */
  /* @[declare_shadow_updatesync] */
  AwsIotShadowError_t AwsIotShadow_UpdateSync( IotMqttConnection_t mqttConnection,
                                               const AwsIotShadowDocumentInfo_t * pUpdateInfo,
                                               uint32_t flags,
                                               uint32_t timeoutMs );
  /* @[declare_shadow_updatesync] */
  
  /**
   * @brief Wait for a Shadow operation to complete.
   *
   * This function blocks to wait for a [delete](@ref shadow_function_deleteasync),
   * [get](@ref shadow_function_getasync), or [update](@ref shadow_function_updateasync) to
   * complete. These operations are by default asynchronous; the function calls
   * queue an operation for processing, and a callback is invoked once the operation
   * is complete.
   *
   * To use this function, the flag #AWS_IOT_SHADOW_FLAG_WAITABLE must have been
   * set in the operation's function call. Additionally, this function must always
   * be called with any waitable operation to clean up resources.
   *
   * Regardless of its return value, this function always clean up resources used
   * by the waitable operation. This means `operation` is invalidated as soon as
   * this function returns, even if it returns #AWS_IOT_SHADOW_TIMEOUT or another
   * error.
   *
   * @param[in] operation Reference to the Shadow operation to wait for. The flag
   * #AWS_IOT_SHADOW_FLAG_WAITABLE must have been set for this operation.
   * @param[in] timeoutMs How long to wait before returning #AWS_IOT_SHADOW_TIMEOUT.
   * @param[out] pShadowDocument A pointer to a buffer containing the Shadow document
   * retrieved by a [Shadow get](@ref shadow_function_getasync) is placed here. The buffer
   * was allocated with the function #AwsIotShadowDocumentInfo_t.mallocDocument passed
   * to @ref shadow_function_getasync. This parameter is only valid for a [Shadow get]
   * (@ref shadow_function_getasync) and ignored for other Shadow operations. This output
   * parameter is only valid if this function returns #AWS_IOT_SHADOW_SUCCESS.
   * @param[out] pShadowDocumentLength The length of the Shadow document in
   * `pShadowDocument` is placed here. This parameter is only valid for a [Shadow get]
   * (@ref shadow_function_getasync) and ignored for other Shadow operations. This output
   * parameter is only valid if this function returns #AWS_IOT_SHADOW_SUCCESS.
   *
   * @return One of the following:
   * - #AWS_IOT_SHADOW_SUCCESS
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_BAD_RESPONSE
   * - #AWS_IOT_SHADOW_TIMEOUT
   * - A Shadow service rejection reason between 400 (#AWS_IOT_SHADOW_BAD_REQUEST)
   * and 500 (#AWS_IOT_SHADOW_SERVER_ERROR)
   *
   * <b>Example 1 (Shadow Update)</b>
   * @code{c}
   * AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
   * AwsIotShadowDocumentInfo_t updateInfo = { ... };
   *
   * // Reference and timeout.
   * AwsIotShadowOperation_t updateOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
   * uint32_t timeout = 5000; // 5 seconds
   *
   * // Shadow update operation.
   * result = AwsIotShadow_UpdateAsync( mqttConnection,
   *                                    &updateInfo,
   *                                    AWS_IOT_SHADOW_FLAG_WAITABLE,
   *                                    NULL,
   *                                    &updateOperation );
   *
   * // Update should have returned AWS_IOT_SHADOW_STATUS_PENDING. The call to wait
   * // returns once the result of the update is available or the timeout expires.
   * if( result == AWS_IOT_SHADOW_STATUS_PENDING )
   * {
   *     // The last two parameters are ignored for a Shadow update.
   *     result = AwsIotShadow_Wait( updateOperation, timeout, NULL, NULL );
   *
   *     // After the call to wait, the result of the update is known
   *     // (not AWS_IOT_SHADOW_STATUS_PENDING).
   *     assert( result != AWS_IOT_SHADOW_STATUS_PENDING );
   * }
   * @endcode
   *
   * <b>Example 2 (Shadow Get)</b>
   * @code{c}
   * AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
   * AwsIotShadowDocumentInfo_t getInfo = { ... };
   *
   * // Reference and timeout.
   * AwsIotShadowOperation_t getOperation = AWS_IOT_SHADOW_OPERATION_INITIALIZER;
   * uint32_t timeout = 5000; // 5 seconds
   *
   * // Buffer pointer and size for retrieved Shadow document.
   * const char * pShadowDocument = NULL;
   * size_t documentLength = 0;
   *
   * // Buffer allocation function must be set for a waitable Shadow get.
   * getInfo.get.mallocDocument = malloc;
   *
   * // Shadow get operation.
   * result = AwsIotShadow_GetAsync( mqttConnection,
   *                                 &getInfo,
   *                                 AWS_IOT_SHADOW_FLAG_WAITABLE,
   *                                 NULL,
   *                                 &getOperation );
   *
   * // Get should have returned AWS_IOT_SHADOW_STATUS_PENDING. The call to wait
   * // returns once the result of the get is available or the timeout expires.
   * if( result == AWS_IOT_SHADOW_STATUS_PENDING )
   * {
   *     // The last two parameters must be set for a Shadow get.
   *     result = AwsIotShadow_Wait( getOperation, timeout, &pShadowDocument, &documentLength );
   *
   *     // After the call to wait, the result of the get is known
   *     // (not AWS_IOT_SHADOW_STATUS_PENDING).
   *     assert( result != AWS_IOT_SHADOW_STATUS_PENDING );
   *
   *     // The retrieved Shadow document is only valid for a successful Shadow get.
   *     if( result == AWS_IOT_SHADOW_SUCCESS )
   *     {
   *         // Do something with the Shadow document...
   *
   *         // Free the Shadow document when finished.
   *         free( pShadowDocument );
   *     }
   * }
   * @endcode
   */
  /* @[declare_shadow_wait] */
  AwsIotShadowError_t AwsIotShadow_Wait( AwsIotShadowOperation_t operation,
                                         uint32_t timeoutMs,
                                         const char ** const pShadowDocument,
                                         size_t * const pShadowDocumentLength );
  /* @[declare_shadow_wait] */
  
  /**
   * @brief Set a callback to be invoked when the Thing Shadow `desired` and `reported`
   * states differ.
   *
   * A Thing Shadow contains `reported` and `desired` states, meant to represent
   * the current device status and some desired status, respectively. When the
   * `reported` and `desired` states differ, the Thing Shadow service generates a
   * <i>delta document</i> and publishes it to the topic `update/delta`. Devices
   * with a subscription for this topic will receive the delta document and may act
   * based on the different `reported` and `desired` states. See [this page]
   * (https://docs.aws.amazon.com/iot/latest/developerguide/using-device-shadows.html#delta-state)
   * for more information about using delta documents.
   *
   * A <i>delta callback</i> may be invoked whenever a delta document is generated.
   * Each Thing may have a single delta callback set. This function modifies the delta
   * callback for a specific Thing depending on the `pDeltaCallback` parameter and
   * the presence of any existing delta callback:
   * - When no existing delta callback exists for a specific Thing, a new delta
   * callback is added.
   * - If there is an existing delta callback and `pDeltaCallback` is not `NULL`, then
   * the existing callback function and parameter are replaced with `pDeltaCallback`.
   * - If there is an existing subscription and `pDeltaCallback` is `NULL`, then the
   * delta callback is removed.
   *
   * This function is always blocking; it may block for up to the default MQTT
   * timeout. This timeout is set as a parameter to @ref shadow_function_init,
   * and defaults to @ref AWS_IOT_SHADOW_DEFAULT_MQTT_TIMEOUT_MS if not set. If
   * this function's underlying MQTT operations fail to complete within this
   * timeout, then this function returns #AWS_IOT_SHADOW_TIMEOUT.
   *
   * @param[in] mqttConnection The MQTT connection to use for the subscription to
   * `update/delta`.
   * @param[in] pThingName The subscription to `update/delta` will be added for
   * this Thing Name.
   * @param[in] thingNameLength The length of `pThingName`.
   * @param[in] flags This parameter is for future-compatibility. Currently, flags
   * are not supported for this function and this parameter is ignored.
   * @param[in] pDeltaCallback Callback function to invoke for incoming delta
   * documents.
   *
   * @return One of the following:
   * - #AWS_IOT_SHADOW_SUCCESS
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_TIMEOUT
   *
   * @return This function always returns #AWS_IOT_SHADOW_SUCCESS when replacing or
   * removing existing delta callbacks.
   *
   * @see @ref shadow_function_setupdatedcallback for the function to register
   * callbacks for all Shadow updates.
   *
   * <b>Example</b>
   * @code{c}
   * #define THING_NAME "Test_device"
   * #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
   *
   * AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
   * AwsIotShadowCallbackInfo_t deltaCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
   *
   * // _deltaCallbackFunction will be invoked when a delta document is received.
   * deltaCallback.function = _deltaCallbackFunction;
   *
   * // Set the delta callback for the Thing "Test_device".
   * result = AwsIotShadow_SetDeltaCallback( mqttConnection,
   *                                         THING_NAME,
   *                                         THING_NAME_LENGTH,
   *                                         0,
   *                                         &deltaCallback );
   *
   * // Check if callback was successfully set.
   * if( result == AWS_IOT_SHADOW_SUCCESS )
   * {
   *     AwsIotShadowDocumentInfo_t updateInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
   *
   *     // Set the Thing Name for Shadow update.
   *     updateInfo.pThingName = THING_NAME;
   *     updateInfo.thingNameLength = THING_NAME_LENGTH;
   *
   *     // Set the Shadow document to send. This document has different "reported"
   *     // and "desired" states. It represents a scenario where a device is currently
   *     // off, but is being ordered to turn on.
   *     updateInfo.update.pUpdateDocument =
   *     "{"
   *          "\"state\": {"
   *              "\"reported\": { \"deviceOn\": false },"
   *              "\"desired\": { \"deviceOn\": true }"
   *          "}"
   *     "}";
   *     updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
   *
   *     // Send the Shadow document with different "reported" and desired states.
   *     result = AwsIotShadow_UpdateSync( mqttConnection,
   *                                       &updateInfo,
   *                                       0,
   *                                       0,
   *                                       5000 );
   *
   *     // After the update is successfully sent, the function _deltaCallbackFunction
   *     // will be invoked once the Shadow service generates and sends a delta document.
   *     // The delta document will contain the different "deviceOn" states, as well as
   *     // metadata.
   *
   *     // Once the delta callback is no longer needed, it may be removed by passing
   *     // NULL as pDeltaCallback.
   *     result = AwsIotShadow_SetDeltaCallback( mqttConnection,
   *                                             THING_NAME,
   *                                             THING_NAME_LENGTH,
   *                                             0,
   *                                             NULL );
   *
   *     // The return value from removing a delta callback should always be success.
   *     assert( result == AWS_IOT_SHADOW_SUCCESS );
   * }
   * @endcode
   */
  /* @[declare_shadow_setdeltacallback] */
  AwsIotShadowError_t AwsIotShadow_SetDeltaCallback( IotMqttConnection_t mqttConnection,
                                                     const char * pThingName,
                                                     size_t thingNameLength,
                                                     uint32_t flags,
                                                     const AwsIotShadowCallbackInfo_t * pDeltaCallback );
  /* @[declare_shadow_setdeltacallback] */
  
  /**
   * @brief Set a callback to be invoked when a Thing Shadow changes.
   *
   * The Shadow service publishes a state document to the `update/documents` topic
   * whenever a Thing Shadow is successfully updated. This document reports the
   * complete previous and current Shadow documents in `previous` and `current`
   * sections, respectively. Therefore, the `update/documents` topic is useful
   * for monitoring Shadow updates.
   *
   * An <i>updated callback</i> may be invoked whenever a document is published to
   * `update/documents`. Each Thing may have a single updated callback set. This function
   * modifies the updated callback for a specific Thing depending on the `pUpdatedCallback`
   * parameter and the presence of any existing updated callback.
   * - When no existing updated callback exists for a specific Thing, a new updated
   * callback is added.
   * - If there is an existing updated callback and `pUpdatedCallback` is not `NULL`,
   * then the existing callback function and parameter are replaced with `pUpdatedCallback`.
   * - If there is an existing updated callback and `pUpdatedCallback` is `NULL`,
   * then the updated callback is removed.
   *
   * @param[in] mqttConnection The MQTT connection to use for the subscription to `update/documents`.
   * @param[in] pThingName The subscription to `update/documents` will be added for
   * this Thing Name.
   * @param[in] thingNameLength The length of `pThingName`.
   * @param[in] flags This parameter is for future-compatibility. Currently, flags are
   * not supported for this function and this parameter is ignored.
   * @param[in] pUpdatedCallback Callback function to invoke for incoming updated documents.
   *
   * @return One of the following:
   * - #AWS_IOT_SHADOW_SUCCESS
   * - #AWS_IOT_SHADOW_NOT_INITIALIZED
   * - #AWS_IOT_SHADOW_BAD_PARAMETER
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   * - #AWS_IOT_SHADOW_TIMEOUT
   *
   * @note Documents published to `update/documents` will be large, as they contain 2
   * complete Shadow state documents. If an updated callback is used, ensure that the
   * device has sufficient memory for incoming documents.
   *
   * @see @ref shadow_function_setdeltacallback for the function to register callbacks
   * for delta documents.
   *
   * <b>Example</b>
   * @code{c}
   * #define THING_NAME "Test_device"
   * #define THING_NAME_LENGTH ( sizeof( THING_NAME ) - 1 )
   *
   * AwsIotShadowError_t result = AWS_IOT_SHADOW_STATUS_PENDING;
   * AwsIotShadowCallbackInfo_t updatedCallback = AWS_IOT_SHADOW_CALLBACK_INFO_INITIALIZER;
   *
   * // _updatedCallbackFunction will be invoked when an updated document is received.
   * updatedCallback.function = _updatedCallbackFunction;
   *
   * // Set the updated callback for the Thing "Test_device".
   * result = AwsIotShadow_SetUpdatedCallback( mqttConnection,
   *                                           THING_NAME,
   *                                           THING_NAME_LENGTH,
   *                                           0,
   *                                           &updatedCallback );
   *
   * // Check if the callback was successfully set.
   * if( result == AWS_IOT_SHADOW_SUCCESS )
   * {
   *     AwsIotShadowDocumentInfo_t updateInfo = AWS_IOT_SHADOW_DOCUMENT_INFO_INITIALIZER;
   *
   *     // Set the Thing Name for Shadow update.
   *     updateInfo.pThingName = THING_NAME;
   *     updateInfo.thingNameLength = THING_NAME_LENGTH;
   *
   *     // Set the Shadow document to send. Any Shadow update will trigger the
   *     // updated callback.
   *     updateInfo.update.pUpdateDocument =
   *     "{"
   *          "\"state\": {"
   *              "\"reported\": { \"deviceOn\": false }"
   *          "}"
   *     "}";
   *     updateInfo.update.updateDocumentLength = strlen( updateInfo.update.pUpdateDocument );
   *
   *     // Send the Shadow document. A successful update will trigger the updated callback.
   *     result = AwsIotShadow_UpdateSync( mqttConnection,
   *                                       &updateInfo,
   *                                       0,
   *                                       0,
   *                                       5000 );
   *
   *     // After a successful Shadow update, the updated callback will be invoked.
   *
   *     // Once the updated callback is no longer needed, it may be removed by
   *     // passing NULL as pUpdatedCallback.
   *     result = AwsIotShadow_SetUpdatedCallback( mqttConnection,
   *                                               THING_NAME,
   *                                               THING_NAME_LENGTH,
   *                                               NULL );
   *
   *     // The return value from removing an updated callback should always be
   *     // success.
   *     assert( result == AWS_IOT_SHADOW_SUCCESS );
   * }
   * @endcode
   */
  /* @[declare_shadow_setupdatedcallback] */
  AwsIotShadowError_t AwsIotShadow_SetUpdatedCallback( IotMqttConnection_t mqttConnection,
                                                       const char * pThingName,
                                                       size_t thingNameLength,
                                                       uint32_t flags,
                                                       const AwsIotShadowCallbackInfo_t * pUpdatedCallback );
  /* @[declare_shadow_setupdatedcallback] */
  
  /**
   * @brief Remove persistent Thing Shadow operation topic subscriptions.
   *
   * Passing the flag @ref AWS_IOT_SHADOW_FLAG_KEEP_SUBSCRIPTIONS to @ref shadow_function_deleteasync,
   * @ref shadow_function_getasync, @ref shadow_function_updateasync, or their blocking versions.
   * causes the Shadow operation topic subscriptions to be maintained for future calls to the
   * same function. If a persistent subscription for a Shadow topic are no longer needed,
   * this function may be used to remove it.
   *
   * @param[in] mqttConnection The MQTT connection associated with the persistent subscription.
   * @param[in] pThingName The Thing Name associated with the persistent subscription.
   * @param[in] thingNameLength The length of `pThingName`.
   * @param[in] flags Flags that determine which subscriptions to remove. Valid values are
   * the bitwise OR of the following individual flags:
   * - @ref AWS_IOT_SHADOW_FLAG_REMOVE_DELETE_SUBSCRIPTIONS
   * - @ref AWS_IOT_SHADOW_FLAG_REMOVE_GET_SUBSCRIPTIONS
   * - @ref AWS_IOT_SHADOW_FLAG_REMOVE_UPDATE_SUBSCRIPTIONS
   *
   * @return On success:
   * - #AWS_IOT_SHADOW_SUCCESS
   * @return If an MQTT UNSUBSCRIBE packet cannot be sent, one of the following:
   * - #AWS_IOT_SHADOW_NO_MEMORY
   * - #AWS_IOT_SHADOW_MQTT_ERROR
   *
   * @note @ref shadow_function_cleanup removes all persistent subscriptions as well.
   *
   * @warning This function is not safe to call with any in-progress operations!
   * It also does not affect delta and updated callbacks registered with @ref
   * shadow_function_setdeltacallback and @ref shadow_function_setupdatedcallback,
   * respectively. (See documentation for those functions on how to remove their
   * callbacks).
   */
  /* @[declare_shadow_removepersistentsubscriptions] */
  AwsIotShadowError_t AwsIotShadow_RemovePersistentSubscriptions( IotMqttConnection_t mqttConnection,
                                                                  const char * pThingName,
                                                                  size_t thingNameLength,
                                                                  uint32_t flags );
  /* @[declare_shadow_removepersistentsubscriptions] */
  
  /*------------------------- Shadow helper functions -------------------------*/
  
  /**
   * @brief Returns a string that describes an #AwsIotShadowError_t.
   *
   * Like POSIX's `strerror`, this function returns a string describing a return
   * code. In this case, the return code is a Shadow library error code, `status`.
   *
   * The string returned by this function <b>MUST</b> be treated as read-only: any
   * attempt to modify its contents may result in a crash. Therefore, this function
   * is limited to usage in logging.
   *
   * @param[in] status The status to describe.
   *
   * @return A read-only string that describes `status`.
   *
   * @warning The string returned by this function must never be modified.
   */
  /* @[declare_shadow_strerror] */
  const char * AwsIotShadow_strerror( AwsIotShadowError_t status );
  /* @[declare_shadow_strerror] */
  
  /**
   * @cond DOXYGEN_IGNORE
   * Doxygen should ignore this section.
   *
   * Backwards compatibility macros for previous function names.
   */
  #define AwsIotShadow_Delete         AwsIotShadow_DeleteAsync
  #define AwsIotShadow_TimedDelete    AwsIotShadow_DeleteSync
  #define AwsIotShadow_Get            AwsIotShadow_GetAsync
  #define AwsIotShadow_TimedGet       AwsIotShadow_GetSync
  #define AwsIotShadow_Update         AwsIotShadow_UpdateAsync
  #define AwsIotShadow_TimedUpdate    AwsIotShadow_UpdateSync
  /** @endcond */
  
  #endif /* ifndef AWS_IOT_SHADOW_H_ */
  