/*
 * Net Keyboard
 * 
 * Alistair MacDonald 2026
 * 
 */

#include <W55RP20lwIP.h>
#include <ArduinoMDNS.h>
#include <Keyboard.h>
#include "indexhtml.h"

Wiznet55rp20lwIP ethernet(20); // Chip select on pin 20

// Create a [web] server object
WiFiServer server(80);

// Create a multicast DNS (mDNS) object
WiFiUDP udp;
MDNS mdns(udp);

// The main setup routing
void setup() {
  // Open the serial port for debuggin
  Serial.begin(115200);
  Serial.println();
  Serial.println("Starting...");

  // Initalise the Ethernet interface
  if (!ethernet.begin()) {
    Serial.println("Error: No Ethernet hardware detected.");
    while (true) { delay(1000); }
  }

  // Wait for connection and an IP address
  while (!ethernet.connected()) {
    Serial.print(".");
    delay(500);
  }

  // Report our IP address
  Serial.println("");
  Serial.println("Ethernet connected");
  Serial.println("IP address: ");
  Serial.println(ethernet.localIP());

  // Initiate mDNS so we can be discovered
  mdns.begin(ethernet.localIP(), "netkeyboard");

  // Enable the USB keyboard emulation
  Keyboard.begin();

  // Start the [web] server
  server.begin();
}

// A function to return the keybaord page
void sendResponce_index(WiFiClient client) {  
  client.println("HTTP/1.1 200 OK");
  client.println("Content-Type: text/html");
  client.println("Connection: close");  // the connection will be closed after completion of the response
  client.println();
  client.println(indexHTML);  
}

// A function to send a key pres from the emulatied USB keyboard 
void sendResponce_key(WiFiClient client, String inKey) {
  int keyCode = inKey.toInt();
  if (keyCode>0) {
    Keyboard.write(keyCode);
    Serial.print("Keycode sent : ");
    Serial.println(keyCode);
  }
  else {
    Serial.print("Invalid keycode : ");
    Serial.println(inKey);
  }
}

// Send a 404 error page if an unexpected page is requested
void sendResponce_404(WiFiClient client) {
  client.println("HTTP/1.1 404 Not Found");
  client.println("Content-Type: text/html");
  client.println("Connection: close");  // the connection will be closed after completion of the response
  client.println();
  client.println(F("<!DOCTYPE HTML><html><head><title>Not Found<title></title></head><body><h1>Not Found</h1><p>The requested URL was not found on this server.</p></body></html>"));
}

// A helper function to read a string from a netowrk connection
String readClientString(WiFiClient client) {
  String nextLine = "";
  // Loop thougth reading a character at a time
  while ( client.connected() ) {
    char nextChar = client.read();
    // Add character to the new string if not a control character 
    if ( (nextChar>=32) && (nextChar<=126) ) {
      nextLine += nextChar;
    }
    // Return the completed string if complete
    else if (nextChar==10) {
      return nextLine;
    }
    // Ignore any control characters we don't knwo how to deal with
  }
  // Return what we have if the connection is closed while reading
  return nextLine;
}

// A function to service a web request connection
void serviceConnection(WiFiClient client) {
  // Read the HTTP request
  String requestLine = readClientString(client);
  // Read the rest of the header
  while (client.connected()) {
      String headerLine = readClientString(client);
      if (headerLine=="") break;
  }
  // Send the responce
  if ( requestLine.startsWith("GET / ") ) {
    // The main page (with the keyboard on it)
    return sendResponce_index(client);
  }
  else if ( requestLine.startsWith("POST /key?code=") ) {
    // An api call to send a key press
    return sendResponce_key(client, requestLine.substring(15, requestLine.lastIndexOf(" ")));
  }
  else {
    // A error fallback for anything else
    return sendResponce_404(client);
  }
}

void loop() {
  // listen for incoming clients
  WiFiClient client = server.accept();
  if (client) {
    Serial.println("New connection.");
    serviceConnection(client);
    // A qucik fix to give the web browser time to receive the data
    delay(100);
    // close the connection:
    client.stop();
  }
  // Advertise ourselves to the local netowrk using mDNS
  mdns.run();

}
