text: // Copyright (c) Microsoft. All rights reserved.
  // Licensed under the MIT license. See LICENSE file in the project root for full license information.
  
  // CAVEAT: This sample is to demonstrate azure IoT client concepts only and is not a guide design principles or style
  // Checking of return codes and error values shall be omitted for brevity.  Please practice sound engineering practices
  // when writing production code.
  #include <stdio.h>
  #include <stdlib.h>
  
  #include "iothub.h"
  #include "iothub_message.h"
  #include "iothub_client_version.h"
  #include "azure_c_shared_utility/threadapi.h"
  #include "azure_c_shared_utility/tickcounter.h"
  #include "azure_c_shared_utility/shared_util_options.h"
  #include "azure_c_shared_utility/http_proxy_io.h"
  
  #include "iothub_device_client_ll.h"
  #include "iothub_client_options.h"
  #include "azure_prov_client/prov_device_ll_client.h"
  #include "azure_prov_client/prov_security_factory.h"
  
  #ifdef SET_TRUSTED_CERT_IN_SAMPLES
  #include "certs.h"
  #endif // SET_TRUSTED_CERT_IN_SAMPLES
  
  //
  // The protocol you wish to use should be uncommented
  //
  #define SAMPLE_MQTT
  //#define SAMPLE_MQTT_OVER_WEBSOCKETS
  //#define SAMPLE_AMQP
  //#define SAMPLE_AMQP_OVER_WEBSOCKETS
  //#define SAMPLE_HTTP
  
  #ifdef SAMPLE_MQTT
  #include "iothubtransportmqtt.h"
  #include "azure_prov_client/prov_transport_mqtt_client.h"
  #endif // SAMPLE_MQTT
  #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS
  #include "iothubtransportmqtt_websockets.h"
  #include "azure_prov_client/prov_transport_mqtt_ws_client.h"
  #endif // SAMPLE_MQTT_OVER_WEBSOCKETS
  #ifdef SAMPLE_AMQP
  #include "iothubtransportamqp.h"
  #include "azure_prov_client/prov_transport_amqp_client.h"
  #endif // SAMPLE_AMQP
  #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS
  #include "iothubtransportamqp_websockets.h"
  #include "azure_prov_client/prov_transport_amqp_ws_client.h"
  #endif // SAMPLE_AMQP_OVER_WEBSOCKETS
  #ifdef SAMPLE_HTTP
  #include "iothubtransportmqtt.h"
  #include "azure_prov_client/prov_transport_http_client.h"
  #endif // SAMPLE_HTTP
  
  #ifdef SET_TRUSTED_CERT_IN_SAMPLES
  #include "certs.h"
  #endif // SET_TRUSTED_CERT_IN_SAMPLES
  
  // This sample is to demostrate iothub reconnection with provisioning and should not
  // be confused as production code
  
  MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(PROV_DEVICE_RESULT, PROV_DEVICE_RESULT_VALUE);
  MU_DEFINE_ENUM_STRINGS_WITHOUT_INVALID(PROV_DEVICE_REG_STATUS, PROV_DEVICE_REG_STATUS_VALUES);
  
  static const char* global_prov_uri = "global.azure-devices-provisioning.net";
  static const char* id_scope = "[ID Scope]";
  
  static bool g_use_proxy = false;
  static const char* PROXY_ADDRESS = "127.0.0.1";
  
  #define PROXY_PORT                  8888
  #define MESSAGES_TO_SEND            2
  #define TIME_BETWEEN_MESSAGES       2
  
  typedef struct CLIENT_SAMPLE_INFO_TAG
  {
      unsigned int sleep_time;
      char* iothub_uri;
      char* access_key_name;
      char* device_key;
      char* device_id;
      int registration_complete;
  } CLIENT_SAMPLE_INFO;
  
  typedef struct IOTHUB_CLIENT_SAMPLE_INFO_TAG
  {
      int connected;
      int stop_running;
  } IOTHUB_CLIENT_SAMPLE_INFO;
  
  static IOTHUBMESSAGE_DISPOSITION_RESULT receive_msg_callback(IOTHUB_MESSAGE_HANDLE message, void* user_context)
  {
      (void)message;
      IOTHUB_CLIENT_SAMPLE_INFO* iothub_info = (IOTHUB_CLIENT_SAMPLE_INFO*)user_context;
      (void)printf("Stop message recieved from IoTHub\r\n");
      iothub_info->stop_running = 1;
      return IOTHUBMESSAGE_ACCEPTED;
  }
  
  static void registration_status_callback(PROV_DEVICE_REG_STATUS reg_status, void* user_context)
  {
      (void)user_context;
      (void)printf("Provisioning Status: %s\r\n", MU_ENUM_TO_STRING(PROV_DEVICE_REG_STATUS, reg_status));
  }
  
  static void iothub_connection_status(IOTHUB_CLIENT_CONNECTION_STATUS result, IOTHUB_CLIENT_CONNECTION_STATUS_REASON reason, void* user_context)
  {
      (void)reason;
      if (user_context == NULL)
      {
          printf("iothub_connection_status user_context is NULL\r\n");
      }
      else
      {
          IOTHUB_CLIENT_SAMPLE_INFO* iothub_info = (IOTHUB_CLIENT_SAMPLE_INFO*)user_context;
          if (result == IOTHUB_CLIENT_CONNECTION_AUTHENTICATED)
          {
              iothub_info->connected = 1;
          }
          else
          {
              iothub_info->connected = 0;
              iothub_info->stop_running = 1;
          }
      }
  }
  
  static void register_device_callback(PROV_DEVICE_RESULT register_result, const char* iothub_uri, const char* device_id, void* user_context)
  {
      if (user_context == NULL)
      {
          printf("user_context is NULL\r\n");
      }
      else
      {
          CLIENT_SAMPLE_INFO* user_ctx = (CLIENT_SAMPLE_INFO*)user_context;
          if (register_result == PROV_DEVICE_RESULT_OK)
          {
              (void)printf("Registration Information received from service: %s!\r\n", iothub_uri);
              (void)mallocAndStrcpy_s(&user_ctx->iothub_uri, iothub_uri);
              (void)mallocAndStrcpy_s(&user_ctx->device_id, device_id);
              user_ctx->registration_complete = 1;
          }
          else
          {
              (void)printf("Failure encountered on registration %s\r\n", MU_ENUM_TO_STRING(PROV_DEVICE_RESULT, register_result) );
              user_ctx->registration_complete = 2;
          }
      }
  }
  
  int main()
  {
      SECURE_DEVICE_TYPE hsm_type;
      //hsm_type = SECURE_DEVICE_TYPE_TPM;
      hsm_type = SECURE_DEVICE_TYPE_X509;
      //hsm_type = SECURE_DEVICE_TYPE_SYMMETRIC_KEY;
  
      bool traceOn = false;
  
      (void)IoTHub_Init();
      (void)prov_dev_security_init(hsm_type);
      // Set the symmetric key if using they auth type
      //prov_dev_set_symmetric_key_info("<symm_registration_id>", "<symmetric_Key>");
  
      PROV_DEVICE_TRANSPORT_PROVIDER_FUNCTION prov_transport;
      HTTP_PROXY_OPTIONS http_proxy;
      CLIENT_SAMPLE_INFO user_ctx;
  
      memset(&http_proxy, 0, sizeof(HTTP_PROXY_OPTIONS));
      memset(&user_ctx, 0, sizeof(CLIENT_SAMPLE_INFO));
  
      // Protocol to USE - HTTP, AMQP, AMQP_WS, MQTT, MQTT_WS
  #ifdef SAMPLE_MQTT
      prov_transport = Prov_Device_MQTT_Protocol;
  #endif // SAMPLE_MQTT
  #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS
      prov_transport = Prov_Device_MQTT_WS_Protocol;
  #endif // SAMPLE_MQTT_OVER_WEBSOCKETS
  #ifdef SAMPLE_AMQP
      prov_transport = Prov_Device_AMQP_Protocol;
  #endif // SAMPLE_AMQP
  #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS
      prov_transport = Prov_Device_AMQP_WS_Protocol;
  #endif // SAMPLE_AMQP_OVER_WEBSOCKETS
  #ifdef SAMPLE_HTTP
      prov_transport = Prov_Device_HTTP_Protocol;
  #endif // SAMPLE_HTTP
  
      // Set ini
      user_ctx.registration_complete = 0;
      user_ctx.sleep_time = 10;
  
      printf("Provisioning API Version: %s\r\n", Prov_Device_LL_GetVersionString());
      printf("Iothub API Version: %s\r\n", IoTHubClient_GetVersionString());
  
      if (g_use_proxy)
      {
          http_proxy.host_address = PROXY_ADDRESS;
          http_proxy.port = PROXY_PORT;
      }
  
      PROV_DEVICE_LL_HANDLE handle;
      if ((handle = Prov_Device_LL_Create(global_prov_uri, id_scope, prov_transport)) == NULL)
      {
          (void)printf("failed calling Prov_Device_LL_Create\r\n");
      }
      else
      {
          if (http_proxy.host_address != NULL)
          {
              Prov_Device_LL_SetOption(handle, OPTION_HTTP_PROXY, &http_proxy);
          }
  
          Prov_Device_LL_SetOption(handle, PROV_OPTION_LOG_TRACE, &traceOn);
  #ifdef SET_TRUSTED_CERT_IN_SAMPLES
          // Setting the Trusted Certificate.  This is only necessary on system with without
          // built in certificate stores.
          Prov_Device_LL_SetOption(handle, OPTION_TRUSTED_CERT, certificates);
  #endif // SET_TRUSTED_CERT_IN_SAMPLES
  
          // This option sets the registration ID it overrides the registration ID that is 
          // set within the HSM so be cautious if setting this value
          //Prov_Device_LL_SetOption(handle, PROV_REGISTRATION_ID, "[REGISTRATION ID]");
  
          if (Prov_Device_LL_Register_Device(handle, register_device_callback, &user_ctx, registration_status_callback, &user_ctx) != PROV_DEVICE_RESULT_OK)
          {
              (void)printf("failed calling Prov_Device_LL_Register_Device\r\n");
          }
          else
          {
              do
              {
                  Prov_Device_LL_DoWork(handle);
                  ThreadAPI_Sleep(user_ctx.sleep_time);
              } while (user_ctx.registration_complete == 0);
          }
          Prov_Device_LL_Destroy(handle);
      }
  
      if (user_ctx.registration_complete != 1)
      {
          (void)printf("registration failed!\r\n");
      }
      else
      {
          IOTHUB_CLIENT_TRANSPORT_PROVIDER iothub_transport;
  
          // Protocol to USE - HTTP, AMQP, AMQP_WS, MQTT, MQTT_WS
  #if defined(SAMPLE_MQTT) || defined(SAMPLE_HTTP) // HTTP sample will use mqtt protocol
          iothub_transport = MQTT_Protocol;
  #endif // SAMPLE_MQTT
  #ifdef SAMPLE_MQTT_OVER_WEBSOCKETS
          iothub_transport = MQTT_WebSocket_Protocol;
  #endif // SAMPLE_MQTT_OVER_WEBSOCKETS
  #ifdef SAMPLE_AMQP
          iothub_transport = AMQP_Protocol;
  #endif // SAMPLE_AMQP
  #ifdef SAMPLE_AMQP_OVER_WEBSOCKETS
          iothub_transport = AMQP_Protocol_over_WebSocketsTls;
  #endif // SAMPLE_AMQP_OVER_WEBSOCKETS
  
          IOTHUB_DEVICE_CLIENT_LL_HANDLE device_ll_handle;
  
          (void)printf("Creating IoTHub Device handle\r\n");
          if ((device_ll_handle = IoTHubDeviceClient_LL_CreateFromDeviceAuth(user_ctx.iothub_uri, user_ctx.device_id, iothub_transport) ) == NULL)
          {
              (void)printf("failed create IoTHub client from connection string %s!\r\n", user_ctx.iothub_uri);
          }
          else
          {
              IOTHUB_CLIENT_SAMPLE_INFO iothub_info;
              TICK_COUNTER_HANDLE tick_counter_handle = tickcounter_create();
              tickcounter_ms_t current_tick;
              tickcounter_ms_t last_send_time = 0;
              size_t msg_count = 0;
              iothub_info.stop_running = 0;
              iothub_info.connected = 0;
  
              (void)IoTHubDeviceClient_LL_SetConnectionStatusCallback(device_ll_handle, iothub_connection_status, &iothub_info);
  
              // Set any option that are neccessary.
              // For available options please see the iothub_sdk_options.md documentation
  
              IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_LOG_TRACE, &traceOn);
  
  #ifdef SET_TRUSTED_CERT_IN_SAMPLES
              // Setting the Trusted Certificate.  This is only necessary on system with without
              // built in certificate stores.
              IoTHubDeviceClient_LL_SetOption(device_ll_handle, OPTION_TRUSTED_CERT, certificates);
  #endif // SET_TRUSTED_CERT_IN_SAMPLES
  
              (void)IoTHubDeviceClient_LL_SetMessageCallback(device_ll_handle, receive_msg_callback, &iothub_info);
  
              (void)printf("Sending 1 messages to IoTHub every %d seconds for %d messages (Send any message to stop)\r\n", TIME_BETWEEN_MESSAGES, MESSAGES_TO_SEND);
              do
              {
                  if (iothub_info.connected != 0)
                  {
                      // Send a message every TIME_BETWEEN_MESSAGES seconds
                      (void)tickcounter_get_current_ms(tick_counter_handle, &current_tick);
                      if ((current_tick - last_send_time) / 1000 > TIME_BETWEEN_MESSAGES)
                      {
                          static char msgText[1024];
                          sprintf_s(msgText, sizeof(msgText), "{ \"message_index\" : \"%zu\" }", msg_count++);
  
                          IOTHUB_MESSAGE_HANDLE msg_handle = IoTHubMessage_CreateFromByteArray((const unsigned char*)msgText, strlen(msgText));
                          if (msg_handle == NULL)
                          {
                              (void)printf("ERROR: iotHubMessageHandle is NULL!\r\n");
                          }
                          else
                          {
                              if (IoTHubDeviceClient_LL_SendEventAsync(device_ll_handle, msg_handle, NULL, NULL) != IOTHUB_CLIENT_OK)
                              {
                                  (void)printf("ERROR: IoTHubClient_LL_SendEventAsync..........FAILED!\r\n");
                              }
                              else
                              {
                                  (void)tickcounter_get_current_ms(tick_counter_handle, &last_send_time);
                                  (void)printf("IoTHubClient_LL_SendEventAsync accepted message [%zu] for transmission to IoT Hub.\r\n", msg_count);
  
                              }
                              IoTHubMessage_Destroy(msg_handle);
                          }
                      }
                  }
                  IoTHubDeviceClient_LL_DoWork(device_ll_handle);
                  ThreadAPI_Sleep(1);
              } while (iothub_info.stop_running == 0 && msg_count < MESSAGES_TO_SEND);
  
              size_t index = 0;
              for (index = 0; index < 10; index++)
              {
                  IoTHubDeviceClient_LL_DoWork(device_ll_handle);
                  ThreadAPI_Sleep(1);
              }
              tickcounter_destroy(tick_counter_handle);
              // Clean up the iothub sdk handle
              IoTHubDeviceClient_LL_Destroy(device_ll_handle);
          }
      }
      free(user_ctx.iothub_uri);
      free(user_ctx.device_id);
      prov_dev_security_deinit();
  
      // Free all the sdk subsystem
      IoTHub_Deinit();
  
      (void)printf("Press any enter to continue:\r\n");
      (void)getchar();
  
      return 0;
  }
  