#include "BLE-FlashStick-Service.h"
#include "WiFi.h"
#include "my_wifi.h"
#include "global.h"
#include "AppUpgrade.h" 
#include "AppVersion.h"

static const char *tag = "BLE_FlashStickService";

#define UPGRADE_SERVICE_UUID "abcdef03-2345-6789-1234-56789abcdef0"
#define UPGRADE_CHARACTERISTIC1_UUID "abcdef03-2345-6789-1234-56789abcdef1"

NimBLEService *pUpgradeService = nullptr;
NimBLECharacteristic *pUpgradeCharacteristic1 = nullptr;

enum WIFI_STAT : byte { WIFI_DISCONNECTED=0, WIFI_BAD_CREDS=1, WIFI_NO_AP=2, WIFI_CONNECTED=3 }; 

struct FLASHSTICK_PACKET {
    bool reistered = false;
    char msg[16] = "Hello...";
}flashstickPacket;


// Class for handling server events
class ServerCallbacks : public NimBLEServerCallbacks {
    void onConnect(NimBLEServer* pServer) override {
        ESP_LOGI(tag, "Flash-Stick connected");
    }

    void onDisconnect(NimBLEServer* pServer) override {
        ESP_LOGI(tag, "Flash-Stick disconnected");
    }
};


// Class for handling characteristic events
class UpgradeChar_Callbacks : public NimBLECharacteristicCallbacks {
    
    void onWrite(NimBLECharacteristic *pCharacteristic) override {
        std::string value = pCharacteristic->getValue();
        ESP_LOGD(tag, "Upgrade Char written with value: %s", value.c_str());

        if (value.compare(0, 12, "wifi-connect") == 0) { // Update WiFi credentials
            JsonDocument doc;
            deserializeJson(doc, value.substr(13));
            JsonObject wifiJson = doc.as<JsonObject>();
            String ssid = wifiJson["ssid"].as<String>();
            String pass = wifiJson["pass"].as<String>();
            ESP_LOGI(tag, "Wifi Credentials: %s, %s", ssid.c_str(), pass.c_str());
    
            bool status = StartWifiConnectTask(ssid, pass);
            if(status == true){
                updatePacket.wifiStatus = WIFI_DISCONNECTED;
                updatePacket.wifiOnline = false;
                updatePacket.wifiIP[0] = updatePacket.wifiIP[1] = updatePacket.wifiIP[2] = updatePacket.wifiIP[3] = 0;
            }else{
                ESP_LOGI(tag, "Failed to start WiFi connection task");
            }
        }
        else if (value.compare("version-check") == 0) { // Check if new version is available
            ESP_LOGI(tag, "Version check command received: newVersion=%d.%d.%d", otaVersion.major(), otaVersion.minor(), otaVersion.patch());
            if(updatePacket.newVersion[0] == 0){
                startVersionCheckTask(); // start the task and done
            }else{
                ESP_LOGI(tag, "Version already checked");
            }
        }
        else if (value.compare("upgrade-start") == 0) { // Start OTA update
            ESP_LOGI(tag, "Start OTA update command received");
            startFirmwareUpdateTask(nullptr); // start the task
        }
        else if (value.compare("rename-device") == 0) { // Start renaming device
            ESP_LOGI(tag, "Start renane device command received");        
        }
        else {
            ESP_LOGW(tag, "Unknown command received: %s", value.c_str());
        }
    }

    void onRead(NimBLECharacteristic *pCharacteristic) override {
        updatePacket.wifiOnline = InternetAvailable;
        if(WiFi.status() == WL_CONNECTED){
          updatePacket.wifiStatus = WIFI_CONNECTED;
            if(updatePacket.wifiIP[0] == 0){
                updatePacket.wifiIP[0] = WiFi.localIP()[0];
                updatePacket.wifiIP[1] = WiFi.localIP()[1];
                updatePacket.wifiIP[2] = WiFi.localIP()[2];
                updatePacket.wifiIP[3] = WiFi.localIP()[3];
            }
        }else{
           updatePacket.wifiStatus = WIFI_DISCONNECTED; 
            if(updatePacket.wifiIP[0] > 0){
                updatePacket.wifiIP[0] = 0;
                updatePacket.wifiIP[1] = 0;
                updatePacket.wifiIP[2] = 0;
                updatePacket.wifiIP[3] = 0;
            }
        }

        //update version
        if(otaVersion.major() != 0){
            ESP_LOGI(tag, "Updated new version: major=%d, minor=%d, patch=%d", otaVersion.major(), otaVersion.minor(), otaVersion.patch());
            updatePacket.newVersion[0] = otaVersion.major();
            updatePacket.newVersion[1] = otaVersion.minor();
            updatePacket.newVersion[2] = otaVersion.patch();
        }

        pCharacteristic->setValue(reinterpret_cast<uint8_t*>(&updatePacket), sizeof(updatePacket));  
        ESP_LOGI(tag, "Upgrade Char read");
    }
};


void bleUpgrade_send_message(String s){
    if(pUpgradeCharacteristic2){
        if (s != nullptr) {
            pUpgradeCharacteristic2->setValue(s);
            pUpgradeCharacteristic2->notify();
        } else {
            ESP_LOGW(tag, "Null string passed to bleUpgrade_send_message");
        }
    }
}


void Init_UpgradeBLEService(NimBLEServer *pServer){

    // Create Upgrade BLE Service
    pUpgradeService= pServer->createService( UPGRADE_SERVICE_UUID );

    pUpgradeCharacteristic1 = pUpgradeService->createCharacteristic(
        UPGRADE_CHARACTERISTIC1_UUID,
        NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::NOTIFY
    );

    // Register the callback with the characteristic
    pUpgradeCharacteristic1->setCallbacks(new UpgradeChar_Callbacks());
    ESP_LOGI(tag, "Upgrade callback registered!");


    pUpgradeCharacteristic2 = pUpgradeService->createCharacteristic(
        UPGRADE_CHARACTERISTIC2_UUID,
        NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::NOTIFY
    );

    // Register the callback with the characteristic
    pUpgradeCharacteristic2->setCallbacks(new UpgradeChar_Callbacks());
    ESP_LOGI(tag, "Upgrade callback registered!");

    pUpgradeService->start();

    NimBLEAdvertising *pAdvertising = NimBLEDevice::getAdvertising();
    pAdvertising->addServiceUUID( UPGRADE_SERVICE_UUID ); // Advertise service UUID


}

