// Wi-Fi Signal Scanner 
//
// Adapted for the HackerBox ESP ECG from: 
// https://www.instructables.com/Wi-Fi-Signal-Scanner-With-ESP32-Live-Network-Scan/
// For project details:   https://www.instructables.com/member/TeknoTrek/

#include <WiFi.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>

#define BUTTON_PIN 4

#define TFT_DC    26
#define TFT_CS     5
#define TFT_MOSI  23
#define TFT_CLK   18
#define TFT_RST   22 
#define TFT_MISO  19
#define TFT_BL    25

Adafruit_ILI9341 tft = Adafruit_ILI9341(TFT_CS, TFT_DC, TFT_MOSI, TFT_CLK, TFT_RST, TFT_MISO);

uint16_t colors[] = {ILI9341_GREEN, ILI9341_YELLOW, ILI9341_RED, ILI9341_BLUE, ILI9341_CYAN, ILI9341_MAGENTA, ILI9341_ORANGE, ILI9341_WHITE};

int networkCount = 0;

void setup() {
    Serial.begin(115200);
    WiFi.mode(WIFI_STA);
    WiFi.disconnect();

    pinMode(TFT_BL, OUTPUT);
    digitalWrite(TFT_BL, HIGH);  // Backlight ON

    pinMode(BUTTON_PIN, INPUT_PULLDOWN);
    
    tft.begin();
    tft.setRotation(1);
    tft.fillScreen(ILI9341_BLACK);
    tft.setTextSize(2);
}

void loop() {
  listWiFi();
  delay(4000);
  scanWiFi();
  
  while (digitalRead(BUTTON_PIN) == LOW) { 
    delay(500);
  }
}

void scanWiFi() {
    uint16_t titleColor = colors[random(8)];
    tft.setTextSize(2);
    tft.fillScreen(ILI9341_BLACK);
    tft.setTextColor(titleColor);

    tft.fillScreen(ILI9341_BLACK);
    titleColor = colors[random(8)];
    tft.setTextColor(titleColor);

    // (Calculate text width and height)
    int16_t x1, y1;
    uint16_t w, h;
    tft.getTextBounds("Press Button to Refresh", 0, 0, &x1, &y1, &w, &h);
    int x = (tft.width() - w) / 2;
    int y = 8; // (Keep title at the top)
    tft.setCursor(x, y);
    tft.print("Press Button to Refresh");

    // (Display the number of WiFi networks in the top right corner)
    tft.setCursor(tft.width() - 60, 37);
    tft.setTextSize(1);
    tft.setTextColor(ILI9341_CYAN);
    tft.print("Net: ");
    tft.print(networkCount);

    if (networkCount == 0) {
        tft.setCursor(10, 40);
        tft.print("No networks found.");
    } else {
        int barWidth = (tft.width() - 50) / networkCount - 10;
        int barSpacing = 10;
        int startX = 40;
        int bottomY = tft.height() - 30;
        int gridSpacing = 20;

        tft.drawFastVLine(30, 30, tft.height() - 60, ILI9341_DARKGREY);

        for (int y = 30; y <= bottomY; y += gridSpacing) {
            tft.drawFastHLine(30, y, tft.width() - 30, ILI9341_DARKGREY);
            int rssiValue = map(y, 30, bottomY, -30, -100);
            tft.setTextSize(1);
            tft.setTextColor(ILI9341_WHITE);
            tft.setCursor(5, y - 5);
            tft.print(rssiValue);
        }

        tft.setTextSize(1);
        tft.setTextColor(ILI9341_WHITE);
        tft.setCursor(5, 215);
        tft.print("SSID>"); 

        tft.setTextSize(1);
        tft.setTextColor(ILI9341_WHITE);
        tft.setCursor(5, 225);
        tft.print("  CH>"); 

        for (int i = 0; i < networkCount; i++) {
            int signalStrength = WiFi.RSSI(i);
            int barHeight = map(signalStrength, -100, -30, 10, bottomY - 30);
            uint16_t color = colors[i % 8];

            int barX = startX + i * (barWidth + barSpacing);

            tft.fillRect(barX, bottomY - barHeight, barWidth, barHeight, color);

            tft.setTextSize(1);
            tft.setTextColor(color);
            tft.setCursor(barX + (barWidth / 4), bottomY - barHeight - 15);
            tft.print(signalStrength);

            tft.setCursor(barX, bottomY + 5);
            String ssid = WiFi.SSID(i);
            if (ssid.length() > 3) {
                ssid = ssid.substring(0, 3);
            }
            tft.print(ssid);

            if (WiFi.encryptionType(i) != WIFI_AUTH_OPEN) {
                tft.setCursor(barX + (barWidth / 2), bottomY - barHeight - 25);
                tft.print("*");
            }

            tft.setCursor(barX, bottomY + 15);
            tft.print(" ");
            tft.print(WiFi.channel(i));
        }
    }
}

void listWiFi() {

    uint16_t titleColor = colors[random(8)];
    tft.setTextSize(2);
    tft.fillScreen(ILI9341_BLACK);
    tft.setTextColor(titleColor);

    // (Calculate text width and height)
    int16_t x1, y1;
    uint16_t w, h;
    tft.getTextBounds("Scanning Wi-Fi Networks...", 0, 0, &x1, &y1, &w, &h);

    // Center text
    int x = (tft.width() - w) / 2;
    int y = (tft.height() - h) / 2;

    tft.setCursor(x, y);
    tft.print("Scanning Wi-Fi Networks...");

    networkCount = WiFi.scanNetworks();

    if (networkCount == 0) {
        tft.fillScreen(ILI9341_BLACK);
        tft.setCursor(10, 40);
        tft.setTextColor(ILI9341_RED);
        tft.print("No networks found.");
        return;
    }

    String ssidList[networkCount];
    int rssiList[networkCount];
    int channelList[networkCount];
    bool encryptionList[networkCount];

    // (Add networks to the list)
    for (int i = 0; i < networkCount; i++) {
        ssidList[i] = WiFi.SSID(i);
        rssiList[i] = WiFi.RSSI(i);
        channelList[i] = WiFi.channel(i);
        encryptionList[i] = (WiFi.encryptionType(i) != WIFI_AUTH_OPEN);
    }

    // Sort by RSSI (highest to lowest)
    for (int i = 0; i < networkCount - 1; i++) {
        for (int j = i + 1; j < networkCount; j++) {
            if (rssiList[j] > rssiList[i]) {
                std::swap(ssidList[i], ssidList[j]);
                std::swap(rssiList[i], rssiList[j]);
                std::swap(channelList[i], channelList[j]);
                std::swap(encryptionList[i], encryptionList[j]);
            }
        }
    }

    tft.setTextSize(2);
    
    tft.fillScreen(ILI9341_BLACK);
    titleColor = colors[random(8)];
    tft.setTextColor(titleColor);

    // (Calculate text width and height)
    tft.getTextBounds("2.4GHz Wi-Fi Networks", 0, 0, &x1, &y1, &w, &h);
    x = (tft.width() - w) / 2;
    y = 8; // (Keep title at the top)
    tft.setCursor(x, y);
    tft.print("2.4GHz Wi-Fi Networks");

    tft.setTextSize(1);

    // Array of colors (For different lines)
    uint16_t colors[] = {ILI9341_GREEN, ILI9341_YELLOW, ILI9341_RED, ILI9341_BLUE, ILI9341_CYAN, ILI9341_MAGENTA, ILI9341_ORANGE, ILI9341_WHITE};
    int colorCount = sizeof(colors) / sizeof(colors[0]);

    for (int i = 0; i < networkCount && i < 26; i++) {
        tft.setCursor(10, 36 + (i * 12));
        tft.setTextColor(colors[i % colorCount]);  // (Use different colors for each line)
        
        tft.print(ssidList[i]);   // SSID
        tft.print(" ");
        tft.print(rssiList[i]);   // (Signal Strength)
        tft.print("dBm Ch:");
        tft.print(channelList[i]); // (Channel Number)
        tft.print(" | ");
        tft.print(encryptionList[i] ? "Pwd" : "NotPwd"); // (Encrypted or not?)
    }
}