#include <Arduino.h>
    #include "WiFi.h"
    #include <NTPClient.h>
    #include <LittleFS.h>
    #include <WiFiUdp.h>
    #include "TFT_eSPI.h"
    #include <FastLED.h>
    #include "OneButton.h"
    #include <WiFiClient.h>
    #include <WebServer.h>
    #include <ESPmDNS.h>
    #include <SD_MMC.h>
    #include <FS.h>
    #include <ArduinoJson.h>
    #include <esp_partition.h>

    WiFiUDP ntpUDP;
    NTPClient NTP(ntpUDP);

    #define WIFI_SSID            ""
    #define WIFI_PASSWORD        ""

    #ifndef BOOT_PIN
    #define BOOT_PIN       0
    #endif

    #define LED_DI_PIN     40
    #define LED_CI_PIN     39

    #define TFT_CS_PIN     4
    #define TFT_SDA_PIN    3
    #define TFT_SCL_PIN    5
    #define TFT_DC_PIN     2
    #define TFT_RES_PIN    1
    #define TFT_LEDA_PIN   38

    #define LCD_PIXEL_WIDTH             160
    #define LCD_PIXEL_HEIGHT            80

    #define SD_MMC_D0_PIN               14
    #define SD_MMC_D1_PIN               17
    #define SD_MMC_D2_PIN               21
    #define SD_MMC_D3_PIN               18
    #define SD_MMC_CLK_PIN              12
    #define SD_MMC_CMD_PIN              16

    OneButton button(BOOT_PIN, true);
    uint32_t rgb_change_interval = 0; // milliseconds
    uint8_t  rgb_hue = 0;
    CRGB leds;
    bool sdEnable = false;
    TFT_eSPI tft = TFT_eSPI();
    bool BrokenFS;
    // UDP broadcast для обнаружения устройства
    WiFiUDP udp;
    const unsigned int udpPort = 48899; // Стандартный порт для обнаружения
    const char* udpMessage = "SamovarichLogger";
    unsigned long lastUdpTime = 0;
    const unsigned long udpInterval = 5000; // 5 секунд

    // Состояние USB Mass Storage
    bool usbMassStorageActive = false;
    bool sdMounted = true; // SD примонтирована для веб-сервера

    // IP подсеть для broadcast
    IPAddress broadcastIp;
    // Глобальные переменные для выбора файловой системы
    bool useLittleFS = false;

    const char *host = "esp32";
    WebServer server(80);
    File fsUploadFile;

    void InitMQTT();
    void loopMQTT();
    void setup_WS();
    void loop_WS();
    void Init_USB_Mass_Storage();
    void ToggleUSBMassStorage();
    bool hasActiveMQTTSessions();
    bool enableBrokerClients();

inline String format_float(float v, int d) {
    char outstr[15];
    dtostrf(v, 1, d, outstr);
    return outstr;
}
void RotateDisplay() {
    static bool dispRotate = false;
    if (dispRotate) tft.setRotation(1);
    else tft.setRotation(3);
    dispRotate = !dispRotate;
    tft.fillScreen(TFT_BLACK);
}
void DisplayInfo() {
    tft.setTextColor(TFT_BLUE, TFT_BLACK);
    tft.setCursor (8, 10);
    if (WiFi.isConnected()) tft.print(WiFi.localIP().toString() + " RSSI:" + String(WiFi.RSSI())); // This uses the standard ADAFruit small font
                        else tft.print("Connect to WiFi...");
    static uint32_t SDPrintTime = millis() + 10000;
    if ( millis() - SDPrintTime > 10000) {
        SDPrintTime = millis();
        float free = (SD_MMC.totalBytes() - SD_MMC.usedBytes());
        String freeS = "";
        uint8_t cardType = SD_MMC.cardType();
        String CardStr;
        if (cardType != CARD_NONE) {
            uint64_t card_size_bytes = SD_MMC.cardSize();
            float sdCardSize;
            if (card_size_bytes >= (uint64_t)1024 * 1024 * 1024) {
                sdCardSize = (float)card_size_bytes / (1024.0 * 1024.0 * 1024.0);
                CardStr = "SD:" + String(sdCardSize) + " GB";
            } else {
                sdCardSize = (float)card_size_bytes / (1024.0 * 1024.0);
                CardStr = "SD:" + String(sdCardSize) + " MB";
            }
            if (free > 1024 * 1024) {
                CardStr += " Free: " + format_float(free / (1024 * 1024 * 1024), 1) + " GB";
            } else if (free > 1024 * 1024) {
                CardStr += " Free: " + format_float(free / (1024 * 1024), 1) + " MB";
            } else if (free > 1024) {
                CardStr += " Free: " + format_float(free / 1024, 1) + " KB";
            } else {
                CardStr += " Free: " + String(free) + " B"; 
            }

        } else {
            CardStr = "SD:N.A";
        }
        tft.setTextColor(TFT_SILVER, TFT_BLACK);
        tft.setCursor (8, 70);
        tft.print(CardStr);
    }
    if (hasActiveMQTTSessions()) tft.setTextColor(TFT_RED, TFT_BLACK);//цвет часов в зависимости от статуса логгера
    else if (enableBrokerClients()) tft.setTextColor(TFT_GREEN, TFT_BLACK);
    else tft.setTextColor(TFT_GOLD, TFT_BLACK);

    tft.setCursor (5, 32);
    tft.setTextSize(3);
    if (WiFi.isConnected()) tft.print(NTP.getFormattedTime());
                        else tft.print("-=TIME=-");
    tft.setTextSize(1);
    
}
void InitSD() {
        // ================ Initialize SD card =================
    SD_MMC.setPins(SD_MMC_CLK_PIN, SD_MMC_CMD_PIN, SD_MMC_D0_PIN, SD_MMC_D1_PIN, SD_MMC_D2_PIN, SD_MMC_D3_PIN);
    if (!SD_MMC.begin()) {
        Serial.println("Card Mount Failed");
    } else sdEnable = true;
    uint8_t cardType = SD_MMC.cardType();
    if (cardType != CARD_NONE) {
        Serial.print("SD_MMC Card Type: ");
        if (cardType == CARD_MMC) {
            Serial.println("MMC");
        } else if (cardType == CARD_SD) {
            Serial.println("SDSC");
        } else if (cardType == CARD_SDHC) {
            Serial.println("SDHC");
        } else {
            Serial.println("UNKNOWN");
        }
        Serial.printf("SD_MMC Card Size: %llu bytes\n", SD_MMC.cardSize());
    }
}
void InitDisplay() {
    // Initialise TFT
    tft.init();
    tft.setRotation(1);
    digitalWrite(TFT_LEDA_PIN, 0);
    tft.setTextFont(1);
    tft.fillScreen(TFT_BLACK);
    tft.setTextColor(TFT_YELLOW, TFT_BLACK); // Note: the new fonts do not draw the background colour   
    Serial.println("LCD initialized");
}
void MassStotageOn() {
    ToggleUSBMassStorage();
}
void calculateBroadcastIp() {
    IPAddress localIp = WiFi.localIP();
    broadcastIp = IPAddress(localIp[0], localIp[1], localIp[2], 255);
}
void sendUdpBroadcast() {
    if (!WiFi.isConnected()) return;
    calculateBroadcastIp();
    udp.beginPacket(broadcastIp, udpPort);
    udp.write((const uint8_t*)udpMessage, strlen(udpMessage));
    udp.endPacket();
}
void save_profile() {
    // Ваша функция сохранения
    // Если нет - добавьте пустую реализацию
    Serial.println("Profile saved");
}

void setup() {
    pinMode(TFT_LEDA_PIN, OUTPUT);
    pinMode(BOOT_PIN, INPUT);
    button.attachClick(RotateDisplay);
    button.attachLongPressStop(MassStotageOn); // Длинное нажатие для переключения USB

    Serial.begin(115200);
    InitDisplay();
    FastLED.addLeds<APA102, LED_DI_PIN, LED_CI_PIN, BGR>(&leds, 1);
    FastLED.setBrightness(100);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    delay(1000);
    // Инициализация LittleFS для внутренней памяти
    if (!LittleFS.begin(true, "/littlefs", 10, "littlefs")) {
        Serial.println("LittleFS Mount Failed");
        BrokenFS = true;
    } else {
        Serial.println("LittleFS mounted successfully");
        BrokenFS = false;
    }
    
    InitSD();
    
    
    
    setup_WS();
    NTP.setTimeOffset(3 * 3600);
    NTP.setUpdateInterval(10000); // 10 sec
    NTP.begin(); 
    InitMQTT();
    
    leds[0] = CRGB::Blue; // Индикатор нормального режима
    FastLED.show();
    Serial.printf("SD total: %llu bytes, used: %llu bytes\n", SD_MMC.totalBytes(), SD_MMC.usedBytes());
}

void loop() {
    button.tick();
    loopMQTT();
    
    static bool USB_MS_enable = false;
    
    static uint32_t targetTime = millis();
    if ( millis() - targetTime > 1000) {
        targetTime = millis();
        DisplayInfo();        
    }
    
    if (WiFi.isConnected()) {
        NTP.update();
        
        // Отправка UDP broadcast каждые 30 секунд
        if (millis() - lastUdpTime > udpInterval) {
            sendUdpBroadcast();
            lastUdpTime = millis();
        }
    }

    if (millis() - rgb_change_interval > 50) {
        rgb_change_interval = millis();
        leds = CHSV(rgb_hue++, 0XFF, 100);
        FastLED.show();
    }
    
    loop_WS();
    delay(5);
}
