/* ESP32 WiFi Scanning and Connection Example with WPA2-Enterprise Support and Persistent Storage */

#include "WiFi.h"
#include "esp_wpa2.h" // Include WPA2 library for enterprise networks
#include <Preferences.h>

Preferences preferences;
bool wifiConnected = false;
String wifiPassword;
String wifiUsername; // Username for WPA2-Enterprise
String wifiIdentity; // Identity for WPA2-Enterprise
String wifiSSID;

void setup() {
  Serial.begin(115200);
  Serial.println("Initializing WiFi...");
  WiFi.mode(WIFI_STA);
  preferences.begin("wifi", false);

  wifiSSID = preferences.getString("ssid", "");
  wifiPassword = preferences.getString("password", "");
  wifiUsername = preferences.getString("username", "");
  wifiIdentity = preferences.getString("identity", "");

  if (wifiSSID != "") {
    Serial.print("Attempting to connect to saved network: ");
    Serial.println(wifiSSID);
    if (wifiUsername != "") {
      connectToEnterpriseNetwork(wifiSSID, wifiUsername, wifiIdentity, wifiPassword);
    } else {
      connectToNetwork(wifiSSID, wifiPassword);
    }
  } else {
    scanNetworks();
  }
}

void loop() {
  if (WiFi.status() == WL_CONNECTED && !wifiConnected) {
    wifiConnected = true;
    Serial.println("WiFi connected!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
  } else if (WiFi.status() != WL_CONNECTED && wifiConnected) {
    wifiConnected = false;
    Serial.println("WiFi connection lost.");
    scanNetworks();
  }
}

void scanNetworks() {
  Serial.println("Scanning for WiFi networks...");

  int networkCount = WiFi.scanNetworks();
  Serial.println("Scan done!");

  if (networkCount == 0) {
    Serial.println("No networks found.");
    return;
  } 

  Serial.print(networkCount);
  Serial.println(" networks found");
  for (int i = 0; i < networkCount; ++i) {
    Serial.print(i + 1);
    Serial.print(": ");
    Serial.print(WiFi.SSID(i));
    Serial.print(" (");
    Serial.print(WiFi.RSSI(i));
    Serial.print(") ");
    Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? "Open" : "Secured");
    delay(10);
  }

  selectNetwork(networkCount);
}

void selectNetwork(int networkCount) {
  Serial.println("Enter the number of the network you want to connect to:");

  while (true) {
    if (Serial.available() > 0) {
      int networkIndex = Serial.parseInt();
      if (networkIndex > 0 && networkIndex <= networkCount) {
        wifiSSID = WiFi.SSID(networkIndex - 1);
        Serial.print("Selected network: ");
        Serial.println(wifiSSID);
        if (WiFi.encryptionType(networkIndex - 1) == WIFI_AUTH_WPA2_ENTERPRISE) {
          Serial.println("Enter the username:");
          while (!Serial.available()) {
            // Wait for user to enter the username
          }
          wifiUsername = Serial.readStringUntil('\n');

          Serial.println("Enter the identity:");
          while (!Serial.available()) {
            // Wait for user to enter the identity
          }
          wifiIdentity = Serial.readStringUntil('\n');

          Serial.println("Enter the password:");
          while (!Serial.available()) {
            // Wait for user to enter the password
          }
          wifiPassword = Serial.readStringUntil('\n');

          Serial.print("Connecting to ");
          Serial.println(wifiSSID);
          connectToEnterpriseNetwork(wifiSSID, wifiUsername, wifiIdentity, wifiPassword);
        } else {
          Serial.println("Enter the password:");
          while (!Serial.available()) {
            // Wait for user to enter the password
          }
          wifiPassword = Serial.readStringUntil('\n');

          Serial.print("Connecting to ");
          Serial.println(wifiSSID);
          connectToNetwork(wifiSSID, wifiPassword);
        }
        break;
      } else {
        Serial.println("Invalid network number. Try again.");
      }
    }
  }
}

void connectToNetwork(String ssid, String password) {
  WiFi.begin(ssid.c_str(), password.c_str());

  Serial.print("Connecting");
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 40) { // 10 seconds timeout
    delay(250);
    Serial.print(".");
    attempts++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println();
    Serial.println("Connected successfully!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    saveNetworkDetails(ssid, password, "", "");
  } else {
    Serial.println();
    Serial.println("Connection failed. Check your password and try again.");
    scanNetworks();
  }
}

void connectToEnterpriseNetwork(String ssid, String username, String identity, String password) {
  WiFi.disconnect(true); // Disconnect from any existing connection
  esp_wifi_sta_wpa2_ent_set_identity((uint8_t *)identity.c_str(), identity.length());
  esp_wifi_sta_wpa2_ent_set_username((uint8_t *)username.c_str(), username.length());
  esp_wifi_sta_wpa2_ent_set_password((uint8_t *)password.c_str(), password.length());
  esp_wifi_sta_wpa2_ent_enable();

  WiFi.begin(ssid.c_str());

  Serial.print("Connecting");
  int attempts = 0;
  while (WiFi.status() != WL_CONNECTED && attempts < 40) { // 10 seconds timeout
    delay(250);
    Serial.print(".");
    attempts++;
  }

  if (WiFi.status() == WL_CONNECTED) {
    Serial.println();
    Serial.println("Connected successfully!");
    Serial.print("IP address: ");
    Serial.println(WiFi.localIP());
    saveNetworkDetails(ssid, password, username, identity);
  } else {
    Serial.println();
    Serial.println("Connection failed. Check your credentials and try again.");
    scanNetworks();
  }
}

void saveNetworkDetails(String ssid, String password, String username, String identity) {
  preferences.putString("ssid", ssid);
  preferences.putString("password", password);
  preferences.putString("username", username);
  preferences.putString("identity", identity);
  Serial.println("Network details saved.");
}