#include <Arduino.h>
#include <WiFi.h>
#include <SPI.h>
#include <AsyncTCP.h>
#include <ESPAsyncWebSrv.h>
#include <ArduinoJson.h>

// Configurazione hardware
#define CS_PIN 5
#define DRDY_PIN 4

// Configurazione acquisizione
#define DEFAULT_SAMPLE_RATE 30000 // Hz

// Configurazione Wi-Fi
const char* WIFI_SSID = "WebPocket-E280";
const char* WIFI_PASSWORD = "dorabino.7468!";

// Gestione configurazione
struct Config {
    uint32_t sampleRate;
    uint8_t gain;
    bool filterEnabled;
    float threshold;
    bool streaming;
};

// Variabili globali
QueueHandle_t dataQueue;
AsyncWebServer server(80);
AsyncWebSocket ws("/ws");
Config globalConfig = {DEFAULT_SAMPLE_RATE, 1, false, 1000000, true};
float emaAlpha = 0.1;

void WiFiEvent(WiFiEvent_t event) {
    Serial.printf("[WiFi] Event: %d\n", event);
    switch(event) {
        case ARDUINO_EVENT_WIFI_READY: 
            Serial.println("WiFi interface ready");
            break;
        case ARDUINO_EVENT_WIFI_SCAN_DONE:
            Serial.println("Completed scan for access points");
            break;
        case ARDUINO_EVENT_WIFI_STA_START:
            Serial.println("WiFi client started");
            break;
        case ARDUINO_EVENT_WIFI_STA_STOP:
            Serial.println("WiFi clients stopped");
            break;
        case ARDUINO_EVENT_WIFI_STA_CONNECTED:
            Serial.println("Connected to access point");
            break;
        case ARDUINO_EVENT_WIFI_STA_DISCONNECTED:
            Serial.println("Disconnected from WiFi access point");
            break;
        case ARDUINO_EVENT_WIFI_STA_AUTHMODE_CHANGE:
            Serial.println("Authentication mode of access point changed");
            break;
        case ARDUINO_EVENT_WIFI_STA_GOT_IP:
            Serial.print("Obtained IP address: ");
            Serial.println(WiFi.localIP());
            break;
        case ARDUINO_EVENT_WIFI_STA_LOST_IP:
            Serial.println("Lost IP address and IP address is reset to 0");
            break;
    }
}

void printWiFiDetails() {
    if(WiFi.status() == WL_CONNECTED) {
        Serial.println("\nWiFi Connection Details:");
        Serial.printf("SSID: %s\n", WiFi.SSID().c_str());
        Serial.printf("BSSID: %s\n", WiFi.BSSIDstr().c_str());
        Serial.printf("Channel: %d\n", WiFi.channel());
        Serial.printf("RSSI: %d dBm\n", WiFi.RSSI());
        Serial.printf("IP: %s\n", WiFi.localIP().toString().c_str());
        Serial.printf("Subnet: %s\n", WiFi.subnetMask().toString().c_str());
        Serial.printf("Gateway: %s\n", WiFi.gatewayIP().toString().c_str());
        Serial.printf("DNS: %s\n", WiFi.dnsIP().toString().c_str());
        Serial.printf("MAC: %s\n", WiFi.macAddress().c_str());
    }
}

// Gestione WebSocket
void onWsEvent(AsyncWebSocket *server, AsyncWebSocketClient *client, 
               AwsEventType type, void *arg, uint8_t *data, size_t len) {
    if(type == WS_EVT_CONNECT){
        Serial.printf("WebSocket client #%u connected from %s\n", client->id(), client->remoteIP().toString().c_str());
    } else if(type == WS_EVT_DISCONNECT){
        Serial.printf("WebSocket client #%u disconnected\n", client->id());
    } else if(type == WS_EVT_DATA){
        AwsFrameInfo *info = (AwsFrameInfo*)arg;
        if(info->final && info->index == 0 && info->len == len && info->opcode == WS_TEXT) {
            data[len] = 0;
            String message = String((char*)data);
            StaticJsonDocument<200> doc;
            DeserializationError error = deserializeJson(doc, message);
            
            if(!error) {
                if(doc.containsKey("samplerate")) {
                    globalConfig.sampleRate = doc["samplerate"].as<int>();
                    Serial.printf("Sample rate impostato a: %d\n", globalConfig.sampleRate);
                }
                if(doc.containsKey("alfaema")) {
                    emaAlpha = doc["alfaema"].as<float>();
                    Serial.printf("EMA alpha impostato a: %.2f\n", emaAlpha);
                }
            }
        }
    }
}
/*
// Task ADC
void IRAM_ATTR adcTask(void* pvParameters) {
    SPIClass spi(VSPI);
    spi.begin(18, 19, 23, CS_PIN);
    pinMode(CS_PIN, OUTPUT);
    pinMode(DRDY_PIN, INPUT);

    uint32_t lastSample = 0;
    uint32_t sampleInterval = 1000000 / globalConfig.sampleRate;
    float emaFilteredValue = 0.0;

    while (true) {
        if (!globalConfig.streaming) {
            vTaskDelay(100 / portTICK_PERIOD_MS);
            continue;
        }

        if (digitalRead(DRDY_PIN) == LOW &&
            (micros() - lastSample) >= sampleInterval) {

            digitalWrite(CS_PIN, LOW);
            uint8_t data[3] = {0x01, 0x02, 0x03};
            digitalWrite(CS_PIN, HIGH);

            int32_t value = (data[0] << 16) | (data[1] << 8) | data[2];
            if (value & 0x800000) value -= 0x1000000;

            emaFilteredValue = emaAlpha * value + (1 - emaAlpha) * emaFilteredValue;

            // Invia alla coda
            xQueueSend(dataQueue, &emaFilteredValue, 0);
            lastSample = micros();
        }
    }
}
*/
void adcTask(void* pvParameters) {
    uint32_t lastSample = 0;
    uint32_t sampleInterval = 1000000 / globalConfig.sampleRate;

    while (true) {
        if (!globalConfig.streaming) {
            vTaskDelay(100 / portTICK_PERIOD_MS);
            continue;
        }

        if ((micros() - lastSample) >= sampleInterval) {
            uint32_t timestamp = micros();
            float sample = random(0, 1000);  // Campioni casuali (da 0 a 1000)

            // Pacchetto JSON con campione e timestamp
            char message[128];
            snprintf(message, sizeof(message), "{\"timestamp\": %u, \"sample\": %.2f}", timestamp, sample);

            // Invia al server WebSocket
            xQueueSend(dataQueue, &message, 0);
            lastSample = timestamp;
        }
    }
}

// Task WebSocket
void webSocketDataTask(void* pvParameters) {
    float value;
    char buffer[32];
    
    while(true) {
        if(xQueueReceive(dataQueue, &value, portMAX_DELAY) == pdTRUE) {
            if(ws.count() > 0) {  // Se ci sono client connessi
                snprintf(buffer, sizeof(buffer), "%.2f", value);
                ws.textAll(buffer);
            }
        }
    }
}

void setup() {
    Serial.begin(115200);
    
    // Abilita log verbose WiFi
    esp_log_level_set("wifi", ESP_LOG_VERBOSE);
    
    // Registra handler eventi WiFi
    WiFi.onEvent(WiFiEvent);
    
    // Inizializza WiFi
    WiFi.mode(WIFI_STA);
    WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
    
    uint8_t attempts = 0;
    while (WiFi.status() != WL_CONNECTED && attempts < 20) {
        delay(500);
        Serial.print(".");
        attempts++;
    }
    
    if (WiFi.status() == WL_CONNECTED) {
        printWiFiDetails();
    } else {
        Serial.println("\nWiFi connection failed!");
        ESP.restart();
    }

    // Setup WebSocket
    ws.onEvent(onWsEvent);
    server.addHandler(&ws);
    
    // Avvia server
    server.begin();
    
    // Crea la coda
    dataQueue = xQueueCreate(1024, sizeof(float));
    
    // Avvia i task
    xTaskCreatePinnedToCore(webSocketDataTask, "WS Data Task", 8192, NULL, 1, NULL, 0); // Sul core 0 con WiFi
    xTaskCreatePinnedToCore(adcTask, "ADC Task", 16384, NULL, 5, NULL, 1);             // Sul core 1
    
    Serial.println("Setup completato");
}

void loop() {
    static unsigned long lastDebugTime = 0;
    const unsigned long debugInterval = 5000; // Debug ogni 5 secondi
    
    if (millis() - lastDebugTime > debugInterval) {
        // Stampa info memoria
        Serial.printf("Free Heap: %d bytes\n", ESP.getFreeHeap());
        Serial.printf("Min Free Heap: %d bytes\n", ESP.getMinFreeHeap());
        Serial.printf("Max Alloc Heap: %d bytes\n", ESP.getMaxAllocHeap());
        
        // Stampa info WiFi
        Serial.printf("WiFi RSSI: %d dBm\n", WiFi.RSSI());
        Serial.printf("WebSocket Clients: %u\n", ws.count());
        
        lastDebugTime = millis();
    }
    
    vTaskDelay(10); // Previene watchdog reset
}
$abcdeabcde151015202530fghijfghij
esp:VIN
esp:GND.2
esp:D13
esp:D12
esp:D14
esp:D27
esp:D26
esp:D25
esp:D33
esp:D32
esp:D35
esp:D34
esp:VN
esp:VP
esp:EN
esp:3V3
esp:GND.1
esp:D15
esp:D2
esp:D4
esp:RX2
esp:TX2
esp:D5
esp:D18
esp:D19
esp:D21
esp:RX0
esp:TX0
esp:D22
esp:D23
led1:A
led1:C
led2:A
led2:C
led3:A
led3:C
r1:1
r1:2
r2:1
r2:2
r3:1
r3:2