#include <WiFi.h>
#include <WebServer.h>
#include "esp_wifi.h"

const char* ssid = "ESP32-Louis";
const char* password = "motdepasse";

WebServer server(80);

void handleRoot() {
  digitalWrite(2,HIGH);
  wifi_sta_list_t sta_list;
  esp_wifi_ap_get_sta_list(&sta_list);

  String page = "";
  page += "<!DOCTYPE html>";
  page += "<html>";
  page += "<head>";
  page += "<title>ESP32</title>";
  page += "<meta charset=\"UTF-8\">";
  page += "</head>";
  page += "<body>";
  page += "<h1>Appareils connectés à l'ESP32</h1>";
  page += "<ul>";

  page += "<ul>";

  for (int i = 0; i < sta_list.num; i++) {
    const wifi_sta_info_t& client = sta_list.sta[i];
    char macStr[18];
    snprintf(macStr, sizeof(macStr),
             "%02X:%02X:%02X:%02X:%02X:%02X",
             client.mac[0], client.mac[1], client.mac[2],
             client.mac[3], client.mac[4], client.mac[5]);
    page += "<li>MAC : ";
    page += macStr;
    page += "</li>";
  }

  page += "</ul>";
  page += "<p>Nombre total : " + String(sta_list.num) + "</p>";

  page += "</body></html>";

  server.send(200, "text/html", page);
  digitalWrite(2,LOW);
}

void onClientConnected(WiFiEvent_t event, WiFiEventInfo_t info) {
  digitalWrite(2, HIGH);

  const wifi_event_ap_staconnected_t* conn = reinterpret_cast<const wifi_event_ap_staconnected_t*>(&info);


  byte packet[11];  // 2 header + 1 code + 6 MAC + 1 fin
  packet[0] = 0x02;
  packet[1] = 0x02;
  packet[2] = 0x01;

  // Copier l'adr MAC dans le tableau
  memcpy(&packet[3], conn->mac, 6);

  packet[9] = 0x1B;  // marqueur avant fin
  packet[10] = 0x03; // fin de trame

  Serial.write(packet, sizeof(packet));
  digitalWrite(2, LOW);
}

void onClientDisconnected(WiFiEvent_t event, WiFiEventInfo_t info) {
  digitalWrite(2, HIGH);

  const wifi_event_ap_stadisconnected_t* disc = reinterpret_cast<const wifi_event_ap_stadisconnected_t*>(&info);

  byte packet[12]; 
  packet[0] = 0x02;
  packet[1] = 0x02;  // OP_CODE: Connection Update
  packet[2] = 0x00;  // Disconnected

  memcpy(&packet[3], disc->mac, 6); // MAC address

  packet[9] = 0x1B;  // marqueur avant fin
  packet[10] = 0x03; // fin de trame
  packet[11] = '\n'; // (optionnel, pour debug dans terminal série)

  Serial.write(packet, 11); // <= envoie bien 11 octets, pas 12 (on ne compte pas le \n ici si tu veux l’ignorer)
  digitalWrite(2, LOW);
}

#define BUFFER_SIZE 64
uint8_t rxBuffer[BUFFER_SIZE];
uint8_t rxIndex = 0;
bool inFrame = false;

void processCommand(uint8_t* data, int length) {
  // Vérifie la validité de la trame
  if (length < 4 || data[0] != 0x02 || data[length - 2] != 0x1B || data[length - 1] != 0x03) {
    byte packet[] = {0x02, 0x00, 0x03, 0x1B, 0x03}; // Erreur : Trame invalide
    Serial.write(packet, sizeof(packet));
    return;
  }

  uint8_t type = data[1];

  switch (type) {

    case 0x01: { // Wi-Fi State
      bool wifiUp = WiFi.status() == WL_CONNECTED;
      byte packet[] = {0x02, 0x01, wifiUp ? 0x01 : 0x00, 0x1B, 0x03};
      Serial.write(packet, sizeof(packet));
      break;
    }

    case 0x03: { // Request Connected Devices
      wifi_sta_list_t staList;
      if (esp_wifi_ap_get_sta_list(&staList) == ESP_OK) {
        byte packet[3 + 1 + 6 * 10 + 2]; // max 10 clients
        uint8_t index = 0;
        packet[index++] = 0x02;
        packet[index++] = 0x04;
        packet[index++] = staList.num; // LEN

        for (int i = 0; i < staList.num; i++) {
          memcpy(&packet[index], staList.sta[i].mac, 6);
          index += 6;
        }

        packet[index++] = 0x1B;
        packet[index++] = 0x03;

        Serial.write(packet, index);
      } else {
        byte error[] = {0x02, 0x00, 0x02, 0x1B, 0x03}; // Erreur : args
        Serial.write(error, sizeof(error));
      }
      break;
    }

    case 0x05: { // Send Message
      if (length < 12) {
        byte packet[] = {0x02, 0x00, 0x02, 0x1B, 0x03}; // args error
        Serial.write(packet, sizeof(packet));
        break;
      }

      uint8_t* mac = &data[2];
      uint8_t msgLen = data[8];

      if (length != 9 + msgLen + 2) {
        byte packet[] = {0x02, 0x00, 0x04, 0x1B, 0x03}; // too long trame
        Serial.write(packet, sizeof(packet));
        break;
      }

      // Ici tu pourrais ajouter une logique pour router le message au bon appareil (plus tard)
      // ACK possible :
      byte ack[] = {0x02, 0x04, 0x01, 0x1B, 0x03}; // ACK
      Serial.write(ack, sizeof(ack));
      break;
    }

    default: {
      byte packet[] = {0x02, 0x00, 0x01, 0x1B, 0x03}; // Erreur : commande inconnue
      Serial.write(packet, sizeof(packet));
    }
  }
}

void setup() {
  Serial.begin(115200);
  if (!WiFi.softAP(ssid, password)) {
    byte packet[] = {0x02, 0x01, 0x00, 0x03};
    Serial.write(packet, sizeof(packet));
  }

  WiFi.onEvent(onClientConnected, ARDUINO_EVENT_WIFI_AP_STACONNECTED);
  WiFi.onEvent(onClientDisconnected, ARDUINO_EVENT_WIFI_AP_STADISCONNECTED);

  delay(1000);  // Donne un peu de temps pour démarrer le WiFi

  server.on("/", handleRoot);
  server.begin();

  pinMode(2, OUTPUT);

  byte packet[] = {0x02, 0x01, 0x01, 0x03};
  Serial.write(packet, sizeof(packet));

}

void loop() {
  server.handleClient();

  bool escaping = false;

  while (Serial.available()) {
    uint8_t b = Serial.read();

    if (!inFrame) {
      if (b == 0x02) {
        inFrame = true;
        rxIndex = 0;
        rxBuffer[rxIndex++] = b;
      }
      continue;
    }

    if (escaping) {
      if (rxIndex < BUFFER_SIZE) {
        rxBuffer[rxIndex++] = b;
      }
      escaping = false;
      continue;
    }

    if (b == 0x1B) {
      escaping = true;
    } else if (b == 0x03) {
      rxBuffer[rxIndex++] = b;
      processCommand(rxBuffer, rxIndex);
      inFrame = false;
      rxIndex = 0;
    } else {
      if (rxIndex < BUFFER_SIZE) {
        rxBuffer[rxIndex++] = b;
      } else {
        inFrame = false;
        rxIndex = 0;
      }
    }
  }

}