#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <DHT11.h> // Συμπερίληψη της βιβλιοθήκης για τον αισθητήρα DHT11

// Όνομα του BLE Server (Έτσι θα εμφανίζεται σε άλλες συσκευές/κινητά κατά την αναζήτηση)
#define BLE_SERVER_NAME "DHT_ESP32"

// Ορισμός Pin για το LED και τον αισθητήρα
#define LED_PIN 23
#define DHT11_PIN 2

// Δημιουργία αντικειμένου για τον αισθητήρα DHT11 στο Pin 2
DHT11 dht11(DHT11_PIN);

// Μεταβλητές χρονισμού (Timer) για τον έλεγχο της συχνότητας αποστολής
unsigned long previousMillis = 0;
const unsigned long timerDelay = 10000; // Αποστολή δεδομένων κάθε 10 δευτερόλεπτα

// Κατάσταση λειτουργίας για το LED
int ledState = 0;
bool isDeviceConnected = false;

// Τα UUIDs (Universally Unique Identifiers) για την Υπηρεσία και τα Χαρακτηριστικά
#define SERVICE_UUID          "0000181A-0000-1000-8000-00805F9B34FB"
#define TEMP_CHARACTERISTIC_UUID "00002A6E-0000-1000-8000-00805F9B34FB"
#define HUM_CHARACTERISTIC_UUID  "00002A6F-0000-1000-8000-00805F9B34FB"
#define CHARACTERISTIC_UUID_RX     "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"

/**
 * Διαχειριστής Χαρακτηριστικού Λήψης (RX Callback)
 * Εδώ διαβάζουμε εντολές που έρχονται ΑΠΟ τον client, π.χ. για να ανάψουμε το LED.
 */
class RxDataHandler : public BLECharacteristicCallbacks {
  void onWrite(BLECharacteristic *pCharacteristic) {
    String rxValue = pCharacteristic->getValue();

    if (rxValue.length() > 0) {
      Serial.println("\n--- Νέα Λήψη Δεδομένων ---");
      Serial.print("Τιμή που ελήφθη: ");
      for (int i = 0; i < rxValue.length(); i++) {
        Serial.print(rxValue[i]);
      }
      Serial.println("\n--------------------------");

      // Απλή λογική: Αν λάβουμε '0' σβήνουμε το LED, αλλιώς το ανάβουμε
      if (rxValue[0] != '0') {
        ledState = 1;
      } else {
        ledState = 0;
      }
    }
  }
};

// Δημιουργία Χαρακτηριστικού και Περιγραφέα (Descriptor) για τη Θερμοκρασία
BLECharacteristic dhtTemperatureCharacteristic(
  TEMP_CHARACTERISTIC_UUID, 
  BLECharacteristic::PROPERTY_NOTIFY
);
BLEDescriptor dhtTemperatureDescriptor(BLEUUID((uint16_t)0x2902));

// Δημιουργία Χαρακτηριστικού και Περιγραφέα (Descriptor) για την Υγρασία
BLECharacteristic dhtHumidityCharacteristic(
  HUM_CHARACTERISTIC_UUID, 
  BLECharacteristic::PROPERTY_NOTIFY
);
BLEDescriptor dhtHumidityDescriptor(BLEUUID((uint16_t)0x2903));

/**
 * Διαχειριστής Συνδέσεων του Server (Server Callbacks)
 * Ελέγχει πότε μια συσκευή συνδέεται ή αποσυνδέεται.
 */
class ServerConnectionHandler : public BLEServerCallbacks {
  void onConnect(BLEServer *pServer) {
    isDeviceConnected = true;
    Serial.println("[ΣΥΝΔΕΣΗ] Ένας client συνδέθηκε με επιτυχία!");
  };

  void onDisconnect(BLEServer *pServer) {
    isDeviceConnected = false;
    Serial.println("[ΑΠΟΣΥΝΔΕΣΗ] Ο client αποσυνδέθηκε.");
    // Ξεκινάμε ξανά τη διαφήμιση για να μπορέσουν να συνδεθούν άλλοι
    BLEDevice::startAdvertising();
  }
};

void setup() {
  Serial.begin(115200); // Χρησιμοποιούμε 115200 για ταχύτητα & BLE output
  Serial.println("Εκκίνηση BLE Server (Εκπομπός Δεδομένων DHT11) - IoT Lab");

  pinMode(LED_PIN, OUTPUT);

  // 1. Δημιουργία του BLE Device με το ορισμένο όνομα
  BLEDevice::init(BLE_SERVER_NAME);

  // 2. Δημιουργία του BLE Server
  BLEServer *pServer = BLEDevice::createServer();
  pServer->setCallbacks(new ServerConnectionHandler());

  // 3. Δημιουργία της Υπηρεσίας (Service)
  BLEService *bDhtService = pServer->createService(SERVICE_UUID);

  // 4. Προσθήκη Χαρακτηριστικών & Περιγραφέων στην Υπηρεσία

  // - Χαρακτηριστικό Θερμοκρασίας
  bDhtService->addCharacteristic(&dhtTemperatureCharacteristic);
  dhtTemperatureDescriptor.setValue("Θερμοκρασία DHT11");
  dhtTemperatureCharacteristic.addDescriptor(&dhtTemperatureDescriptor);

  // - Χαρακτηριστικό Υγρασίας
  bDhtService->addCharacteristic(&dhtHumidityCharacteristic);
  dhtHumidityDescriptor.setValue("Υγρασία DHT11");
  dhtHumidityCharacteristic.addDescriptor(new BLE2902());

  // - Χαρακτηριστικό Λήψης RX (Για διακόπτη LED)
  BLECharacteristic *pRxCharacteristic = bDhtService->createCharacteristic(
      CHARACTERISTIC_UUID_RX,
      BLECharacteristic::PROPERTY_WRITE
  );
  pRxCharacteristic->setCallbacks(new RxDataHandler());

  // 5. Εκκίνηση της Υπηρεσίας
  bDhtService->start();

  // 6. Ξεκινάμε τη 'Διαφήμιση' (Advertising) για να μας βρουν άλλες συσκευές
  BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
  pAdvertising->addServiceUUID(SERVICE_UUID);
  pServer->getAdvertising()->start();
  
  Serial.println("[ΕΤΟΙΜΟΤΗΤΑ] Ο Server είναι ενεργός και περιμένει συνδέσεις...");
}

void loop() {
  // Εκτέλεση λειτουργιών ΜΟΝΟ αν υπάρχει συνδεδεμένη συσκευή
  if (isDeviceConnected) {

    // Έλεγχος αν πέρασε ο χρόνος (10 δευτερόλεπτα)
    if ((millis() - previousMillis) > timerDelay) {
      
      int temperature = 0;
      int humidity = 0;

      // Πραγματική ανάγνωση τιμών από τον DHT11
      int result = dht11.readTemperatureHumidity(temperature, humidity);

      // Έλεγχος αν η ανάγνωση πέτυχε
      if (result == 0) {
        // --- Επεξεργασία & Αποστολή Θερμοκρασίας ---
        char tempString[8];
        sprintf(tempString, "%d", temperature); // Μετατροπή του ακεραίου σε κείμενο
        dhtTemperatureCharacteristic.setValue((uint8_t*)tempString, strlen(tempString));
        dhtTemperatureCharacteristic.notify(); // Ειδοποίηση (Notify) στον Client
        
        Serial.print("Απεσταλμένη Θερμοκρασία: ");
        Serial.print(tempString);
        Serial.print(" °C");

        // Ενημέρωση κατάστασης του LED με βάση τελευταίες λήψεις (RX)
        digitalWrite(LED_PIN, ledState);

        // --- Επεξεργασία & Αποστολή Υγρασίας ---
        char humString[8];
        sprintf(humString, "%d", humidity);
        dhtHumidityCharacteristic.setValue((uint8_t*)humString, strlen(humString));
        dhtHumidityCharacteristic.notify(); 
        
        Serial.print(" | Απεσταλμένη Υγρασία: ");
        Serial.print(humString);
        Serial.println(" %");

      } else {
        // Αν υπάρχει σφάλμα ανάγνωσης, εκτυπώνουμε το σφάλμα
        Serial.print("\n[ΣΦΑΛΜΑ ΑΙΣΘΗΤΗΡΑ DHT11] ");
        Serial.println(DHT11::getErrorString(result));
      }

      // Ανανέωση του χρονομέτρου για την επόμενη ανάγνωση αποστολή
      previousMillis = millis();
    }
  }
}
