#include <stdio.h>
#include <stdbool.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <esp_err.h>
#include "freertos/semphr.h"
#include "esp_system.h"
#include <string.h>    //strlen
#include "esp_wifi.h"
#include "esp_event_loop.h"
#include "freertos/event_groups.h"
#include "esp_log.h"
#include "lwip/err.h"
#include "lwip/sockets.h"
#include "lwip/sys.h"
#include "lwip/netdb.h"
#include "lwip/dns.h"
#include "nvs_flash.h"
#include "driver/gpio.h"
#include<driver/adc.h>
#include "cJSON.h"

#include "mqtt_client.h" //provides important functions to connect with MQTT
//#include "protocol_examples_common.h" //important for running different protocols in code
#include "esp_event.h" //managing events of mqtt

//PROTÓTIPO DE FUNÇÕES=====================================================================
void app_main(void);
void setup();
void task_control_led(void *pvParameters);
void wifi_connect(void);

static void mqtt_event_handler(esp_mqtt_event_handle_t event);
static void mqtt_initialize(void);

//DEFINIÇÕES E CONSTATNTES=================================================================
#define delay(value) vTaskDelay(value/portTICK_PERIOD_MS)

#define LED_AZUL GPIO_NUM_23 // LED

enum estados_led {ON, OFF, INIC};
#define SSID "Wokwi-GUEST"
#define PASSPHARSE ""
static EventGroupHandle_t wifi_event_group;
const int CONNECTED_BIT = BIT0;
static const char *TAG="LOG_MQTT";

//MÁSCARA PARA SAÍDAS====================================================================
#define GPIO_OUTPUT_PIN_SEL 1ULL<<LED_AZUL 

//VARIÁVEIS==============================================================================
static TaskHandle_t xtask_handle_led = NULL;

esp_mqtt_client_handle_t client;

enum estados_led estado;
char msg_cmd[16] = "";
char *msg_sended = " ";
SemaphoreHandle_t mutex;

//FUNÇÕES E TASKS=======================================================================
void wifi_connect() {
  wifi_config_t cfg = {
    .sta = {
        .ssid = SSID,
        .password = PASSPHARSE,
    },
  };
  ESP_ERROR_CHECK(esp_wifi_disconnect());
  ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &cfg));
  ESP_ERROR_CHECK(esp_wifi_connect());
}

static esp_err_t event_handler(void *ctx, system_event_t *event) {
  switch(event->event_id) {
  case SYSTEM_EVENT_STA_START:
      wifi_connect();
      break;
  case SYSTEM_EVENT_STA_GOT_IP:
      xEventGroupSetBits(wifi_event_group, CONNECTED_BIT);
      break;
  case SYSTEM_EVENT_STA_DISCONNECTED:
      esp_wifi_connect();
      xEventGroupClearBits(wifi_event_group, CONNECTED_BIT);
      break;
  default:
      break;
  }
  return ESP_OK;
}

static void initialise_wifi(void) {
  esp_log_level_set("wifi", ESP_LOG_NONE); // disable wifi driver logging
  tcpip_adapter_init();
  wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
  ESP_ERROR_CHECK(esp_wifi_init(&cfg));
  ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));
  ESP_ERROR_CHECK(esp_wifi_start());
}

void task_control_led(void *pvParameters) {
  while(true){    
    if(estado == ON) {        
      gpio_set_level(LED_AZUL, 1);
      printf("Led ligado\n");          
    } 
    else if(estado == OFF) {
      gpio_set_level(LED_AZUL, 0);
      printf("Led desligado\n");   
    }      
    else {
      gpio_set_level(LED_AZUL, 0);
      printf("Led desligado\n"); 
    }
    vTaskSuspend(NULL);         
  }  
}

static void mqtt_event_handler(esp_mqtt_event_handle_t event){ //here esp_mqtt_event_handle_t is a struct which receieves struct event from mqtt app start funtion
    esp_mqtt_client_handle_t client = event->client; //making obj client of struct esp_mqtt_client_handle_t and assigning it the receieved event client
    if(event->event_id == MQTT_EVENT_CONNECTED) {
      ESP_LOGI(TAG, "MQTT_EVENT_CONNECTED");
      printf("conectado... mqtt\n");
    }
    else if(event->event_id == MQTT_EVENT_DISCONNECTED)
    {
      ESP_LOGI(TAG, "MQTT_EVENT_DISCONNECTED"); //if disconnected
    }
    else if(event->event_id == MQTT_EVENT_SUBSCRIBED)
    {
        ESP_LOGI(TAG, "MQTT_EVENT_SUBSCRIBED");
    }
    else if(event->event_id == MQTT_EVENT_UNSUBSCRIBED) //when subscribed
    {
        ESP_LOGI(TAG, "MQTT_EVENT_UNSUBSCRIBED");
    }
    else if(event->event_id == MQTT_EVENT_DATA)//when unsubscribed
    {
      xSemaphoreTake(mutex, portMAX_DELAY);// pega o Mutex
      ESP_LOGI(TAG, "MQTT_EVENT_DATA");          
      char delimitadores[] = "\":}";     
      char * resultado = strtok(event->data, delimitadores);
      resultado = strtok(NULL, delimitadores);
      resultado = strtok(NULL, delimitadores);      
      printf("Mensagem recebida -> %s\n", resultado);     

      //função para mudar o estado do led
      if (strcmp(resultado,"1") == 0) {
        estado = ON;
      }
      else if (strcmp(resultado,"0") == 0) {
          estado = OFF;
      }
      else {
        estado = OFF;
      }
      vTaskResume(xtask_handle_led); //reativa a task dos leds     
      xSemaphoreGive(mutex); //libera o Mutex  
    }
    else if(event->event_id == MQTT_EVENT_ERROR)//when any error
    {
        ESP_LOGI(TAG, "MQTT_EVENT_ERROR");
    }
}
static void mqtt_initialize(void) {/*Depending on your website or cloud there could be more parameters in mqtt_cfg.*/
    esp_log_level_set(TAG, ESP_LOG_VERBOSE);
    const esp_mqtt_client_config_t mqtt_cfg = {
      .username= "adriano_soares:aafb08",
      .host= "200.129.71.138",//"broker.hivemq.com", //Uniform Resource Identifier includes path,protocol
      .port = 1883,
      .transport = MQTT_TRANSPORT_OVER_TCP,
      .event_handle=mqtt_event_handler //described above event handler
    };
    client=esp_mqtt_client_init(&mqtt_cfg); //sending struct as a parameter in init client function
    esp_mqtt_client_start(client); //starting the process
}

void setup() {
  //CONFIGURANDO SAÍDAS===================================================
	gpio_config_t output_config = {};//variável descritora
	output_config.intr_type = GPIO_INTR_DISABLE;//desabilita interrupção externa
	output_config.mode = GPIO_MODE_OUTPUT;//configura GPIO como saídas
	output_config.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;//carrega GPIOs configuradas
	output_config.pull_up_en = 0;//desativa pull_up
	output_config.pull_down_en = 0;//desativa pull_down 
	gpio_config(&output_config);//configura GPIO conforme descritor
  //estado inicial do led
  estado = INIC;  

  ESP_ERROR_CHECK(esp_event_loop_init(event_handler, NULL));
  wifi_event_group = xEventGroupCreate();
  esp_err_t ret = nvs_flash_init();
  if (ret == ESP_ERR_NVS_NO_FREE_PAGES) {
    ESP_ERROR_CHECK(nvs_flash_erase());
    ret = nvs_flash_init();
  }
  ESP_ERROR_CHECK( ret );
  initialise_wifi();
  xEventGroupWaitBits(wifi_event_group,CONNECTED_BIT,false,true,portMAX_DELAY);
  mqtt_initialize();
}

void app_main() {
  setup();   
  mutex = xSemaphoreCreateMutex();
  int msg_sub_id = esp_mqtt_client_subscribe(client, "adriano_soares:aafb08/config", 0);  
  xTaskCreate(&task_control_led, "LEDs", 2048, NULL, 3, &xtask_handle_led);        
}