/*
 *** UTCN DMP LAB X ***
 * WiFi Part 1 - WiFi Access Point + HTTP Server with ESP32
 * originally adapted from https://github.com/espressif/arduino-esp32/blob/master/libraries/WiFi/examples/WiFiAccessPoint/WiFiAccessPoint.ino
 */

/*
 * HEADER FILES
 * Include header files provided by Espressif for the ESP32
 * https://github.com/espressif/arduino-esp32/tree/master/libraries/WiFi/src
 */
#include <WiFi.h>
#include <WiFiAP.h>
#include <WiFiClient.h>

/*
 * GLOBAL CONSTANTS & OTHER VARIABLES
 */

// MESSAGE STRINGS
const String SETUP_INIT = "SETUP: Initializing ESP32 dev board";
const String SETUP_ERROR = "!!ERROR!! SETUP: Unable to start SoftAP mode";
const String SETUP_SERVER_START = "SETUP: HTTP server started --> IP addr: ";
const String SETUP_SERVER_PORT = " on port: ";
const String INFO_NEW_CLIENT = "New client connected";
const String INFO_DISCONNECT_CLIENT = "Client disconnected";

// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
const String HTTP_HEADER = "HTTP/1.1 200 OK\r\nContent-type:text/html\r\n\r\n";
const String HTML_WELCOME = "<h1>Welcome to your ESP32 Web Server!</h1>";
const String HTML_TOGGLE_INFO = "<div>This URL toggles the built-in LED</div><br />";
const String HTML_TOGGLE_CHANGE = "<div>In order to change it, <span style=\"color: red\">please refresh this page in the browser</span></div>";

// BASIC WIFI CONFIGURATION CONSTANTS
// The SSID (Service Set IDentifier), in other words, the network's name
const char *SSID = "DMP_ESP32_AP";
// Password for the network
// By default the ESP32 uses WPA / WPA2-Personal security, therefore the
// the password MUST be between 8 and 63 ASCII characters
const char *PASS = "somepass2023";
// The default port (both TCP & UDP) of a WWW HTTP server number according to
// RFC1340 is 80
const int HTTP_PORT_NO = 80;

// ADDITIONAL GLOBALS
// Initialize the HTTP server on the ESP32 board
WiFiServer HttpServer(HTTP_PORT_NO);

/*
 * MAIN ARDUINO FUNCTIONS (setup & loop)
 */
void setup() {
  pinMode(LED_BUILTIN, OUTPUT);

  Serial.begin(115200);

// ESP32 boards have a delay with UART for some reason from a reset after
// uploading new code to the development board
#if 1  // board was newly programmed
  delay(1000);
#else
  // https://www.arduino.cc/reference/en/language/functions/communication/serial/ifserial/
  while (!Serial) {
    ;  // wait for serial port to connect. Needed for native USB
  }
#endif
  Serial.println();
  Serial.println(SETUP_INIT);

  // Initialize and the start the ESP32 board's WiFi module in SoftAP mode with
  // the network name and password from the beginning of the file
  if (!WiFi.softAP(SSID, PASS)) {
    Serial.println(SETUP_ERROR);
    // Lock system in infinite loop in order to prevent further execution
    while (1)
      ;
  }

  // Get AP's IP address for info message
  const IPAddress accessPointIP = WiFi.softAPIP();
  const String webServerInfoMessage = SETUP_SERVER_START + accessPointIP.toString()
                                      + SETUP_SERVER_PORT + HTTP_PORT_NO;

  // Start the HTTP server
  HttpServer.begin();
  Serial.println(webServerInfoMessage);
}

void loop() {
  WiFiClient client = HttpServer.available();  // listen for incoming clients

  if (client) {                        // if you get a client,
    Serial.println(INFO_NEW_CLIENT);   // print a message out the serial port
    String currentLine = "";           // make a String to hold incoming data from the client
    while (client.connected()) {       // loop while the client's connected
      if (client.available()) {        // if there's bytes to read from the client,
        const char c = client.read();  // read a byte, then
        Serial.write(c);               // print it out the serial monitor

        if (c == '\n') {  // if the byte is a newline character
          // if the current line is blank, you got two newline characters in a row.
          // that's the end of the client HTTP request, so send a response:
          if (currentLine.length() == 0) {
            // Send welcome page to the client
            printWelcomePage(client);
            break;
          } else currentLine = "";
        } else if (c != '\r') {  // if you got anything else but a carriage return character,
          currentLine += c;      // add it to the end of the currentLine
        }

        // Send toggle built-in LED page to the client
        if (currentLine.endsWith("GET /toggle")) {
          Serial.println();
          printToggle(client);
          break;
        }

        // INDIVIDUAL WORK TO IMPLEMENT READING FROM SENSOR
        // See method at the bottom of the file
        if (currentLine.endsWith("GET /read-sens")) {
          Serial.println();
          printReadSensor(client);
          break;
        }
      }
    }
    // close the connection:
    client.stop();
    Serial.println(INFO_DISCONNECT_CLIENT);
    Serial.println();
  }
}

/*
 * ADDITIONAL HELPER FUNCTIONS
 */
void printWelcomePage(WiFiClient client) {
  // Always start the response to the client with the proper headers
  client.println(HTTP_HEADER);

  // Send the relevant HTML
  client.print(HTML_WELCOME);

  // The HTTP response ends with another blank line
  client.println();
}

void printToggle(WiFiClient client) {
  // Get currently elapsed time
  const unsigned long elapsedTime = millis();

  // Get value from LED and flip it on the dev board
  const bool newVal = !digitalRead(LED_BUILTIN);
  digitalWrite(LED_BUILTIN, newVal);
  const String currState = newVal == 1 ? String("ON") : String("OFF");

  // Send the HTML code to the client
  client.println(HTTP_HEADER);
  client.print(HTML_TOGGLE_INFO);
  const String stateMessage = String("Currently, it is <strong>")
                              + currState + String("</strong>");
  client.print(String("<div>") + stateMessage + String("</div><br />"));
  client.print(String("<div>It was last toggled <i>") + String(elapsedTime / 1000)
               + String(" seconds</i> after the start of the application</div><br />"));
  client.print(HTML_TOGGLE_CHANGE);
  client.println();
}

// TODO: Implement reading from sensor and display value
void printReadSensor(WiFiClient client) {}