/**
  ******************************************************************************
  * @file    mqtt-example.c
  * @author  MCD Application Team
  * @brief   This file provides mqtt example using Ali IoT kit.
  ******************************************************************************
  * @attention
  *
  * <h2><center>&copy; Copyright (c) 2016 STMicroelectronics International N.V. 
  * All rights reserved.</center></h2>
  *
  * Redistribution and use in source and binary forms, with or without 
  * modification, are permitted, provided that the following conditions are met:
  *
  * 1. Redistribution of source code must retain the above copyright notice, 
  *    this list of conditions and the following disclaimer.
  * 2. Redistributions in binary form must reproduce the above copyright notice,
  *    this list of conditions and the following disclaimer in the documentation
  *    and/or other materials provided with the distribution.
  * 3. Neither the name of STMicroelectronics nor the names of other 
  *    contributors to this software may be used to endorse or promote products 
  *    derived from this software without specific written permission.
  * 4. This software, including modifications and/or derivative works of this 
  *    software, must execute solely and exclusively on microcontroller or
  *    microprocessor devices manufactured by or for STMicroelectronics.
  * 5. Redistribution and use of this software other than as permitted under 
  *    this license is void and will automatically terminate your rights under 
  *    this license. 
  *
  * THIS SOFTWARE IS PROVIDED BY STMICROELECTRONICS AND CONTRIBUTORS "AS IS" 
  * AND ANY EXPRESS, IMPLIED OR STATUTORY WARRANTIES, INCLUDING, BUT NOT 
  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 
  * PARTICULAR PURPOSE AND NON-INFRINGEMENT OF THIRD PARTY INTELLECTUAL PROPERTY
  * RIGHTS ARE DISCLAIMED TO THE FULLEST EXTENT PERMITTED BY LAW. IN NO EVENT 
  * SHALL STMICROELECTRONICS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, 
  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 
  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 
  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  *
  ******************************************************************************
  */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>

#include "iot_import.h"
#include "iot_export.h"
#include "lite-log.h"
#include "wifi.h"
#include "sensors_data.h"
#include "msg.h"
#include "led_data.h"
#include "iot_flash_config.h"

#include "stm32l4xx_hal.h"

#define PRODUCT_KEY             "a1rHcreXXXX"
#define DEVICE_NAME             "iotkit-xxxx-1"
#define DEVICE_SECRET           "vZfv3I1SWXQPxmbgibLE5bauKosUXXXX"


// These are pre-defined topics
#define TOPIC_UPDATE            "/"PRODUCT_KEY"/"DEVICE_NAME"/update"
#define TOPIC_ERROR             "/"PRODUCT_KEY"/"DEVICE_NAME"/update/error"
#define TOPIC_GET               "/"PRODUCT_KEY"/"DEVICE_NAME"/get"
#define TOPIC_DATA              "/"PRODUCT_KEY"/"DEVICE_NAME"/data"

#define MSG_LEN_MAX             (2048)
#define MAX_MQTT_PUBLISH_COUNT   3000

static void *pclient;
static int mqtt_client_example(void);
static void event_handle_mqtt(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg);
static void _demo_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg);

static void event_handle_mqtt(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
  uintptr_t packet_id = (uintptr_t)msg->msg;
  iotx_mqtt_topic_info_pt topic_info = (iotx_mqtt_topic_info_pt)msg->msg;
  
  switch (msg->event_type) {
  case IOTX_MQTT_EVENT_UNDEF:
    msg_info("undefined event occur.");
    break;
    
  case IOTX_MQTT_EVENT_DISCONNECT:
    msg_info("MQTT disconnect.");
    break;
    
  case IOTX_MQTT_EVENT_RECONNECT:
    msg_info("MQTT reconnect.");
    break;
    
  case IOTX_MQTT_EVENT_SUBCRIBE_SUCCESS:
    msg_info("subscribe success, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_SUBCRIBE_TIMEOUT:
    msg_info("subscribe wait ack timeout, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_SUBCRIBE_NACK:
    msg_info("subscribe nack, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_UNSUBCRIBE_SUCCESS:
    msg_info("unsubscribe success, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_UNSUBCRIBE_TIMEOUT:
    msg_info("unsubscribe timeout, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_UNSUBCRIBE_NACK:
    msg_info("unsubscribe nack, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_PUBLISH_SUCCESS:
    msg_info("publish success, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_PUBLISH_TIMEOUT:
    msg_info("publish timeout, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_PUBLISH_NACK:
    msg_info("publish nack, packet-id=%u", (unsigned int)packet_id);
    break;
    
  case IOTX_MQTT_EVENT_PUBLISH_RECVEIVED:
    msg_info("topic message arrived but without any related handle: topic=%.*s, topic_msg=%.*s",
                  topic_info->topic_len,
                  topic_info->ptopic,
                  topic_info->payload_len,
                  topic_info->payload);
    break;
    
  default:
    msg_info("Should NOT arrive here.");
    break;
  }
}

static void _demo_message_arrive(void *pcontext, void *pclient, iotx_mqtt_event_msg_pt msg)
{
  iotx_mqtt_topic_info_pt ptopic_info = (iotx_mqtt_topic_info_pt) msg->msg;
  
  // print topic name and topic message
  msg_info("----");
  msg_info("Topic: '%.*s' (Length: %d)",
                ptopic_info->topic_len,
                ptopic_info->ptopic,
                ptopic_info->topic_len);
  msg_info("Payload: '%.*s' (Length: %d)",
                ptopic_info->payload_len,
                ptopic_info->payload,
                ptopic_info->payload_len);
  msg_info("----");
}

int mqtt_client_example(void)
{
  int rc = 0, cnt = 0;
  iotx_conn_info_pt pconn_info;
  iotx_mqtt_param_t mqtt_params;
  iotx_mqtt_topic_info_t topic_msg;
  char msg_pub[512]={0};
  char *msg_buf = NULL, *msg_readbuf = NULL;
  uint8_t bp_pushed;
  char ledstate[] = { "Off" };
  
#ifdef ENABLE_SENSOR
  int res = init_sensors();
  if(0 != res)
  {
    msg_info("init_sensors returned error : %d\n", res);
    goto do_exit;
  }
#endif
  
  if (NULL == (msg_buf = (char *)HAL_Malloc(MSG_LEN_MAX))) {
    msg_info("not enough memory");
    rc = -1;
    goto do_exit;
  }
  
  if (NULL == (msg_readbuf = (char *)HAL_Malloc(MSG_LEN_MAX))) {
    msg_info("not enough memory");
    rc = -1;
    goto do_exit;
  }
  
  /* Device AUTH */
  if (0 != IOT_SetupConnInfo(PRODUCT_KEY, DEVICE_NAME, DEVICE_SECRET, (void **)&pconn_info)) {
    msg_info("AUTH request failed!");
    rc = -1;
    goto do_exit;
  }
  
  /* Initialize MQTT parameter */
  memset(&mqtt_params, 0x0, sizeof(mqtt_params));
  
  mqtt_params.port = pconn_info->port;
  mqtt_params.host = pconn_info->host_name;
  mqtt_params.client_id = pconn_info->client_id;
  mqtt_params.username = pconn_info->username;
  mqtt_params.password = pconn_info->password;
  mqtt_params.pub_key = pconn_info->pub_key;
  
  mqtt_params.request_timeout_ms = 5000;
  mqtt_params.clean_session = 0;
  mqtt_params.keepalive_interval_ms = 120000;
  mqtt_params.pread_buf = msg_readbuf;
  mqtt_params.read_buf_size = MSG_LEN_MAX;
  mqtt_params.pwrite_buf = msg_buf;
  mqtt_params.write_buf_size = MSG_LEN_MAX;
  
  mqtt_params.handle_event.h_fp = event_handle_mqtt;
  mqtt_params.handle_event.pcontext = NULL;
  
  /* Construct a MQTT client with specify parameter */
  pclient = IOT_MQTT_Construct(&mqtt_params);
  if (NULL == pclient) {
    msg_info("MQTT construct failed");
    rc = -1;
    goto do_exit;
  }
  
  /* Subscribe the specific topic */
  rc = IOT_MQTT_Subscribe(pclient, TOPIC_DATA, IOTX_MQTT_QOS1, _demo_message_arrive, NULL);
  
  if (rc < 0) {
    IOT_MQTT_Destroy(&pclient);
    msg_info("IOT_MQTT_Subscribe() failed, rc = %d", rc);
    rc = -1;
    goto do_exit;
  }
  
  /* Initialize topic information */
  topic_msg.qos = IOTX_MQTT_QOS1;
  topic_msg.retain = 0;
  topic_msg.dup = 0;
  
  do {    
    printf("\n*************************************************************************************************************\n");
    printf("Press the User button (Blue) to publish LED desired value on the %s topic\n", TOPIC_DATA);
    printf("*************************************************************************************************************\n\n");
    bp_pushed = Button_WaitForPush(1000);
    
    if (bp_pushed != BP_NOT_PUSHED)
    {
      if(strstr(ledstate, "Off")!= NULL)
      {
        strcpy(ledstate, "On");
        Led_On();
      }
      else
      {
        strcpy(ledstate, "Off");
        Led_Off();
      }
    
      printf("Sending the desired LED state to Aliyun.\n");
      
      /* Generate topic message */
      
      memset(msg_pub, 0, sizeof(msg_pub));
      strcat(msg_pub, "\n{\n\"state\":{\n  \"desired\":");
      strcat(msg_pub, "{\n    \"LED_value\":\"");
      strcat(msg_pub, ledstate);
      strcat(msg_pub, "\"\n    }\n");
      strcat(msg_pub, "  }\n}\n");

      topic_msg.payload = (void *)msg_pub;
      topic_msg.payload_len = strlen(msg_pub);
      
      rc = IOT_MQTT_Publish(pclient, TOPIC_DATA, &topic_msg);
      
      if (rc < 0) {
        msg_info("error occur when publish LED status");
        rc = -1;
        break;
      }
	  
      cnt ++;
      msg_info("packet-id=%u, publish topic msg=%s", (uint32_t)rc, msg_pub);
    }
	
#ifdef ENABLE_SENSOR
    prepare_mqtt_payload(msg_pub, sizeof(msg_pub),NULL);
    topic_msg.payload = (void *)msg_pub;
    topic_msg.payload_len = strlen(msg_pub);
    
    rc = IOT_MQTT_Publish(pclient, TOPIC_DATA, &topic_msg);
    
    if (rc < 0) {
      msg_info("error occur when publish sensor data");
      rc = -1;
      break;
    }
	
    cnt ++;
    msg_info("packet-id=%u, publish topic msg=%s", (uint32_t)rc, msg_pub);
#endif
    
    /* handle the MQTT packet received from TCP or SSL connection */
    IOT_MQTT_Yield(pclient, 1000);
  } while (cnt < MAX_MQTT_PUBLISH_COUNT);
  
  IOT_MQTT_Unsubscribe(pclient, TOPIC_DATA);
  HAL_SleepMs(200);
  IOT_MQTT_Destroy(&pclient);
  
do_exit:

  if (NULL != msg_buf) {
    HAL_Free(msg_buf);
  }
  
  if (NULL != msg_readbuf) {
    HAL_Free(msg_readbuf);
  }
  
  msg_info("mqtt example loop end!");
  
  return rc;
}
           
int cloud_test(int argc, char *argv[])
{
  int ret = 0;
  
  uint8_t  MAC_Addr[6]; 
  uint8_t  IP_Addr[4];
 
  const char *ssid;
  const char  *psk;
  
  WIFI_Ecn_t security_mode;
  bool skip_reconf = false;

  printf("\n*** start WiFi provisioning ***\n\n");

  // wifi provisioning
  skip_reconf = (checkWiFiCredentials(&ssid, &psk, (uint8_t *) &security_mode) == HAL_OK);

  if (skip_reconf == true)
  {
    printf("Push the User button (Blue) within the next 2 seconds if you want to update"
           " the WiFi network configuration.\n\n");

    skip_reconf = (Button_WaitForPush(2000) == BP_NOT_PUSHED);
  }
  
  if (skip_reconf == false)
  {
    printf("Your WiFi parameters need to be entered to proceed.\n");
    do
    {
      updateWiFiCredentials(&ssid, &psk, (uint8_t *) &security_mode);
    } while (checkWiFiCredentials(&ssid, &psk, (uint8_t *) &security_mode) != HAL_OK);
  }

  printf("\n*** start WiFi initialization ***\n\n");
  
  if(WIFI_Init() ==  WIFI_STATUS_OK)
  {
    printf("> WiFi module initialized.\n\n");
    
    if(WIFI_GetMAC_Address(MAC_Addr) == WIFI_STATUS_OK)
    {
      printf("> WiFi module MAC address is: %X:%X:%X:%X:%X:%X\n\n",     
             MAC_Addr[0],
             MAC_Addr[1],
             MAC_Addr[2],
             MAC_Addr[3],
             MAC_Addr[4],
             MAC_Addr[5]);   
    }
    else
    {
      printf("> ERROR : CANNOT get MAC address.\n\n");  
      ret  = -1;
      goto EXIT;
    }

    ret = -1;
    
    if(WIFI_Connect(ssid, psk, security_mode) == WIFI_STATUS_OK)
    {
      ret = 0;
    }
        
    if(ret == 0)
    {
      printf("> WiFi module connected to access point.\n\n");
      if(WIFI_GetIP_Address(IP_Addr) == WIFI_STATUS_OK)
      {
        printf("> WiFi module got IP address : %d.%d.%d.%d\n\n",     
               IP_Addr[0],
               IP_Addr[1],
               IP_Addr[2],
               IP_Addr[3]); 
        
        mqtt_client_example();
      }
      
    }
    else
    {
      printf("WiFi connection error");
    }
    return 0;
  }
  
EXIT:
    
  return ret;
}

/* USER CODE END 0 */
