#include <ESP8266WiFi.h>
#include <ESP8266WebServer.h>
#include <ArduinoJson.h>

ESP8266WebServer server(80);

const int MAX_SAMPLES = 500;

int samples[MAX_SAMPLES];
int sampleCount = 0;
uint32_t batchNumber = 0;

void handleRoot() {

  String html = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>ESP8266 Oscilloscope</title>

<style>
body{
  font-family:Arial;
  text-align:center;
  margin-top:20px;
}

canvas{
  border:1px solid black;
}
</style>
</head>

<body>

<h2>Microphone Waveform</h2>

<canvas id="chart" width="1000" height="400"></canvas>

<script>

const canvas = document.getElementById("chart");
const ctx = canvas.getContext("2d");

let buffer = [];
let displayValues = [];

let currentBatch = -1;
let loading = false;

async function getBatch() {

    if(loading) return;
    if(buffer.length > 0) return;

    loading = true;

    try {

        const res = await fetch('/json');
        const json = await res.json();

        if(json.batch !== currentBatch &&
           json.values.length > 0) {

            currentBatch = json.batch;
            buffer = [...json.values];

            console.log(
                "New batch",
                currentBatch,
                buffer.length
            );
        }

    } catch(e) {

        console.log(e);

    }

    loading = false;
}

function playback() {

    if(buffer.length === 0) {

        getBatch();
        return;
    }

    displayValues.push(buffer.shift());

    if(displayValues.length > 500)
        displayValues.shift();

    draw();
}

function draw() {

    ctx.clearRect(
        0,
        0,
        canvas.width,
        canvas.height
    );

    // Grid

    ctx.strokeStyle = "#dddddd";

    for(let i=0;i<=10;i++) {

        ctx.beginPath();

        ctx.moveTo(
            0,
            i * canvas.height / 10
        );

        ctx.lineTo(
            canvas.width,
            i * canvas.height / 10
        );

        ctx.stroke();
    }

    // Waveform

    ctx.beginPath();
    ctx.strokeStyle = "blue";
    ctx.lineWidth = 2;

    for(let i=0;i<displayValues.length;i++) {

        let x =
            (i / 499) *
            canvas.width;

        let y =
            canvas.height -
            ((displayValues[i] / 1023.0) *
             canvas.height);

        if(i===0)
            ctx.moveTo(x,y);
        else
            ctx.lineTo(x,y);
    }

    ctx.stroke();

    // Status

    ctx.fillStyle = "black";
    ctx.font = "14px Arial";

    ctx.fillText(
        "Batch: " + currentBatch,
        10,
        20
    );

    ctx.fillText(
        "Remaining: " + buffer.length,
        10,
        40
    );
}

getBatch();

// Playback speed
// Change if desired

setInterval(playback, 2);

</script>

</body>
</html>
)rawliteral";

  server.send(200, "text/html", html);
}

void handleJson() {

  JsonDocument doc;

  doc["batch"] = batchNumber;

  JsonArray arr =
      doc["values"].to<JsonArray>();

  for(int i = 0; i < sampleCount; i++) {
    arr.add(samples[i]);
  }

  String json;
  serializeJson(doc, json);

  server.send(
      200,
      "application/json",
      json
  );
}

void handleData() {

  String body = server.arg("plain");

  JsonDocument doc;

  DeserializationError err =
      deserializeJson(doc, body);

  if(err) {

    Serial.print("JSON error: ");
    Serial.println(err.c_str());

    server.send(
        400,
        "text/plain",
        "Bad JSON"
    );

    return;
  }

  JsonArray arr =
      doc["sensor0_reading"];

  if(arr.isNull()) {

    server.send(
        400,
        "text/plain",
        "Missing sensor0_reading"
    );

    return;
  }

  sampleCount =
      min((int)arr.size(),
          MAX_SAMPLES);

  for(int i = 0;
      i < sampleCount;
      i++) {

    JsonVariant v = arr[i];

    if(v.is<JsonObject>()) {

      samples[i] =
          v["waarde"] | 0;

    } else {

      samples[i] =
          v | 0;
    }
  }

  batchNumber++;

  Serial.printf(
      "Received batch %lu (%d samples)\n",
      batchNumber,
      sampleCount
  );

  server.send(
      200,
      "text/plain",
      "OK"
  );
}

void setup() {

  Serial.begin(115200);

  WiFi.softAP(
      "iPhone van Rolf",
      "ocean123"
  );

  Serial.println();
  Serial.println(
      "Access Point started"
  );

  Serial.print("IP: ");
  Serial.println(
      WiFi.softAPIP()
  );

  server.on(
      "/",
      HTTP_GET,
      handleRoot
  );

  server.on(
      "/json",
      HTTP_GET,
      handleJson
  );

  server.on(
      "/data",
      HTTP_POST,
      handleData
  );

  server.begin();

  Serial.println(
      "Web server started"
  );
}

void loop() {
  server.handleClient();
}