#include <Arduino.h>
#include <NimBLEDevice.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <Wire.h>

#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, -1);

static const char* SERVICE_UUID = "181A";
static const char* TEMP_UUID    = "2A6E";
static const char* HUM_UUID     = "2A6F";
static const char* PRES_UUID    = "2A6D";

static const NimBLEAdvertisedDevice* advDevice;
static bool doConnect = false;
static bool connected = false;

float t = 0, h = 0, p = 0;
String statusMsg = "Inditas...";

void updateOLED() {
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(1);
    display.setCursor(0,0);
    display.println(statusMsg);
    display.printf("T:%.2f C\nH:%.1f %%\nP:%.1f hPa", t, h, p);
    display.display();
}

void notifyCB(NimBLERemoteCharacteristic* pRemoteCharacteristic, uint8_t* pData, size_t length, bool isNotify) {
    std::string uuid = pRemoteCharacteristic->getUUID().toString();
    if (uuid.find("2a6e") != std::string::npos) t = (*(int16_t*)pData) / 100.0;
    else if (uuid.find("2a6f") != std::string::npos) h = (*(int16_t*)pData) / 100.0;
    else if (uuid.find("2a6d") != std::string::npos) p = (*(uint32_t*)pData) / 10.0;
    updateOLED();
}

class ClientCallbacks : public NimBLEClientCallbacks {
    void onConnect(NimBLEClient* pClient) override {
        connected = true;
        statusMsg = "Kapcsolodva";
        updateOLED();
    }
    void onDisconnect(NimBLEClient* pClient, int reason) override {
        connected = false;
        statusMsg = "Szakadt - Ujra scan";
        updateOLED();
        // Szakadás után azonnal indítjuk a végtelen szkennelést
        NimBLEDevice::getScan()->start(0, false, true);
    }
} clientCallbacks;

class ScanCallbacks : public NimBLEScanCallbacks {
    void onResult(const NimBLEAdvertisedDevice* advertisedDevice) override {
        if (advertisedDevice->isAdvertisingService(NimBLEUUID(SERVICE_UUID)) || 
            advertisedDevice->getName() == "C3-BME280") {
            
            NimBLEDevice::getScan()->stop();
            advDevice = advertisedDevice;
            doConnect = true;
        }
    }
} scanCallbacks;

bool connectToServer() {
    NimBLEClient* pClient = nullptr;
    if (NimBLEDevice::getCreatedClientCount()) {
        pClient = NimBLEDevice::getClientByPeerAddress(advDevice->getAddress());
        if (pClient) {
            if (!pClient->connect(advDevice, false)) return false;
        } else {
            pClient = NimBLEDevice::getDisconnectedClient();
        }
    }

    if (!pClient) {
        pClient = NimBLEDevice::createClient();
        pClient->setClientCallbacks(&clientCallbacks, false);
        pClient->setConnectionParams(12, 12, 0, 150);
        pClient->setConnectTimeout(5 * 1000);
        if (!pClient->connect(advDevice)) {
            NimBLEDevice::deleteClient(pClient);
            return false;
        }
    }

    NimBLERemoteService* pSvc = pClient->getService(SERVICE_UUID);
    if (pSvc) {
        NimBLERemoteCharacteristic* cT = pSvc->getCharacteristic(TEMP_UUID);
        NimBLERemoteCharacteristic* cH = pSvc->getCharacteristic(HUM_UUID);
        NimBLERemoteCharacteristic* cP = pSvc->getCharacteristic(PRES_UUID);
        if (cT && cT->canNotify()) cT->subscribe(true, notifyCB);
        if (cH && cH->canNotify()) cH->subscribe(true, notifyCB);
        if (cP && cP->canNotify()) cP->subscribe(true, notifyCB);
        return true;
    }
    return false;
}

void setup() {
     Wire.begin(8, 9);
    if(!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) for(;;);
    
    NimBLEDevice::init("C3-Client");
    NimBLEScan* pScan = NimBLEDevice::getScan();
    pScan->setScanCallbacks(&scanCallbacks, false);
    pScan->setInterval(100);
    pScan->setWindow(100);
    pScan->setActiveScan(true);
    
    // START 0: Végtelen szkennelés indítása
    pScan->start(0, false);
    
    statusMsg = "Szkenneles...";
    updateOLED();
}

void loop() {
    // 1. Csatlakozás vezérlése
    if (doConnect) {
        doConnect = false;
        if (connectToServer()) {
            statusMsg = "Adat fogadas...";
        } else {
            statusMsg = "Hiba - Scan ujra";
            NimBLEDevice::getScan()->start(0, false, true);
        }
        updateOLED();
    }

    // 2. ÉLETMENTŐ LOGIKA: Ha nincs kapcsolat és nem szkennel, indítsa újra!
    // Ez kezeli azt, ha a rádió elakadna vagy időtúllépés történne
    if (!connected && !doConnect) {
        NimBLEScan* pScan = NimBLEDevice::getScan();
        if (!pScan->isScanning()) {
            pScan->start(0, false, true);
        }
    }
    delay(200); // Ne terheljük túl a CPU-t, de reagáljon gyorsan
}
