// Learn about the ESP32 WiFi simulation in
#include <TimeLib.h>
#include <DHT.h>
#include <WiFi.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#include <freertos/queue.h>
#include "P.h"

SemaphoreHandle_t xSerialSem;
QueueHandle_t xFila;
SemaphoreHandle_t xSerialSem2;

TaskHandle_t Task1;
TaskHandle_t Task2;
TaskHandle_t Task3;
TaskHandle_t Task4;
TaskHandle_t Task5;

// LED pins
const int led1 = 2;
const int led2 = 4;
int connected = 0;

DHT dht(12, DHT22);

LiquidCrystal_I2C LCD = LiquidCrystal_I2C(0x27, 16, 2);

#define NTP_SERVER "pool.ntp.org"
#define UTC_OFFSET 0
#define UTC_OFFSET_DST 0
#define SPEAKER_PIN 5

int tamFila = 200;



void setup()
{
  Serial.begin(115200);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(SPEAKER_PIN, OUTPUT);

  // Cria o semáforo na memória
  if (xSerialSem == NULL)
  {
    xSerialSem = xSemaphoreCreateMutex();
    if (xSerialSem != NULL)
      xSemaphoreGive(xSerialSem);
  }

    // Cria o semáforo na memória
  if (xSerialSem2 == NULL)
  {
    xSerialSem2 = xSemaphoreCreateMutex();
    if (xSerialSem2 != NULL)
      xSemaphoreGive(xSerialSem2);
  }

  xFila = xQueueCreate( tamFila, sizeof( String ) );
  if(xFila == NULL){
  Serial.println("Erro criando fila");
  }


  LCD.init();
  LCD.backlight();

  
  
 

  // create a task that will be executed in the Task1code() function,
  // with priority 1 and executed on core 0
  xTaskCreatePinnedToCore(
      Task1code, /* Task function. */
      "Task1",   /* name of task. */
      10000,     /* Stack size of task */
      NULL,      /* parameter of the task */
      1,         /* priority of the task */
      &Task1,    /* Task handle to keep track of created task */
      0);        /* pin task to core 0 */

  // create a task that will be executed in the Task2code() function
  // with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
      Task2code, /* Task function. */
      "Task2",   /* name of task. */
      10000,     /* Stack size of task */
      NULL,      /* parameter of the task */
      1,         /* priority of the task */
      &Task2,    /* Task handle to keep track of created task */
      0);        /* pin task to core 1 */

  // create a task that will be executed in the Task2code() function
  // with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
      Task3code, /* Task function. */
      "Task3",   /* name of task. */
      10000,     /* Stack size of task */
      NULL,      /* parameter of the task */
      1,         /* priority of the task */
      &Task3,    /* Task handle to keep track of created task */
      1);        /* pin task to core 1 */

  // create a task that will be executed in the Task2code() function
  // with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
      Task4code, /* Task function. */
      "Task4",   /* name of task. */
      10000,     /* Stack size of task */
      NULL,      /* parameter of the task */
      1,         /* priority of the task */
      &Task4,    /* Task handle to keep track of created task */
      1);        /* pin task to core 1 */

  // create a task that will be executed in the Task2code() function
  // with priority 1 and executed on core 1
  xTaskCreatePinnedToCore(
      Task5code, /* Task function. */
      "Task5",   /* name of task. */
      10000,     /* Stack size of task */
      NULL,      /* parameter of the task */
      1,         /* priority of the task */
      &Task5,    /* Task handle to keep track of created task */
      0);        /* pin task to core 1 */
}

// Task1code: blinks an LED every 1000 ms
void Task1code(void *pvParameters)
{
  Serial.print("Task1 running on core ");
  Serial.println(xPortGetCoreID());
  for (;;)
  {
    digitalWrite(led1, HIGH);
    vTaskDelay(1000);
    digitalWrite(led1, LOW);
    vTaskDelay(1000);
  }
}
// Task2code: blinks an LED every 700 ms
void Task2code(void *pvParameters)
{
  Serial.print("Task2 running on core ");
  Serial.println(xPortGetCoreID());
  for (;;)
  {
    digitalWrite(led2, HIGH);
    vTaskDelay(700);
    digitalWrite(led2, LOW);
    vTaskDelay(700);
  }
}

// Task3code:
void Task3code(void *pvParameters)
{
  Serial.print("Task3 running on core ");
  Serial.println(xPortGetCoreID());
  
  for (;;)
  {
 
    if( connected == 1){
      if (xSemaphoreTake(xSerialSem, (TickType_t)5 == pdTRUE)){
        
          Serial.println("Task 3");
          Serial.println("Temp: " + String(dht.readTemperature(), 1) + " \260C");
          Serial.println("Hum: " + String(dht.readHumidity(), 1) + " %");

          if (xSemaphoreTake(xSerialSem2, (TickType_t)10 == pdTRUE)){

                String minhaString  = String(dht.readTemperature(), 1) + " \260C";

                xQueueSend(xFila, (void *)&minhaString, portMAX_DELAY);

                
            xSemaphoreGive(xSerialSem2);
          
          } 

          printLocalTime();
          vTaskDelay(1000);
          xSemaphoreGive(xSerialSem);

        }
    }else{
      vTaskDelay(700);
    }
  }
}

// Task3code:
void Task4code(void *pvParameters)
{
  Serial.print("Task4 running on core ");
  Serial.println(xPortGetCoreID());
  
  for (;;)
  {
    if( connected == 0){
      if (xSemaphoreTake(xSerialSem, (TickType_t)5 == pdTRUE)){
          
          Serial.println("Task 4");

          LCD.setCursor(0, 0);
          LCD.print("Connecting to ");
          LCD.setCursor(0, 1);
          LCD.print("WiFi ");

          WiFi.begin("Wokwi-GUEST", "", 6);
          while (WiFi.status() != WL_CONNECTED)
          {
            vTaskDelay(10);
            spinner();
          }

          Serial.println("");
          Serial.println("WiFi connected");
          Serial.print("IP address: ");
          Serial.println(WiFi.localIP());

          LCD.clear();
          LCD.setCursor(0, 0);
          LCD.println("Online");
          LCD.setCursor(0, 1);
          LCD.println("Updating time...");

          connected = 1;

          configTime(UTC_OFFSET, UTC_OFFSET_DST, NTP_SERVER);
          
          xSemaphoreGive(xSerialSem);
          Serial.print("Status: ");
          Serial.println(connected);

            tone(SPEAKER_PIN, NOTE_E4);
            vTaskDelay(150);
            tone(SPEAKER_PIN, NOTE_G4);
            vTaskDelay(150);
            tone(SPEAKER_PIN, NOTE_E5);
            vTaskDelay(150);
            tone(SPEAKER_PIN, NOTE_C5);
            vTaskDelay(150);
            tone(SPEAKER_PIN, NOTE_D5);
            vTaskDelay(150);
            tone(SPEAKER_PIN, NOTE_G5);
            vTaskDelay(150);
            noTone(SPEAKER_PIN);

          
      }

  }

  vTaskDelay(1000);
}
}


// Task5code: blinks an LED every 700 ms
void Task5code(void *pvParameters)
{
  Serial.print("Task5 running on core ");
  Serial.println(xPortGetCoreID());
  for (;;)
  {
     if (xSemaphoreTake(xSerialSem2, (TickType_t)10 == pdTRUE)){

        struct tm timeinfo;

        if (getLocalTime(&timeinfo)) {

        char buffer[50]; // Buffer para armazenar a hora formatada
        char *formato = "%Y-%m-%d %H:%M:%S";

          // Utilizar strftime para formatar a hora
          strftime(buffer, sizeof(buffer), formato, &timeinfo);  
          // Imprimir a hora formatada no Serial
          Serial.println(buffer);      
        }
        Serial.println("Imprimindo e limpando a fila:");

        while (uxQueueMessagesWaiting(xFila) > 0) {
          String mensagem;
          if (xQueueReceive(xFila, &mensagem, portMAX_DELAY) == pdPASS) {
            Serial.print(mensagem);
            Serial.print("|");;
          }
        }

        Serial.println("Fila limpa!");


      xSemaphoreGive(xSerialSem2);
     
    } 

     vTaskDelay(15000);
    
  }
}

void loop()
{
}


void spinner()
{
  static int8_t counter = 0;
  const char *glyphs = "\xa1\xa5\xdb";
  LCD.setCursor(15, 1);
  LCD.print(glyphs[counter++]);
  if (counter == strlen(glyphs))
  {
    counter = 0;
  }
}

void printLocalTime()
{
  struct tm timeinfo;
  
    if (!getLocalTime(&timeinfo)){
      LCD.setCursor(0, 1);
      LCD.println("Connection Err");
      connected = 0;
      return;
    }

      LCD.setCursor(8, 0);
      LCD.println(&timeinfo, "%H:%M:%S");
      
    tone(SPEAKER_PIN, NOTE_G5);
    vTaskDelay(10);
    noTone(SPEAKER_PIN);
    
    LCD.setCursor(0, 1);
    LCD.println(&timeinfo, "%d/%m/%Y   %Z");

}