/**
 * Example for the ESP32 HTTP(S) Webserver
 *
 * IMPORTANT NOTE:
 * To run this script, your need to
 *  1) Enter your WiFi SSID and PSK below this comment
 *  2) Make sure to have certificate data available. You will find a
 *     shell script and instructions to do so in the library folder
 *     under extras/
 *
 * This script will install an HTTPS Server on your ESP32 with the following
 * functionalities:
 *  - Show page on web server root that will display some SVG smileys.
 *    Using a simple HTML form and GET parameters, you can change the colors
 *    of those images.
 *  - Provide the svg image with an optional GET parameter that allows to
 *    modify the background color, URL: /images/awesome.svg
 *  - Provide an example that shows how wildcard URL parameters get parsed
 *    URL: /urlparam/some/thing
 *  - 404 for everything else
 */

// TODO: Configure your WiFi here
#define WIFI_SSID "<your ssid goes here>"
#define WIFI_PSK  "<your pre-shared key goes here>"

// Include certificate data (see note above)
#include "cert.h"
#include "private_key.h"

// We will use wifi
#include <WiFi.h>

// Includes for the server
#include <HTTPSServer.hpp>
#include <SSLCert.hpp>
#include <HTTPRequest.hpp>
#include <HTTPResponse.hpp>

// The HTTPS Server comes in a separate namespace. For easier use, include it here.
using namespace httpsserver;

// Create an SSL certificate object from the files included above
SSLCert cert = SSLCert(
  example_crt_DER, example_crt_DER_len,
  example_key_DER, example_key_DER_len
);

// Create an SSL-enabled server that uses the certificate
// The contstructor takes some more parameters, but we go for default values here.
HTTPSServer secureServer = HTTPSServer(&cert);

// We declare some handler functions (definition at the end of the file)
void handleRoot(HTTPRequest * req, HTTPResponse * res);
void handleSVG(HTTPRequest * req, HTTPResponse * res);
void handleURLParam(HTTPRequest * req, HTTPResponse * res);
void handle404(HTTPRequest * req, HTTPResponse * res);

void setup() {
  // For logging
  Serial.begin(115200);

  // Connect to WiFi
  Serial.println("Setting up WiFi");
  WiFi.begin(WIFI_SSID, WIFI_PSK);
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    delay(500);
  }
  Serial.print("Connected. IP=");
  Serial.println(WiFi.localIP());

  // For every resource available on the server, we need to create a ResourceNode
  // The ResourceNode links URL and HTTP method to a handler function
  ResourceNode * nodeRoot     = new ResourceNode("/", "GET", &handleRoot);
  ResourceNode * nodeSVG      = new ResourceNode("/images/awesome.svg", "GET", &handleSVG);
  ResourceNode * node404      = new ResourceNode("", "GET", &handle404);

  // URL params
  // If you want (for example) to return a specific instance of an object type by its ID
  // you can use URLs like /led/1, led/2, ... - And you do not need to register one Resource
  // Node per ID, but you can use wildcards in the URL definition. The following function
  // has to wildcards, and will match for example to /urlparam/foo/bar, where "foo" and "bar"
  // are accessible parameters in the handler function.
  // Note: The wildcards can only be used between slashes at the moment (so /urlparam* would
  // not work).
  ResourceNode * nodeURLParam = new ResourceNode("/urlparam/*/*", "GET", &handleURLParam);

  // Add the root node to the server
  secureServer.registerNode(nodeRoot);

  // Add the SVG image
  secureServer.registerNode(nodeSVG);

  // Add the URL param node
  // Note: The order of nodes may become important here. If you have one node for "/led" (e.g. list of LEDs)
  // and one node for /led/* (LED details), you should register the non-parameterized version first. The server
  // follows a first-match policy. If you would register the details node first, a call to /led/ will be targetted
  // at the details handler function with an empty parameter, which is probably not what you want.
  secureServer.registerNode(nodeURLParam);

  // Add the 404 not found node to the server.
  // The path is ignored for the default node.
  secureServer.setDefaultNode(node404);

  Serial.println("Starting server...");
  secureServer.start();
  if (secureServer.isRunning()) {
    Serial.println("Server ready.");
  }
}

void loop() {
	// This call will let the server do its work
	secureServer.loop();

	// Other code would go here...
	delay(1);
}

void handleRoot(HTTPRequest * req, HTTPResponse * res) {
  // We will deliver an HTML page
  res->setHeader("Content-Type", "text/html");

  // Write the response page
  res->println("<!DOCTYPE html>");
  res->println("<html>");
  res->println("<head><title>Hello World!</title></head>");
  res->println("<body>");

  res->println("<h1>GET parameters</h1>");

  // Show a form to select a color to colorize the faces
  // We pass the selection as get parameter "shades" to this very same page,
  // so we can evaluate it below
  res->println("<form method=\"GET\" action=\"/\">Show me faces in shades of ");
  res->println("<select name=\"shades\">");
  res->println("<option value=\"red\">red</option>");
  res->println("<option value=\"green\">green</option>");
  res->println("<option value=\"blue\">blue</option>");
  res->println("<option value=\"yellow\">yellow</option>");
  res->println("<option value=\"cyan\">cyan</option>");
  res->println("<option value=\"magenta\">magenta</option>");
  res->println("<option value=\"rainbow\">rainbow</option>");
  res->println("</select>");
  res->println("<button type=\"submit\">Go!</button>");
  res->println("</form>");

  // Get the params to check if the user did select something
  ResourceParameters * params = req->getParams();
  std::string paramName = "shades";

  // Print 6 faces
  for(int i = 0; i < 6; i++) {
    // Include the image of the handleSVG function with a specific color code
    res->print("<img style=\"height:100px;width:100px\" src=\"images/awesome.svg?color=");

    // Depending on the selection we show the images in a specific color shade
    // Default is dark gray.
    int r = 63, g = 63, b = 63;
    if (params->isRequestParameterSet(paramName)) {
      std::string paramVal = params->getRequestParameter(paramName);
      if (paramVal == "red" || paramVal == "magenta" || paramVal == "yellow" || paramVal == "rainbow") {
        r = 128 + random(0, 128);
      }
      if (paramVal == "green" || paramVal == "cyan" || paramVal == "yellow" || paramVal == "rainbow") {
        g = 128 + random(0, 128);
      }
      if (paramVal == "blue" || paramVal == "magenta" || paramVal == "cyan" || paramVal == "rainbow") {
        b = 128 + random(0, 128);
      }
		}

    // Print the random color. As the HTTPResponse extends the Print interface, we can make use of that.
    res->print(r, HEX);
    res->print(g, HEX);
    res->print(b, HEX);

    res->print("\" alt=\"Awesome!\" />");
  }

  // Link to the URL parameter demo
  res->println("<h1>URL parameters</h1>");
  res->println("<p>You'll find the demo <a href=\"/urlparam/foo/bar\">here</a>.</p>");

  res->println("</body>");
  res->println("</html>");
}

// This callback responds with an SVG image to a GET request. The icon is the awesome face.
// (borrowed from https://commons.wikimedia.org/wiki/File:718smiley.svg)
//
// If the color request parameter is set (so the URL is like awesome.svg?color=fede58), the
// background of our awesome face is changed.
void handleSVG(HTTPRequest * req, HTTPResponse * res) {
  // Get access to the parameters
  ResourceParameters * params = req->getParams();

  // Set SVG content type
  res->setHeader("Content-Type", "image/svg+xml");

  // Set a default color
  std::string fillColor = "fede58";

  // Get request parameter (like awesome.svg?color=ff0000) and validate it
  std::string colorParamName = "color";

  // Check that the parameter is set
  if (params->isRequestParameterSet(colorParamName)) {
    // Get the value of the parameter
    std::string requestColor = params->getRequestParameter(colorParamName);
    // Check for correct length
    if (requestColor.length()==6) {
      bool colorOk = true;
      // Check that we only have characters within [0-9a-fA-F]
      for(int i = 1; i < 6 && colorOk; i++) {
        if (!(
          (requestColor[i]>='0' && requestColor[i]<='9' ) ||
          (requestColor[i]>='a' && requestColor[i]<='f' ) ||
          (requestColor[i]>='A' && requestColor[i]<='F' )
        )) {
          colorOk = false;
        }
      }
      // If validation was successful, replace the default color
      if (colorOk) {
        fillColor = requestColor;
      }
    }
  }

  // Print the SVG to the response:
  res->print("<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>");
  res->print("<svg id=\"svg1923\" width=\"733\" xmlns=\"http://www.w3.org/2000/svg\" height=\"733\">");
  res->print("<circle cy=\"366.5\" cx=\"366.5\" r=\"366.5\"/>");
  res->print("<circle cy=\"366.5\" cx=\"366.5\" r=\"336.5\" fill=\"#");
  // We insert the color from the parameter here
  res->printStd(fillColor);
  res->print("\"/>");
  res->print("<path d=\"m325 665c-121-21-194-115-212-233v-8l-25-1-1-18h481c6 13 10 27 13 41 13 94-38 146-114 193-45 23-93 29-142 26z\"/>");
  res->print("<path d=\"m372 647c52-6 98-28 138-62 28-25 46-56 51-87 4-20 1-57-5-70l-423-1c-2 56 39 118 74 157 31 34 72 54 116 63 11 2 38 2 49 0z\" fill=\"#871945\"/>");
  res->print("<path d=\"m76 342c-13-26-13-57-9-85 6-27 18-52 35-68 21-20 50-23 77-18 15 4 28 12 39 23 18 17 30 40 36 67 4 20 4 41 0 60l-6 21z\"/>");
  res->print("<path d=\"m234 323c5-6 6-40 2-58-3-16-4-16-10-10-14 14-38 14-52 0-15-18-12-41 6-55 3-3 5-5 5-6-1-4-22-8-34-7-42 4-57.6 40-66.2 77-3 17-1 53 4 59h145.2z\" fill=\"#fff\"/>");
  res->print("<path d=\"m378 343c-2-3-6-20-7-29-5-28-1-57 11-83 15-30 41-52 72-60 29-7 57 0 82 15 26 17 45 49 50 82 2 12 2 33 0 45-1 10-5 26-8 30z\"/>");
  res->print("<path d=\"m565 324c4-5 5-34 4-50-2-14-6-24-8-24-1 0-3 2-6 5-17 17-47 13-58-9-7-16-4-31 8-43 4-4 7-8 7-9 0 0-4-2-8-3-51-17-105 20-115 80-3 15 0 43 3 53z\" fill=\"#fff\"/>");
  res->print("<path d=\"m504 590s-46 40-105 53c-66 15-114-7-114-7s14-76 93-95c76-18 126 49 126 49z\" fill=\"#f9bedd\"/>");
  res->print("</svg>");
}

// This is a simple handler function that will show the content of URL parameters.
// If you call for example /urlparam/foo/bar, you will get the parameter values
// "foo" and "bar" provided by the ResourceParameters.
void handleURLParam(HTTPRequest * req, HTTPResponse * res) {
  // Get access to the parameters
  ResourceParameters * params = req->getParams();

  // Set a simple content type
  res->setHeader("Content-Type", "text/plain");

  // The url pattern is: /urlparam/*/*
  // This will make the content for the first parameter available on index 0,
  // and the second wildcard as index 1.

  // Print the first parameter value
  res->print("Parameter 1: ");
  res->printStd(params->getUrlParameter(0));

  // Print the second parameter value
  res->print("\nParameter 2: ");
  res->printStd(params->getUrlParameter(1));

  res->println("\n\nChange the parameters in the URL to see how they get parsed!");

  // Note: If you have objects that are identified by an ID, you may also use
  // ResourceParameters::getUrlParameterInt(int) for convenience
}

// For details to this function, see the Static-Page example
void handle404(HTTPRequest * req, HTTPResponse * res) {
  req->discardRequestBody();
  res->setStatusCode(404);
  res->setStatusText("Not Found");
  res->setHeader("Content-Type", "text/html");
  res->println("<!DOCTYPE html>");
  res->println("<html>");
  res->println("<head><title>Not Found</title></head>");
  res->println("<body><h1>404 Not Found</h1><p>The requested resource was not found on this server.</p></body>");
  res->println("</html>");
}
