#include <Arduino_FreeRTOS.h>
#include <queue.h>
#include <task.h>
#include <semphr.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>

// Definições para o LCD
#define LCD_16X2_I2C_ADDRESS    0x27
#define LCD_16X2_COLS           16
#define LCD_16X2_ROWS           2

// Definição do pino do LED
#define LED_PIN 12

// Configurações ADC
#define ADC_MAX 1023.0
#define MAX_VOLTAGE_ADC 5.0

// Caracteres customizados para a exibição de níveis de bateria
byte battery_states[8][8] = {
  {B01110, B10001, B10001, B10001, B10001, B10001, B10001, B11111},  // Vazio
  {B01110, B10001, B10001, B10001, B10001, B10001, B11111, B11111},  // 1/4
  {B01110, B10001, B10001, B10001, B10001, B11111, B11111, B11111},  // 1/2
  {B01110, B10001, B10001, B10001, B11111, B11111, B11111, B11111},  // 3/4
  {B01110, B10001, B10001, B11111, B11111, B11111, B11111, B11111},  // Cheio
  {B01110, B10001, B11111, B11111, B11111, B11111, B11111, B11111},  // Sobrecarga
  {B01110, B11111, B11111, B11111, B11111, B11111, B11111, B11111},  // Muito baixa
  {B01110, B10001, B10101, B10101, B10101, B10101, B10001, B11111}   // Contorno
};

// Protótipos das tarefas
void task_breathing_light(void *pvParameters);
void task_serial(void *pvParameters);
void task_lcd(void *pvParameters);
void task_sensor(void *pvParameters);
void task_led(void *pvParameters);

// Instância do LCD
LiquidCrystal_I2C lcd(LCD_16X2_I2C_ADDRESS, LCD_16X2_COLS, LCD_16X2_ROWS);

// Handles para filas e semáforos
QueueHandle_t xQueue_LCD;
SemaphoreHandle_t xSerial_semaphore;

void setup() {
  Serial.begin(9600);
  while (!Serial) // Aguarda a inicialização da porta serial
    ;

  lcd.init();

  // Cria os caracteres customizados
  for (int i = 0; i < 8; i++) {
    lcd.createChar(i, battery_states[i]);
  }

  lcd.backlight();
  lcd.clear();

  lcd.setCursor(0, 0);
  lcd.print("VOLTIMETRO ===");

  pinMode(LED_PIN, OUTPUT);
  digitalWrite(LED_PIN, LOW);

  xQueue_LCD = xQueueCreate(1, sizeof(float));
  xSerial_semaphore = xSemaphoreCreateMutex();

  xTaskCreate(task_sensor, "SENSOR", 128, NULL, 3, NULL);
  xTaskCreate(task_lcd, "LCD", 156, NULL, 2, NULL);
  xTaskCreate(task_led, "LED", 128, NULL, 1, NULL);
}

void loop() {
  // Nada a fazer no loop principal quando usar o FreeRTOS
}

void task_sensor(void *pvParameters) {
  int adc_read = 0;
  float voltage = 0.0;
  while (1) {
    adc_read = analogRead(0);
    voltage = (adc_read / ADC_MAX) * MAX_VOLTAGE_ADC;
    xQueueOverwrite(xQueue_LCD, &voltage);
    vTaskDelay(pdMS_TO_TICKS(1000));
  }
}

void task_lcd(void *pvParameters) {
  float voltage_rcv = 0.0;
  while (1) {
    xQueueReceive(xQueue_LCD, &voltage_rcv, portMAX_DELAY);
    update_displays(voltage_rcv);
    vTaskDelay(pdMS_TO_TICKS(100));
  }
}

void task_led(void *pvParameters) {
  while (1) {
    digitalWrite(LED_PIN, HIGH);
    vTaskDelay(pdMS_TO_TICKS(250));
    digitalWrite(LED_PIN, LOW);
    vTaskDelay(pdMS_TO_TICKS(250));
  }
}

void update_displays(float voltage) {
  // Mapeia 0-5V para 0-6 (índices do array)
  int battery_state = static_cast<int>(map_float(voltage, 0.0, 5.0, 0, 6));
  lcd_battery_status(battery_state, 0); // Exibe o status da bateria na primeira linha
  lcd.setCursor(0, 1);
  lcd.print("Tensao: ");
  lcd.setCursor(11, 1);
  lcd.print(voltage, 2); // Exibe com duas casas decimais
  lcd.print("V ");
}

void lcd_battery_status(int state, int row) {
  lcd.setCursor(15, row); // Posiciona no final da primeira linha
  lcd.write(state); // Escreve o estado da bateria como um caractere customizado
}

float map_float(float x, float in_min, float in_max, float out_min, float out_max) {
  return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}