#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// ====== CONFIGURAÇÕES ======
const char* SSID = "Wokwi-GUEST";
const char* PASSWORD = "";
const char* BROKER_MQTT = "52.67.252.34";
const int BROKER_PORT = 1883;
const char* ID_MQTT = "smartlab_001";
// ====== TÓPICOS MQTT ======
const char* TOPICO_SLOT1 = "/iot/slot1";
const char* TOPICO_SLOT2 = "/iot/slot2";
const char* TOPICO_SLOT3 = "/iot/slot3";
const char* TOPICO_SLOT4 = "/iot/slot4";
// ====== OBJETOS ======
WiFiClient espClient;
PubSubClient MQTT(espClient);
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo servo1, servo2, servo3, servo4;
// ====== PINOS DOS SERVOS ======
const int pinoServo1 = 13;
const int pinoServo2 = 12;
const int pinoServo3 = 14;
const int pinoServo4 = 27;
// ====== PINOS I2C ======
#define SDA_PIN 21
#define SCL_PIN 22
// ====== ESTOQUE SIMULADO ======
int estoque[4] = {3, 2, 1, 4}; // Estoque inicial dos slots
// ====== CONTROLE DE TEMPO DOS SERVOS ======
static unsigned long startTime1 = 0;
static unsigned long startTime2 = 0;
static unsigned long startTime3 = 0;
static unsigned long startTime4 = 0;
const unsigned long TEMPO_RETORNO = 3000; // 3 segundos para retornar à posição inicial
const int POSICAO_INICIAL = 0;   // Posição inicial dos servos
const int POSICAO_DISPENSAR = 180;  // Posição para dispensar item
// ====== FUNÇÕES ======
void initWiFi() {
  delay(10);
  Serial.println();
  Serial.print("Conectando ao WiFi");
  WiFi.begin(SSID, PASSWORD);
  
  int tentativas = 0;
  while (WiFi.status() != WL_CONNECTED && tentativas < 20) {
    delay(500);
    Serial.print(".");
    tentativas++;
  }
  
  if (WiFi.status() == WL_CONNECTED) {
    Serial.println();
    Serial.println("WiFi conectado!");
    Serial.print("IP: ");
    Serial.println(WiFi.localIP());
  } else {
    Serial.println();
    Serial.println("Falha na conexão WiFi");
  }
}
void initMQTT() {
  MQTT.setServer(BROKER_MQTT, BROKER_PORT);
  MQTT.setCallback(mqtt_callback);
}
void atualizarDisplay() {
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("S1:");
  lcd.print(estoque[0]);
  lcd.print(" S2:");
  lcd.print(estoque[1]);
  lcd.print(" S3:");
  lcd.print(estoque[2]);
  
  lcd.setCursor(0, 1);
  lcd.print("S4:");
  lcd.print(estoque[3]);
  lcd.print(" SmartLab OK");
}
void liberarItem(int slot) {
  Serial.printf("=== ACIONANDO SLOT %d ===\n", slot + 1);
  
  if (estoque[slot] > 0) {
    estoque[slot]--;
    
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("Liberando...");
    lcd.setCursor(0, 1);
    lcd.print("Slot ");
    lcd.print(slot + 1);
    
    Serial.printf("Movendo servo do slot ", slot + 1);
    Serial.printf("Estoque restante: ", estoque[slot]);
    // Aciona o servo correspondente baseado no exemplo funcional
    switch(slot) {
      case 0: // Slot 1
        servo1.write(POSICAO_DISPENSAR);
        startTime1 = millis();
        break;
      case 1: // Slot 2
        servo2.write(POSICAO_DISPENSAR);
        startTime2 = millis();
        break;
      case 2: // Slot 3
        servo3.write(POSICAO_DISPENSAR);
        startTime3 = millis();
        break;
      case 3: // Slot 4
        servo4.write(POSICAO_DISPENSAR);
        startTime4 = millis();
        break;
    }
    
    Serial.printf("Servo do slot acionado! Retornará em segundos\n", slot + 1, TEMPO_RETORNO/1000);
    
    // Atualiza display
    delay(1000);
    atualizarDisplay();
    
  } else {
    lcd.clear();
    lcd.setCursor(0, 0);
    lcd.print("ERRO!");
    lcd.setCursor(0, 1);
    lcd.print("Slot ");
    lcd.print(slot + 1);
    lcd.print(" vazio");
    
    Serial.printf("ERRO: Slot sem estoque!\n", slot + 1);
    
    delay(2000);
    atualizarDisplay();
  }
}
void controlarServos() {
  // Controla retorno do servo1 baseado no exemplo funcional
  if(startTime1 > 0 && millis() - startTime1 >= TEMPO_RETORNO) {
    servo1.write(POSICAO_INICIAL);
    startTime1 = 0; // Reset do cronômetro
    Serial.println("Servo 1 retornou à posição inicial");
  }
  
  // Controla retorno do servo2
  if(startTime2 > 0 && millis() - startTime2 >= TEMPO_RETORNO) {
    servo2.write(POSICAO_INICIAL);
    startTime2 = 0;
    Serial.println("Servo 2 retornou à posição inicial");
  }
  
  // Controla retorno do servo3
  if(startTime3 > 0 && millis() - startTime3 >= TEMPO_RETORNO) {
    servo3.write(POSICAO_INICIAL);
    startTime3 = 0;
    Serial.println("Servo 3 retornou à posição inicial");
  }
  
  // Controla retorno do servo4
  if(startTime4 > 0 && millis() - startTime4 >= TEMPO_RETORNO) {
    servo4.write(POSICAO_INICIAL);
    startTime4 = 0;
    Serial.println("Servo 4 retornou à posição inicial");
  }
}
void mqtt_callback(char* topic, byte* payload, unsigned int length) {
  String msg = "";
  for (int i = 0; i < length; i++) {
    msg += (char)payload[i];
  }
  Serial.printf(">>> MQTT RECEBIDO <<<\n");
  Serial.printf("Tópico: %s\n", topic);
  Serial.printf("Mensagem: %s\n", msg.c_str());
  if (msg == "dispense") {
    String topico = String(topic);
    
    if (topico == TOPICO_SLOT1) {
      Serial.println("Acionando SERVO 1 (Pino 13)");
      liberarItem(0);
    } else if (topico == TOPICO_SLOT2) {
      Serial.println("Acionando SERVO 2 (Pino 12)");
      liberarItem(1);
    } else if (topico == TOPICO_SLOT3) {
      Serial.println("Acionando SERVO 3 (Pino 14)");
      liberarItem(2);
    } else if (topico == TOPICO_SLOT4) {
      Serial.println("Acionando SERVO 4 (Pino 27)");
      liberarItem(3);
    } else {
      Serial.printf("ERRO: Tópico não reconhecido: %s\n", topic);
    }
  } else {
    Serial.printf("ERRO: Mensagem não reconhecida: %s\n", msg.c_str());
  }
  Serial.println(">>> FIM CALLBACK <<<\n");
}
void verificaConexoesWiFiEMQTT() {
  // Verifica WiFi
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("Reconectando WiFi...");
    initWiFi();
  }
  // Verifica MQTT
  if (!MQTT.connected()) {
    Serial.print("Conectando ao broker MQTT...");
    
    if (MQTT.connect(ID_MQTT)) {
      Serial.println(" Conectado!");
      
      // Subscribe nos tópicos
      MQTT.subscribe(TOPICO_SLOT1);
      MQTT.subscribe(TOPICO_SLOT2);
      MQTT.subscribe(TOPICO_SLOT3);
      MQTT.subscribe(TOPICO_SLOT4);
      
      Serial.println("Inscrito nos tópicos MQTT");
      
    } else {
      Serial.print(" Falha! Estado: ");
      Serial.println(MQTT.state());
      delay(2000);
    }
  }
}
void setup() {
  Serial.begin(115200);
  delay(1000);
  
  Serial.println("=== SMARTLAB INICIANDO ===");
  
  // Inicializa I2C
  Wire.begin(SDA_PIN, SCL_PIN);
  
  // Inicializa LCD
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print("SmartLab");
  lcd.setCursor(0, 1);
  lcd.print("Iniciando...");
  
  // Inicializa servos baseado no exemplo funcional
  Serial.println("Inicializando servos...");
  servo1.attach(pinoServo1);
  servo2.attach(pinoServo2);
  servo3.attach(pinoServo3);
  servo4.attach(pinoServo4);
  
  // Posição inicial dos servos (90º como no exemplo)
  servo1.write(POSICAO_INICIAL);
  servo2.write(POSICAO_INICIAL);
  servo3.write(POSICAO_INICIAL);
  servo4.write(POSICAO_INICIAL);
  
  // Inicializa os cronômetros
  startTime1 = 0;
  startTime2 = 0;
  startTime3 = 0;
  startTime4 = 0;
  
  delay(2000); // Aguarda servos se posicionarem
  
  // Conecta WiFi e MQTT
  initWiFi();
  initMQTT();
  
  // Atualiza display inicial
  atualizarDisplay();
  
  Serial.println("=== SETUP COMPLETO ===");
}
void loop() {
  verificaConexoesWiFiEMQTT();
  MQTT.loop();
  
  // Controla o retorno automático dos servos (baseado no exemplo)
  controlarServos();
  
  delay(100);
}