#include <WiFi.h>
#include <PubSubClient.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
#include <ArduinoJson.h>

const char* ssid        = "Wokwi-GUEST";
const char* password    = "";
const char* mqtt_server = "broker.emqx.io";
const int   mqtt_port   = 1883;
const char* topicKeypad = "keypad-lcd";
const char* topicChange = "Name_Mssv";
const char* topicVerify = "loi";
const char* MQTT_ID = "ESP32-20x4-KEYPAD";

WiFiClient espClient;
PubSubClient client(espClient);

LiquidCrystal_I2C lcd(0x27, 20, 4);

const byte ROWS = 4; 
const byte COLS = 4;
char keys[ROWS][COLS] = {
  {'1','2','3','A'},
  {'4','5','6','B'},
  {'7','8','9','C'},
  {'*','0','#','D'}
};
byte rowPins[ROWS] = {13, 12, 14, 27};  
byte colPins[COLS] = {26, 25, 33, 32};  

Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);

#define LED_PIN 2

// +++ [Câu c] +++ 
String lastMssvFromWeb = "";
String typedCode = "";

void setup_wifi() {
  Serial.print("Connecting to ");
  Serial.println(ssid);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500); 
    Serial.print(".");
  }
  Serial.println("\nWiFi connected!");
  Serial.print("IP: "); 
  Serial.println(WiFi.localIP());
}

void reconnect() {
  while (!client.connected()) {
    Serial.print("Attempting MQTT connection...");
    if (client.connect(MQTT_ID)) {
      Serial.println("MQTT connected!");
      // [Câu a] 
      client.subscribe(topicKeypad);
      Serial.print("Topic 1: "); Serial.println(topicKeypad);

      // [Câu b] 
      client.subscribe(topicChange);
      Serial.print("Topic 2: "); Serial.println(topicChange);
    } else {
      Serial.print("failed, rc="); 
      Serial.print(client.state());
      delay(2000);
    }
  }
}

// +++ [Câu c] +++ 
void ledOnFor3s() {
  digitalWrite(LED_PIN, HIGH);
  delay(3000);
  digitalWrite(LED_PIN, LOW);
}

// +++ [Câu c] +++
void ledBlink5s() {
  unsigned long start = millis();
  while (millis() - start < 5000) {
    digitalWrite(LED_PIN, HIGH);
    delay(250);
    digitalWrite(LED_PIN, LOW);
    delay(250);
  }
}

void callback(char* topic, byte* payload, unsigned int length) {
  Serial.print("MQTT message on topic: ");
  Serial.println(topic);

  String msg;
  for (unsigned int i = 0; i < length; i++) {
    msg += (char)payload[i];
  }
  Serial.print("Message: ");
  Serial.println(msg);
  digitalWrite(LED_PIN, HIGH);
  delay(200);
  digitalWrite(LED_PIN, LOW);

  // [Câu a] 
  if (strcmp(topic, topicKeypad) == 0) {
    lcd.clear();
    lcd.setCursor(0,0);
    lcd.print(msg);
  }
  // [Câu b] 
  else if (strcmp(topic, topicChange) == 0) {
    StaticJsonDocument<200> doc;
    DeserializationError error = deserializeJson(doc, msg);
    if (!error) {
      String name = doc["name"] | "Unknown";
      String mssv = doc["mssv"] | "00000000";
      lastMssvFromWeb = mssv; // +++ [Câu c] +++ 
      lcd.clear();
      lcd.setCursor(0,0);
      lcd.print("MSSV: " + mssv);
      lcd.setCursor(0,1);
      lcd.print("Name: " + name);
    } else {
      Serial.println("JSON parse failed!");
      lcd.clear();
      lcd.print("JSON error!");
    }
  }
}

void setup() {
  Serial.begin(115200);
  lcd.init();
  lcd.backlight();
  lcd.clear();
  lcd.setCursor(0,0);
  lcd.print("Xin Chao");

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

  setup_wifi();
  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) {
    reconnect();
  }
  client.loop();

  char key = keypad.getKey();
  if (key) {
    // +++ [Câu c] +++ 
    if (key == '#') {
      Serial.print("MSSV cần xác nhận ");
      Serial.println(typedCode);

      client.publish(topicVerify, typedCode.c_str());

      if (typedCode == lastMssvFromWeb) {
        Serial.println("MSSV đúng");
        ledOnFor3s();    // +++ [Câu c]
      } else {
        Serial.println("MSSV sai");
        ledBlink5s();   // +++ [Câu c]
      }
      typedCode = ""; 
    } else {
      typedCode += key;
      Serial.print("Số đang nhập : ");
      Serial.println(typedCode);
    }
  }
  delay(10);
}